Example #1
0
        /// <summary>
        /// Creates the HTML controls required to configure this type of field
        /// </summary>
        /// <returns></returns>
        public override List <Control> ConfigurationControls()
        {
            var controls = base.ConfigurationControls();

            // build a drop down list of attribute matrix templates
            var ddlMatrixTemplate = new RockDropDownList();

            ddlMatrixTemplate.Required = true;
            controls.Add(ddlMatrixTemplate);
            ddlMatrixTemplate.Label = "Attribute Matrix Template";
            ddlMatrixTemplate.Help  = "The Attribute Matrix Template that defines this matrix attribute";

            var list = new AttributeMatrixTemplateService(new RockContext()).Queryable().OrderBy(a => a.Name).Select(a => new
            {
                a.Id,
                a.Name
            }).ToList();

            ddlMatrixTemplate.Items.Clear();
            ddlMatrixTemplate.Items.Add(new ListItem());

            foreach (var item in list)
            {
                ddlMatrixTemplate.Items.Add(new ListItem(item.Name, item.Id.ToString()));
            }

            return(controls);
        }
Example #2
0
        /// <summary>
        /// Sets the value.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="configurationValues">The configuration values.</param>
        /// <param name="value">The value.</param>
        public override void SetEditValue(Control control, Dictionary <string, ConfigurationValue> configurationValues, string value)
        {
            AttributeMatrixEditor attributeMatrixEditor = control as AttributeMatrixEditor;

            if (attributeMatrixEditor != null)
            {
                var rockContext = new RockContext();
                AttributeMatrixTemplate attributeMatrixTemplate = null;
                if (attributeMatrixEditor.AttributeMatrixTemplateId.HasValue)
                {
                    attributeMatrixTemplate = new AttributeMatrixTemplateService(rockContext).Get(attributeMatrixEditor.AttributeMatrixTemplateId.Value);
                }

                if (attributeMatrixTemplate != null)
                {
                    var             attributeMatrixService = new AttributeMatrixService(rockContext);
                    AttributeMatrix attributeMatrix        = null;
                    Guid?           attributeMatrixGuid    = value.AsGuidOrNull();
                    if (attributeMatrixGuid.HasValue)
                    {
                        attributeMatrix = attributeMatrixService.Get(attributeMatrixGuid.Value);
                    }

                    if (attributeMatrix == null)
                    {
                        // Create the AttributeMatrix now and save it even though they haven't hit save yet. We'll need the AttributeMatrix record to exist so that we can add AttributeMatrixItems to it
                        // If this ends up creating an orphan, we can clean up it up later
                        attributeMatrix = new AttributeMatrix {
                            Guid = Guid.NewGuid()
                        };
                        attributeMatrix.AttributeMatrixTemplateId = attributeMatrixEditor.AttributeMatrixTemplateId.Value;
                        attributeMatrix.AttributeMatrixItems      = new List <AttributeMatrixItem>();
                        attributeMatrixService.Add(attributeMatrix);
                        rockContext.SaveChanges();
                    }

                    // If the AttributeMatrixTemplateId jwas changed since the last time the attributeMatrix was saved, change it and wipe out the items
                    if (attributeMatrix.AttributeMatrixTemplateId != attributeMatrixEditor.AttributeMatrixTemplateId.Value)
                    {
                        attributeMatrix.AttributeMatrixTemplateId = attributeMatrixEditor.AttributeMatrixTemplateId.Value;

                        var attributeMatrixItemService = new AttributeMatrixItemService(rockContext);

                        // If the AttributeMatrixTemplateId changed, all the values in the AttributeMatrixItems
                        // are referring to attributes from the old template, so wipe them out. All of them.
                        foreach (var attributeMatrixItem in attributeMatrix.AttributeMatrixItems.ToList())
                        {
                            attributeMatrixItemService.Delete(attributeMatrixItem);
                        }

                        attributeMatrix.AttributeMatrixItems.Clear();
                        rockContext.SaveChanges();
                    }

                    attributeMatrixEditor.AttributeMatrixGuid = attributeMatrix.Guid;
                }
            }
        }
Example #3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="MatrixFieldAttribute" /> class.
        /// </summary>
        /// <param name="attributeMatrixTemplateGuid">The attribute matrix template unique identifier.</param>
        /// <param name="name">The name.</param>
        /// <param name="description">The description.</param>
        /// <param name="required">if set to <c>true</c> [required].</param>
        /// <param name="category">The category.</param>
        /// <param name="order">The order.</param>
        /// <param name="key">The key.</param>
        public MatrixFieldAttribute(string attributeMatrixTemplateGuid, string name, string description = "", bool required = true, string category = "", int order = 0, string key = null)
            : base(name, description, required, null, category, order, key, typeof(MatrixFieldType).FullName)
        {
            var attributeMatrixTemplate = new AttributeMatrixTemplateService(new Data.RockContext()).Get(attributeMatrixTemplateGuid.AsGuid());

            if (attributeMatrixTemplate != null)
            {
                FieldConfigurationValues.Add(MatrixFieldType.ATTRIBUTE_MATRIX_TEMPLATE, new Field.ConfigurationValue(attributeMatrixTemplate.Id.ToString()));
            }
        }
Example #4
0
        /// <summary>
        /// The commands to run to migrate plugin to the specific version
        /// </summary>
        public override void Up()
        {
            #region Email Attribute

            var attributeMatrixTemplateEmailId = new AttributeMatrixTemplateService(new RockContext()).Get(KFSConst.Attribute.ATTRIBUTE_MATRIX_TEMPLATE_SCHEDULED_EMAILS.AsGuid()).Id.ToString();

            // recurrence attribute
            RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.SINGLE_SELECT, "AttributeMatrixTemplateId", attributeMatrixTemplateEmailId, "Recurrence", "", 1, "0", KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SEND_RECURRENCE, "rocks.kfs.ScheduledGroupCommunication.EmailSendRecurrence");
            RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SEND_RECURRENCE, "values", "0^OneTime,1^Weekly,2^BiWeekly,3^Monthly", "F1F0873E-CA36-4935-90F5-DFEFD79083C8");

            // reorder existing matrix attributes

            RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.EMAIL, "AttributeMatrixTemplateId", attributeMatrixTemplateEmailId, "From Email", "", 2, "", KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_FROM_EMAIL, "rocks.kfs.ScheduledGroupCommunication.EmailFromAddress");
            RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.TEXT, "AttributeMatrixTemplateId", attributeMatrixTemplateEmailId, "From Name", "", 3, "", KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_FROM_NAME, "rocks.kfs.ScheduledGroupCommunication.EmailFromName");
            RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.TEXT, "AttributeMatrixTemplateId", attributeMatrixTemplateEmailId, "Subject", "", 4, "", KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SUBJECT, "rocks.kfs.ScheduledGroupCommunication.EmailSubject");
            RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.HTML, "AttributeMatrixTemplateId", attributeMatrixTemplateEmailId, "Message", "", 5, "", KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_MESSAGE, "rocks.kfs.ScheduledGroupCommunication.EmailMessage");

            // set attribute to be required
            Sql(@"
            UPDATE [Attribute] SET [IsRequired] = 1
            WHERE (
                [Guid] = '9BC4F790-A9F7-4822-AEEE-91095A3E3D4C'
            )
            ");

            #endregion

            #region SMS Attributes

            var rockContextSMS = new RockContext();
            var attributeMatrixTemplateSMSId = new AttributeMatrixTemplateService(rockContextSMS).Get(KFSConst.Attribute.ATTRIBUTE_MATRIX_TEMPLATE_SCHEDULED_SMS.AsGuid()).Id.ToString();

            // recurrence attribute
            RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.SINGLE_SELECT, "AttributeMatrixTemplateId", attributeMatrixTemplateSMSId, "Recurrence", "", 1, "0", KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_SEND_RECURRENCE, "rocks.kfs.ScheduledGroupCommunication.SMSSendRecurrence");
            RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_SEND_RECURRENCE, "values", "0^OneTime,1^Weekly,2^BiWeekly,3^Monthly", "4FAE15C5-C1AB-4FDB-B21D-CBF467B1D395");

            // reorder existing matrix attributes
            RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.DEFINED_VALUE, "AttributeMatrixTemplateId", attributeMatrixTemplateSMSId, "From Number", "", 2, "", KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_FROM_NUMBER, "rocks.kfs.ScheduledGroupCommunication.SMSFromNumber");
            RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.MEMO, "AttributeMatrixTemplateId", attributeMatrixTemplateSMSId, "Message", "", 3, "", KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_MESSAGE, "rocks.kfs.ScheduledGroupCommunication.SMSMessage");

            // set attribute to be required
            Sql(@"
            UPDATE [Attribute] SET [IsRequired] = 1
            WHERE (
                [Guid] = 'EC6D13F7-A256-4B03-A94B-3B713F26E62D'
            )
            ");

            #endregion
        }
        /// <summary>
        /// The commands to run to migrate plugin to the specific version
        /// </summary>
        public override void Up()
        {
            #region Email Attribute

            // recurrence attribute
            RockMigrationHelper.UpdateAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SEND_RECURRENCE, "values", "0^OneTime,4^Daily,1^Weekly,2^BiWeekly,3^Monthly", "F1F0873E-CA36-4935-90F5-DFEFD79083C8");

            #endregion

            #region SMS Attributes

            var rockContextSMS = new RockContext();
            var attributeMatrixTemplateSMSId = new AttributeMatrixTemplateService(rockContextSMS).Get(KFSConst.Attribute.ATTRIBUTE_MATRIX_TEMPLATE_SCHEDULED_SMS.AsGuid()).Id.ToString();

            // recurrence attribute
            RockMigrationHelper.UpdateAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_SEND_RECURRENCE, "values", "0^OneTime,4^Daily,1^Weekly,2^BiWeekly,3^Monthly", "4FAE15C5-C1AB-4FDB-B21D-CBF467B1D395");

            #endregion
        }
        /// <summary>
        /// Handles the DeleteClick event of the gList control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="RowEventArgs"/> instance containing the event data.</param>
        protected void gList_DeleteClick(object sender, RowEventArgs e)
        {
            var rockContext = new RockContext();
            AttributeMatrixTemplateService attributeMatrixTemplateService = new AttributeMatrixTemplateService(rockContext);
            AttributeMatrixTemplate        attributeMatrixTemplate        = attributeMatrixTemplateService.Get(e.RowKeyId);

            if (attributeMatrixTemplate != null)
            {
                string errorMessage;
                if (!attributeMatrixTemplateService.CanDelete(attributeMatrixTemplate, true, out errorMessage))
                {
                    mdGridWarning.Show(errorMessage, ModalAlertType.Information);
                    return;
                }

                attributeMatrixTemplateService.Delete(attributeMatrixTemplate);
                rockContext.SaveChanges();
            }

            BindGrid();
        }
        /// <summary>
        /// Binds the grid.
        /// </summary>
        private void BindGrid()
        {
            RockContext rockContext = new RockContext();
            AttributeMatrixTemplateService attributeMatrixTemplateService = new AttributeMatrixTemplateService(rockContext);
            AttributeMatrixService         attributeMatrixService         = new AttributeMatrixService(rockContext);

            var matrixFieldTypeId = FieldTypeCache.Get <MatrixFieldType>().Id;

            var qry = attributeMatrixTemplateService.Queryable()
                      .Select(a => new
            {
                a.Id,
                a.Name,
                a.Description,
                a.IsActive
            }).Sort(gList.SortProperty ?? new SortProperty {
                Property = "Name"
            });

            gList.SetLinqDataSource(qry);
            gList.DataBind();
        }
        /// <summary>
        /// The commands to undo a migration from a specific version
        /// </summary>
        public override void Down()
        {
            RockMigrationHelper.DeleteAttribute(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SEND_DATE);
            RockMigrationHelper.DeleteAttribute(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_FROM_EMAIL);
            RockMigrationHelper.DeleteAttribute(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_FROM_NAME);
            RockMigrationHelper.DeleteAttribute(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SUBJECT);
            RockMigrationHelper.DeleteAttribute(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_MESSAGE);
            RockMigrationHelper.DeleteAttribute(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_SEND_DATE);
            RockMigrationHelper.DeleteAttribute(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_FROM_NUMBER);
            RockMigrationHelper.DeleteAttribute(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_MESSAGE);

            var rockContext = new RockContext();
            var attributeMatrixTemplateEmail = new AttributeMatrixTemplateService(rockContext).Get(KFSConst.Attribute.ATTRIBUTE_MATRIX_TEMPLATE_SCHEDULED_EMAILS.AsGuid());
            var attributeMatrixTemplateSMS   = new AttributeMatrixTemplateService(rockContext).Get(KFSConst.Attribute.ATTRIBUTE_MATRIX_TEMPLATE_SCHEDULED_SMS.AsGuid());

            rockContext.WrapTransaction(() =>
            {
                rockContext.AttributeMatrixTemplates.Remove(attributeMatrixTemplateEmail);
                rockContext.AttributeMatrixTemplates.Remove(attributeMatrixTemplateSMS);
                rockContext.SaveChanges();
            });
        }
        /// <summary>
        /// Called by the ASP.NET page framework to notify server controls that use composition-based implementation to create any child controls they contain in preparation for posting back or rendering.
        /// </summary>
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            // HiddenField to store which AttributeMatrix record we are editing
            _hfAttributeMatrixGuid = new HiddenField {
                ID = "_hfAttributeMatrixGuid"
            };
            this.Controls.Add(_hfAttributeMatrixGuid);

            _nbWarning = new NotificationBox {
                ID = "_nbWarning", NotificationBoxType = NotificationBoxType.Warning, Dismissable = true
            };
            this.Controls.Add(_nbWarning);

            // Grid with view of MatrixItems
            _gMatrixItems = new Grid {
                ID = "_gMatrixItems", DisplayType = GridDisplayType.Light
            };
            this.Controls.Add(_gMatrixItems);
            _gMatrixItems.DataKeyNames      = new string[] { "Id" };
            _gMatrixItems.Actions.AddClick += gMatrixItems_AddClick;
            _gMatrixItems.Actions.ShowAdd   = true;
            _gMatrixItems.IsDeleteEnabled   = true;
            _gMatrixItems.GridReorder      += gMatrixItems_GridReorder;
            _gMatrixItems.GridRebind       += _gMatrixItems_GridRebind;

            _hfRowCount = new HiddenFieldWithValidationProperty {
                ID = "_hfRowCount"
            };
            this.Controls.Add(_hfRowCount);

            _requiredRowCountRangeValidator    = new HiddenFieldRangeValidator();
            _requiredRowCountRangeValidator.ID = _hfRowCount.ID + "_rfv";
            _requiredRowCountRangeValidator.ControlToValidate = _hfRowCount.ID;
            _requiredRowCountRangeValidator.Display           = ValidatorDisplay.Dynamic;
            _requiredRowCountRangeValidator.Type     = ValidationDataType.Integer;
            _requiredRowCountRangeValidator.CssClass = "validation-error help-inline";
            _requiredRowCountRangeValidator.Enabled  = false;

            this.Controls.Add(_requiredRowCountRangeValidator);

            _gMatrixItems.Columns.Add(new ReorderField());

            AttributeMatrixItem tempAttributeMatrixItem = null;

            if (this.AttributeMatrixTemplateId.HasValue)
            {
                tempAttributeMatrixItem = new AttributeMatrixItem();
                tempAttributeMatrixItem.AttributeMatrix = new AttributeMatrix {
                    AttributeMatrixTemplateId = this.AttributeMatrixTemplateId.Value
                };
                tempAttributeMatrixItem.LoadAttributes();

                foreach (var attribute in tempAttributeMatrixItem.Attributes.Select(a => a.Value))
                {
                    _gMatrixItems.Columns.Add(new AttributeField {
                        DataField = attribute.Key, HeaderText = attribute.Name
                    });
                }

                AttributeMatrixTemplateService attributeMatrixTemplateService = new AttributeMatrixTemplateService(new RockContext());
                var attributeMatrixTemplateRanges = attributeMatrixTemplateService.GetSelect(this.AttributeMatrixTemplateId.Value, s => new { s.MinimumRows, s.MaximumRows });

                // If a value is required, make sure we have a minumum row count of at least 1.
                var minRowCount = attributeMatrixTemplateRanges.MinimumRows.GetValueOrDefault(0);
                if (this.Required &&
                    minRowCount < 1)
                {
                    minRowCount = 1;
                }
                _requiredRowCountRangeValidator.MinimumValue = minRowCount.ToString();

                _requiredRowCountRangeValidator.Enabled = minRowCount > 0;
                if (minRowCount == 1)
                {
                    _requiredRowCountRangeValidator.ErrorMessage = "At least 1 row is required.";
                }
                else
                {
                    _requiredRowCountRangeValidator.ErrorMessage = $"At least {minRowCount} rows are required";
                }
            }

            DeleteField deleteField = new DeleteField();

            deleteField.Click += gMatrixItems_DeleteClick;
            _gMatrixItems.Columns.Add(deleteField);

            _gMatrixItems.RowSelected += gMatrixItems_RowSelected;

            // Edit Item
            _pnlEditMatrixItem = new Panel {
                ID = "_pnlEditMatrixItem", Visible = false, CssClass = "well js-validation-group validation-group"
            };
            _hfMatrixItemId = new HiddenField {
                ID = "_hfMatrixItemId"
            };
            _pnlEditMatrixItem.Controls.Add(_hfMatrixItemId);

            _phMatrixItemAttributes = new DynamicPlaceholder {
                ID = "_phMatrixItemAttributes"
            };
            _pnlEditMatrixItem.Controls.Add(_phMatrixItemAttributes);

            string validationGroup = GetValidationGroupForAttributeControls();

            if (tempAttributeMatrixItem != null)
            {
                Rock.Attribute.Helper.AddEditControls(tempAttributeMatrixItem, _phMatrixItemAttributes, false, validationGroup);
            }

            _pnlActions = new Panel {
                ID = "_pnlActions", CssClass = "actions"
            };
            _pnlEditMatrixItem.Controls.Add(_pnlActions);

            _btnSaveMatrixItem = new LinkButton {
                ID = "_btnSaveMatrixItem", CssClass = "btn btn-primary btn-sm", Text = "Save", ValidationGroup = validationGroup, CausesValidation = true
            };
            _btnSaveMatrixItem.Click += btnSaveMatrixItem_Click;
            _pnlActions.Controls.Add(_btnSaveMatrixItem);

            _btnCancelMatrixItem = new LinkButton {
                ID = "_btnCancelMatrixItem", CssClass = "btn btn-link", Text = "Cancel", CausesValidation = false
            };
            _btnCancelMatrixItem.Click += btnCancelMatrixItem_Click;
            _pnlActions.Controls.Add(_btnCancelMatrixItem);

            this.Controls.Add(_pnlEditMatrixItem);
        }
Example #10
0
        /// <summary>
        /// Shows the detail.
        /// </summary>
        /// <param name="attributeMatrixTemplateId">The attribute matrix template identifier.</param>
        public void ShowDetail(int attributeMatrixTemplateId)
        {
            pnlView.Visible = true;

            AttributeMatrixTemplate attributeMatrixTemplate = null;
            var rockContext = new RockContext();

            if (!attributeMatrixTemplateId.Equals(0))
            {
                attributeMatrixTemplate = new AttributeMatrixTemplateService(rockContext).Get(attributeMatrixTemplateId);
                lActionTitle.Text       = ActionTitle.Edit(AttributeMatrixTemplate.FriendlyTypeName).FormatAsHtmlTitle();
                pdAuditDetails.SetEntity(attributeMatrixTemplate, ResolveRockUrl("~"));
            }

            if (attributeMatrixTemplate == null)
            {
                attributeMatrixTemplate = new AttributeMatrixTemplate {
                    Id = 0
                };
                attributeMatrixTemplate.FormattedLava = AttributeMatrixTemplate.FormattedLavaDefault;

                lActionTitle.Text = ActionTitle.Add(AttributeMatrixTemplate.FriendlyTypeName).FormatAsHtmlTitle();

                // hide the panel drawer that show created and last modified dates
                pdAuditDetails.Visible = false;
            }

            hfAttributeMatrixTemplateId.Value = attributeMatrixTemplate.Id.ToString();
            tbName.Text          = attributeMatrixTemplate.Name;
            cbIsActive.Checked   = attributeMatrixTemplate.IsActive;
            tbDescription.Text   = attributeMatrixTemplate.Description;
            tbMinimumRows.Text   = attributeMatrixTemplate.MinimumRows.ToString();
            tbMaximumRows.Text   = attributeMatrixTemplate.MaximumRows.ToString();
            ceFormattedLava.Text = attributeMatrixTemplate.FormattedLava;

            var attributeService = new AttributeService(rockContext);

            AttributesState = attributeService.Get(new AttributeMatrixItem().TypeId, "AttributeMatrixTemplateId", attributeMatrixTemplate.Id.ToString()).AsQueryable()
                              .OrderBy(a => a.Order)
                              .ThenBy(a => a.Name)
                              .ToList();

            BindAttributesGrid();

            // render UI based on Authorized and IsSystem
            bool readOnly = false;

            nbEditModeMessage.Text = string.Empty;
            if (!IsUserAuthorized(Authorization.EDIT))
            {
                readOnly = true;
                nbEditModeMessage.Text = EditModeMessage.ReadOnlyEditActionNotAllowed(AttributeMatrixTemplate.FriendlyTypeName);
            }

            if (readOnly)
            {
                lActionTitle.Text = ActionTitle.View(AttributeMatrixTemplate.FriendlyTypeName);
                btnCancel.Text    = "Close";
            }

            tbName.ReadOnly          = readOnly;
            cbIsActive.Enabled       = !readOnly;
            tbDescription.ReadOnly   = readOnly;
            tbMinimumRows.ReadOnly   = readOnly;
            tbMaximumRows.ReadOnly   = readOnly;
            ceFormattedLava.ReadOnly = readOnly;
            btnSave.Visible          = !readOnly;
        }
Example #11
0
        /// <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)
        {
            AttributeMatrixTemplate attributeMatrixTemplate;
            var rockContext = new RockContext();
            var attributeMatrixTemplateService = new AttributeMatrixTemplateService(rockContext);

            int attributeMatrixTemplateId = int.Parse(hfAttributeMatrixTemplateId.Value);

            if (attributeMatrixTemplateId == 0)
            {
                attributeMatrixTemplate = new AttributeMatrixTemplate();
                attributeMatrixTemplateService.Add(attributeMatrixTemplate);
            }
            else
            {
                attributeMatrixTemplate = attributeMatrixTemplateService.Get(attributeMatrixTemplateId);
            }

            attributeMatrixTemplate.Name          = tbName.Text;
            attributeMatrixTemplate.IsActive      = cbIsActive.Checked;
            attributeMatrixTemplate.Description   = tbDescription.Text;
            attributeMatrixTemplate.MinimumRows   = tbMinimumRows.Text.AsIntegerOrNull();
            attributeMatrixTemplate.MaximumRows   = tbMaximumRows.Text.AsIntegerOrNull();
            attributeMatrixTemplate.FormattedLava = ceFormattedLava.Text;

            // need WrapTransaction due to Attribute saves
            rockContext.WrapTransaction(() =>
            {
                rockContext.SaveChanges();

                /* Save Attributes */

                var entityTypeIdAttributeMatrix = EntityTypeCache.GetId <AttributeMatrixItem>();

                // Get the existing attributes for this entity type and qualifier value
                var attributeService = new AttributeService(rockContext);
                var regFieldService  = new RegistrationTemplateFormFieldService(rockContext);
                var attributes       = attributeService.Get(entityTypeIdAttributeMatrix, "AttributeMatrixTemplateId", attributeMatrixTemplate.Id.ToString());

                // Delete any of those attributes that were removed in the UI
                var selectedAttributeGuids = AttributesState.Select(a => a.Guid);
                foreach (var attr in attributes.Where(a => !selectedAttributeGuids.Contains(a.Guid)))
                {
                    foreach (var field in regFieldService.Queryable().Where(f => f.AttributeId.HasValue && f.AttributeId.Value == attr.Id).ToList())
                    {
                        regFieldService.Delete(field);
                    }

                    attributeService.Delete(attr);
                    rockContext.SaveChanges();
                    Rock.Web.Cache.AttributeCache.Flush(attr.Id);
                }

                // Update the Attributes that were assigned in the UI
                foreach (var attributeState in AttributesState)
                {
                    Helper.SaveAttributeEdits(attributeState, entityTypeIdAttributeMatrix, "AttributeMatrixTemplateId", attributeMatrixTemplate.Id.ToString(), rockContext);
                }

                Rock.Web.Cache.AttributeCache.FlushEntityAttributes();
            });

            NavigateToParentPage();
        }
        /// <summary>
        /// The commands to run to migrate plugin to the specific version
        /// </summary>
        public override void Up()
        {
            var migrateNamespaceEmail = false;
            var migrateNamespaceSMS   = false;
            var oldNamespaceEmail     = "com.kfs.GroupScheduledEmails";
            var oldNamespaceSMS       = "com.kfs.GroupScheduledSMS";

            // check if migration has previously run
            using (var rockContext = new RockContext())
            {
                var migrationNumber = (System.Attribute.GetCustomAttribute(this.GetType(), typeof(MigrationNumberAttribute)) as MigrationNumberAttribute).Number;

                migrateNamespaceEmail = new PluginMigrationService(rockContext)
                                        .Queryable()
                                        .Where(m => m.PluginAssemblyName.Equals(oldNamespaceEmail, StringComparison.CurrentCultureIgnoreCase) && m.MigrationNumber == migrationNumber)
                                        .Any();

                migrateNamespaceSMS = new PluginMigrationService(rockContext)
                                      .Queryable()
                                      .Where(m => m.PluginAssemblyName.Equals(oldNamespaceSMS, StringComparison.CurrentCultureIgnoreCase) && m.MigrationNumber == migrationNumber)
                                      .Any();
            }

            #region Email Attributes

            if (migrateNamespaceEmail)
            {
                var attributeMatrixTemplateEmailId = new AttributeMatrixTemplateService(new RockContext()).Get(KFSConst.Attribute.ATTRIBUTE_MATRIX_TEMPLATE_SCHEDULED_EMAILS.AsGuid()).Id.ToString();

                RockMigrationHelper.EnsureAttributeByGuid(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SEND_DATE, "rocks.kfs.ScheduledGroupCommunication.EmailSendDateTime", "3c9d5021-0484-4846-aef6-b6216d26c3c8", Rock.SystemGuid.FieldType.DATE_TIME, "AttributeMatrixTemplateId", attributeMatrixTemplateEmailId);
                RockMigrationHelper.EnsureAttributeByGuid(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_FROM_EMAIL, "rocks.kfs.ScheduledGroupCommunication.EmailFromAddress", "3c9d5021-0484-4846-aef6-b6216d26c3c8", Rock.SystemGuid.FieldType.EMAIL, "AttributeMatrixTemplateId", attributeMatrixTemplateEmailId);
                RockMigrationHelper.EnsureAttributeByGuid(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_FROM_NAME, "rocks.kfs.ScheduledGroupCommunication.EmailFromName", "3c9d5021-0484-4846-aef6-b6216d26c3c8", Rock.SystemGuid.FieldType.TEXT, "AttributeMatrixTemplateId", attributeMatrixTemplateEmailId);
                RockMigrationHelper.EnsureAttributeByGuid(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SUBJECT, "rocks.kfs.ScheduledGroupCommunication.EmailSubject", "3c9d5021-0484-4846-aef6-b6216d26c3c8", Rock.SystemGuid.FieldType.TEXT, "AttributeMatrixTemplateId", attributeMatrixTemplateEmailId);
                RockMigrationHelper.EnsureAttributeByGuid(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_MESSAGE, "rocks.kfs.ScheduledGroupCommunication.EmailMessage", "3c9d5021-0484-4846-aef6-b6216d26c3c8", Rock.SystemGuid.FieldType.HTML, "AttributeMatrixTemplateId", attributeMatrixTemplateEmailId);

                using (var rockContext = new RockContext())
                {
                    // look for any configured jobs and change the class name
                    var emailJobs = new ServiceJobService(rockContext)
                                    .Queryable()
                                    .Where(j => j.Class.Equals("com.kfs.GroupScheduledEmails.Jobs.SendScheduledGroupEmail", StringComparison.CurrentCultureIgnoreCase))
                                    .ToList();

                    foreach (var job in emailJobs)
                    {
                        job.Class = "rocks.kfs.ScheduledGroupCommunication.Jobs.SendScheduledGroupEmail";
                    }

                    // look for job attributes and change qualifier value
                    var attributes = new AttributeService(rockContext)
                                     .Queryable()
                                     .Where(a => a.EntityTypeQualifierValue.Equals("com.kfs.GroupScheduledEmails.Jobs.SendScheduledGroupEmail", StringComparison.CurrentCultureIgnoreCase))
                                     .ToList();

                    foreach (var attribute in attributes)
                    {
                        attribute.EntityTypeQualifierValue = "rocks.kfs.ScheduledGroupCommunication.Jobs.SendScheduledGroupEmail";
                    }

                    rockContext.SaveChanges();
                }
            }
            else
            {
                // create the attribute matrix template and get it's id to assign to the attributes we'll create
                var attributeMatrixTemplateEmail = new AttributeMatrixTemplate
                {
                    Name            = "Scheduled Emails",
                    Description     = "Used to create scheduled emails.",
                    IsActive        = true,
                    FormattedLava   = AttributeMatrixTemplate.FormattedLavaDefault,
                    CreatedDateTime = RockDateTime.Now,
                    Guid            = KFSConst.Attribute.ATTRIBUTE_MATRIX_TEMPLATE_SCHEDULED_EMAILS.AsGuid()
                };

                var rockContextEmail = new RockContext();
                rockContextEmail.WrapTransaction(() =>
                {
                    rockContextEmail.AttributeMatrixTemplates.Add(attributeMatrixTemplateEmail);
                    rockContextEmail.SaveChanges();
                });

                var attributeMatrixTemplateEmailId = new AttributeMatrixTemplateService(rockContextEmail).Get(KFSConst.Attribute.ATTRIBUTE_MATRIX_TEMPLATE_SCHEDULED_EMAILS.AsGuid()).Id.ToString();

                // send date time
                RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.DATE_TIME, "AttributeMatrixTemplateId", attributeMatrixTemplateEmailId, "Send Date Time", "", 0, "", KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SEND_DATE, "rocks.kfs.ScheduledGroupCommunication.EmailSendDateTime");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SEND_DATE, "datePickerControlType", "Date Picker", "3C8A1A23-F2CD-42F6-BF1E-69AAE87A4701");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SEND_DATE, "displayCurrentOption", "False", "EE2C58B0-5A24-4957-A84D-94829985B2C1");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SEND_DATE, "displayDiff", "False", "B61EB1C8-D811-4D4A-9AC9-2E8B8381E468");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SEND_DATE, "format", "", "83C6F1A4-2514-4C62-A4EA-FD804ABC758C");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SEND_DATE, "futureYearCount", "", "1AFFC6EA-8C45-4125-94DC-25E0E73D8CC6");

                // from email
                RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.EMAIL, "AttributeMatrixTemplateId", attributeMatrixTemplateEmailId, "From Email", "", 1, "", KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_FROM_EMAIL, "rocks.kfs.ScheduledGroupCommunication.EmailFromAddress");

                // from name
                RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.TEXT, "AttributeMatrixTemplateId", attributeMatrixTemplateEmailId, "From Name", "", 2, "", KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_FROM_NAME, "rocks.kfs.ScheduledGroupCommunication.EmailFromName");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_FROM_NAME, "ispassword", "false", "1EE77F52-992D-43CE-8574-306E95A7D740");

                // subject
                RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.TEXT, "AttributeMatrixTemplateId", attributeMatrixTemplateEmailId, "Subject", "", 3, "", KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SUBJECT, "rocks.kfs.ScheduledGroupCommunication.EmailSubject");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_SUBJECT, "ispassword", "false", "CEB3BEB8-20E9-4A8F-8530-2B68DA02B020");

                // message
                RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.HTML, "AttributeMatrixTemplateId", attributeMatrixTemplateEmailId, "Message", "", 4, "", KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_MESSAGE, "rocks.kfs.ScheduledGroupCommunication.EmailMessage");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_MESSAGE, "documentfolderroot", "", "4B2FFA29-5B9C-4F74-ACE5-54C97B68E0B6");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_MESSAGE, "imagefolderroot", "", "77C6B7A9-69DF-466F-AFEC-DC41B33E0DF0");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_MESSAGE, "toolbar", "Light", "E1E5C3F2-F64C-4942-A5B3-B712125BF8CB");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_EMAIL_MESSAGE, "userspecificroot", "False", "C3FF1EBB-7EFF-4747-A326-F6BCD669DD35");

                // set all attributes to be required
                Sql(@"
                UPDATE [Attribute] SET [IsRequired] = 1
                WHERE (
                    [Guid] = '39A38B02-112C-4EDC-A30E-4BDB1B090EE4'
                    OR
                    [Guid] = 'F7C73002-6442-4756-BFDB-BC0BFE58EF15'
                    OR
                    [Guid] = '7BEE419A-8444-44E1-B7EB-451C038977B3'
                    OR
                    [Guid] = '9EC7C8A9-F4C9-421C-9129-2DD023E09D05'
                    OR
                    [Guid] = '8C4EE7A8-086D-42B7-908F-77A9A36E5342'
                )
                ");
            }

            #endregion

            #region SMS Attributes

            if (migrateNamespaceSMS)
            {
                var attributeMatrixTemplateSMSId = new AttributeMatrixTemplateService(new RockContext()).Get(KFSConst.Attribute.ATTRIBUTE_MATRIX_TEMPLATE_SCHEDULED_SMS.AsGuid()).Id.ToString();

                RockMigrationHelper.EnsureAttributeByGuid(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_SEND_DATE, "rocks.kfs.ScheduledGroupCommunication.SMSSendDateTime", "3c9d5021-0484-4846-aef6-b6216d26c3c8", Rock.SystemGuid.FieldType.DATE_TIME, "AttributeMatrixTemplateId", attributeMatrixTemplateSMSId);
                RockMigrationHelper.EnsureAttributeByGuid(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_FROM_NUMBER, "rocks.kfs.ScheduledGroupCommunication.SMSFromNumber", "3c9d5021-0484-4846-aef6-b6216d26c3c8", Rock.SystemGuid.FieldType.DEFINED_VALUE, "AttributeMatrixTemplateId", attributeMatrixTemplateSMSId);
                RockMigrationHelper.EnsureAttributeByGuid(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_MESSAGE, "rocks.kfs.ScheduledGroupCommunication.SMSMessage", "3c9d5021-0484-4846-aef6-b6216d26c3c8", Rock.SystemGuid.FieldType.MEMO, "AttributeMatrixTemplateId", attributeMatrixTemplateSMSId);

                using (var rockContext = new RockContext())
                {
                    // look for any configured jobs and change the class name
                    var smsJobs = new ServiceJobService(rockContext)
                                  .Queryable()
                                  .Where(j => j.Class.Equals("com.kfs.GroupScheduledSMS.Jobs.SendScheduledGroupSMS", StringComparison.CurrentCultureIgnoreCase))
                                  .ToList();

                    foreach (var job in smsJobs)
                    {
                        job.Class = "rocks.kfs.ScheduledGroupCommunication.Jobs.SendScheduledGroupSMS";
                    }

                    // look for job attributes and change qualifier value
                    var attributes = new AttributeService(rockContext)
                                     .Queryable()
                                     .Where(a => a.EntityTypeQualifierValue.Equals("com.kfs.GroupScheduledSMS.Jobs.SendScheduledGroupSMS", StringComparison.CurrentCultureIgnoreCase))
                                     .ToList();

                    foreach (var attribute in attributes)
                    {
                        attribute.EntityTypeQualifierValue = "rocks.kfs.ScheduledGroupCommunication.Jobs.SendScheduledGroupSMS";
                    }

                    rockContext.SaveChanges();
                }
            }
            else
            {
                var attributeMatrixTemplateSMS = new AttributeMatrixTemplate
                {
                    Name            = "Scheduled SMS Messages",
                    Description     = "Used to create scheduled SMS messages.",
                    IsActive        = true,
                    FormattedLava   = AttributeMatrixTemplate.FormattedLavaDefault,
                    CreatedDateTime = RockDateTime.Now,
                    Guid            = KFSConst.Attribute.ATTRIBUTE_MATRIX_TEMPLATE_SCHEDULED_SMS.AsGuid()
                };

                var rockContextSMS = new RockContext();
                rockContextSMS.WrapTransaction(() =>
                {
                    rockContextSMS.AttributeMatrixTemplates.Add(attributeMatrixTemplateSMS);
                    rockContextSMS.SaveChanges();
                });

                var attributeMatrixTemplateSMSId = new AttributeMatrixTemplateService(rockContextSMS).Get(KFSConst.Attribute.ATTRIBUTE_MATRIX_TEMPLATE_SCHEDULED_SMS.AsGuid()).Id.ToString();

                // send date time
                RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.DATE_TIME, "AttributeMatrixTemplateId", attributeMatrixTemplateSMSId, "Send Date Time", "", 0, "", KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_SEND_DATE, "rocks.kfs.ScheduledGroupCommunication.SMSSendDateTime");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_SEND_DATE, "datePickerControlType", "Date Picker", "3C8A1A23-F2CD-42F6-BF1E-69AAE87A4701");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_SEND_DATE, "displayCurrentOption", "False", "EE2C58B0-5A24-4957-A84D-94829985B2C1");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_SEND_DATE, "displayDiff", "False", "B61EB1C8-D811-4D4A-9AC9-2E8B8381E468");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_SEND_DATE, "format", "", "83C6F1A4-2514-4C62-A4EA-FD804ABC758C");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_SEND_DATE, "futureYearCount", "", "1AFFC6EA-8C45-4125-94DC-25E0E73D8CC6");

                // from number
                RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.DEFINED_VALUE, "AttributeMatrixTemplateId", attributeMatrixTemplateSMSId, "From Number", "", 1, "", KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_FROM_NUMBER, "rocks.kfs.ScheduledGroupCommunication.SMSFromNumber");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_FROM_NUMBER, "allowmultiple", "False", "90051033-C881-42E8-A0CE-4152527D6FA3");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_FROM_NUMBER, "definedtype", "", "26143779-5747-416C-98B9-EBC86E4B8EE6");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_FROM_NUMBER, "displaydescription", "True", "8AABC104-CFE5-4DF1-A164-2DA305F46BBC");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_FROM_NUMBER, "enhancedselection", "False", "B0008343-0033-4194-A1A8-F5AEF3B095AD");

                // message
                RockMigrationHelper.UpdateEntityAttribute("Rock.Model.AttributeMatrixItem", Rock.SystemGuid.FieldType.MEMO, "AttributeMatrixTemplateId", attributeMatrixTemplateSMSId, "Message", "", 2, "", KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_MESSAGE, "rocks.kfs.ScheduledGroupCommunication.SMSMessage");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_MESSAGE, "numberofrows", "3", "BB1F72CF-46E3-4ED5-B7C8-BAD274744435");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_MESSAGE, "allowhtml", "False", "0C97ED08-FF8D-4BF3-ADD1-C688EFB999D1");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_MESSAGE, "maxcharacters", "160", "A54E62A3-05BF-4458-8F56-408A2132BDA7");
                RockMigrationHelper.AddAttributeQualifier(KFSConst.Attribute.MATRIX_ATTRIBUTE_SMS_MESSAGE, "showcountdown", "True", "8808BF22-2051-488F-83E5-7188D2941334");

                // set all attributes to be required
                Sql(@"
                UPDATE [Attribute] SET [IsRequired] = 1
                WHERE (
                    [Guid] = 'B2125940-565B-42CE-82BE-CDA58FC65FDE'
                    OR
                    [Guid] = '1984A561-C4A9-4D4F-B366-23AD54BDCFE8'
                    OR
                    [Guid] = 'C57166D5-C0D3-4DA6-88DD-92AFA5126D69'
                )

                DECLARE @CommunicationSMSFromDefinedTypeId int = ( SELECT TOP 1 [Id] FROM [DefinedType] WHERE [Guid] = '611BDE1F-7405-4D16-8626-CCFEDB0E62BE' )

                UPDATE [AttributeQualifier] SET [Value] = CAST( @CommunicationSMSFromDefinedTypeId AS varchar )
                WHERE [Guid] = '26143779-5747-416C-98B9-EBC86E4B8EE6'
                ");
            }

            #endregion
        }