/// <summary>
        /// Handles the SaveClick event of the mdAddCheckinGroupType 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 mdAddCheckinGroupType_SaveClick( object sender, EventArgs e )
        {
            using ( var rockContext = new RockContext() )
            {
                var groupTypeService = new GroupTypeService( rockContext );
                GroupType groupType;

                if ( hfGroupTypeId.Value.AsInteger() == 0 )
                {
                    groupType = new GroupType();
                    groupTypeService.Add( groupType );
                    groupType.GroupTypePurposeValueId = DefinedValueCache.Read( Rock.SystemGuid.DefinedValue.GROUPTYPE_PURPOSE_CHECKIN_TEMPLATE ).Id;
                    groupType.ShowInNavigation = false;
                    groupType.ShowInGroupList = false;
                }
                else
                {
                    groupType = groupTypeService.Get( hfGroupTypeId.Value.AsInteger() );
                }

                groupType.Name = tbGroupTypeName.Text;
                groupType.Description = tbGroupTypeDescription.Text;

                rockContext.SaveChanges();
            }

            mdAddEditCheckinGroupType.Hide();

            BindGrid();
        }
        /// <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 )
        {
            GroupType groupType;
            var rockContext = new RockContext();
            GroupTypeService groupTypeService = new GroupTypeService( rockContext );
            GroupTypeRoleService groupTypeRoleService = new GroupTypeRoleService( rockContext );
            AttributeService attributeService = new AttributeService( rockContext );
            AttributeQualifierService qualifierService = new AttributeQualifierService( rockContext );
            CategoryService categoryService = new CategoryService( rockContext );
            GroupScheduleExclusionService scheduleExclusionService = new GroupScheduleExclusionService( rockContext );

            int groupTypeId = int.Parse( hfGroupTypeId.Value );

            if ( groupTypeId == 0 )
            {
                groupType = new GroupType();
                groupTypeService.Add( groupType );
            }
            else
            {
                groupType = groupTypeService.Get( groupTypeId );

                // selected roles
                var selectedRoleGuids = GroupTypeRolesState.Select( r => r.Guid );
                foreach ( var role in groupType.Roles.Where( r => !selectedRoleGuids.Contains( r.Guid ) ).ToList() )
                {
                    groupType.Roles.Remove( role );
                    groupTypeRoleService.Delete( role );
                }
            }

            foreach ( var roleState in GroupTypeRolesState )
            {
                GroupTypeRole role = groupType.Roles.Where( r => r.Guid == roleState.Guid ).FirstOrDefault();
                if ( role == null )
                {
                    role = new GroupTypeRole();
                    groupType.Roles.Add( role );
                }
                else
                {
                    roleState.Id = role.Id;
                    roleState.Guid = role.Guid;
                }

                role.CopyPropertiesFrom( roleState );
            }

            ScheduleType allowedScheduleTypes = ScheduleType.None;
            foreach( ListItem li in cblScheduleTypes.Items )
            {
                if ( li.Selected )
                {
                    allowedScheduleTypes = allowedScheduleTypes | (ScheduleType)li.Value.AsInteger();
                }
            }

            GroupLocationPickerMode locationSelectionMode = GroupLocationPickerMode.None;
            foreach ( ListItem li in cblLocationSelectionModes.Items )
            {
                if ( li.Selected )
                {
                    locationSelectionMode = locationSelectionMode | (GroupLocationPickerMode)li.Value.AsInteger();
                }
            }

            groupType.Name = tbName.Text;
            groupType.Description = tbDescription.Text;
            groupType.GroupTerm = tbGroupTerm.Text;
            groupType.GroupMemberTerm = tbGroupMemberTerm.Text;
            groupType.ShowInGroupList = cbShowInGroupList.Checked;
            groupType.ShowInNavigation = cbShowInNavigation.Checked;
            groupType.IconCssClass = tbIconCssClass.Text;
            groupType.TakesAttendance = cbTakesAttendance.Checked;
            groupType.SendAttendanceReminder = cbSendAttendanceReminder.Checked;
            groupType.AttendanceRule = ddlAttendanceRule.SelectedValueAsEnum<AttendanceRule>();
            groupType.AttendancePrintTo = ddlPrintTo.SelectedValueAsEnum<PrintTo>();
            groupType.AllowedScheduleTypes = allowedScheduleTypes;
            groupType.LocationSelectionMode = locationSelectionMode;
            groupType.GroupTypePurposeValueId = ddlGroupTypePurpose.SelectedValueAsInt();
            groupType.AllowMultipleLocations = cbAllowMultipleLocations.Checked;
            groupType.InheritedGroupTypeId = gtpInheritedGroupType.SelectedGroupTypeId;
            groupType.EnableLocationSchedules = cbEnableLocationSchedules.Checked;

            groupType.ChildGroupTypes = new List<GroupType>();
            groupType.ChildGroupTypes.Clear();
            foreach ( var item in ChildGroupTypesDictionary )
            {
                var childGroupType = groupTypeService.Get( item.Key );
                if ( childGroupType != null )
                {
                    groupType.ChildGroupTypes.Add( childGroupType );
                }
            }

            // Delete any removed exclusions
            foreach ( var exclusion in groupType.GroupScheduleExclusions.Where( s => !ScheduleExclusionDictionary.Keys.Contains( s.Guid ) ).ToList() )
            {
                groupType.GroupScheduleExclusions.Remove( exclusion );
                scheduleExclusionService.Delete( exclusion );
            }

            // Update exclusions
            foreach( var keyVal in ScheduleExclusionDictionary )
            {
                var scheduleExclusion = groupType.GroupScheduleExclusions
                    .FirstOrDefault( s => s.Guid.Equals( keyVal.Key));
                if ( scheduleExclusion == null )
                {
                    scheduleExclusion = new GroupScheduleExclusion();
                    groupType.GroupScheduleExclusions.Add( scheduleExclusion);
                }

                scheduleExclusion.StartDate = keyVal.Value.Start;
                scheduleExclusion.EndDate = keyVal.Value.End;
            }

            DefinedValueService definedValueService = new DefinedValueService( rockContext );

            groupType.LocationTypes = new List<GroupTypeLocationType>();
            groupType.LocationTypes.Clear();
            foreach ( var item in LocationTypesDictionary )
            {
                var locationType = definedValueService.Get( item.Key );
                if ( locationType != null )
                {
                    groupType.LocationTypes.Add( new GroupTypeLocationType { LocationTypeValueId = locationType.Id } );
                }
            }

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

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

                /* Save Attributes */
                string qualifierValue = groupType.Id.ToString();
                SaveAttributes( new GroupType().TypeId, "Id", qualifierValue, GroupTypeAttributesState, rockContext );
                SaveAttributes( new Group().TypeId, "GroupTypeId", qualifierValue, GroupAttributesState, rockContext );
                SaveAttributes( new GroupMember().TypeId, "GroupTypeId", qualifierValue, GroupMemberAttributesState, rockContext );

                // Reload to save default role
                groupType = groupTypeService.Get( groupType.Id );
                groupType.DefaultGroupRole = groupType.Roles.FirstOrDefault( r => r.Guid.Equals( DefaultRoleGuid ) );
                if ( groupType.DefaultGroupRole == null )
                {
                    groupType.DefaultGroupRole = groupType.Roles.FirstOrDefault();
                }

                rockContext.SaveChanges();

                // Reload the roles and apply their attribute values
                foreach ( var role in groupTypeRoleService.GetByGroupTypeId( groupType.Id ).ToList() )
                {
                    role.LoadAttributes( rockContext );
                    var roleState = GroupTypeRolesState.Where( r => r.Guid.Equals( role.Guid ) ).FirstOrDefault();
                    if ( roleState != null && roleState.AttributeValues != null )
                    {
                        foreach ( var attributeValue in roleState.AttributeValues )
                        {
                            role.SetAttributeValue( attributeValue.Key, roleState.GetAttributeValue( attributeValue.Key ) );
                        }

                        role.SaveAttributeValues( rockContext );
                    }
                }
            } );

            GroupTypeCache.Flush( groupType.Id );

            NavigateToParentPage();
        }
        /// <summary>
        /// Handles the SaveClick event of the mdAddCheckinGroupType 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 mdAddCheckinGroupType_SaveClick( object sender, EventArgs e )
        {
            using ( var rockContext = new RockContext() )
            {
                var groupTypeService = new GroupTypeService( rockContext );
                GroupType groupType;

                if ( hfGroupTypeId.Value.AsInteger() == 0 )
                {
                    groupType = new GroupType();
                    groupTypeService.Add( groupType );
                    groupType.GroupTypePurposeValueId = DefinedValueCache.Read( Rock.SystemGuid.DefinedValue.GROUPTYPE_PURPOSE_CHECKIN_TEMPLATE ).Id;
                    groupType.ShowInNavigation = false;
                    groupType.ShowInGroupList = false;

                    var defaultRole = new GroupTypeRole();
                    defaultRole.Name = "Member";
                    groupType.Roles.Add( defaultRole );
                }
                else
                {
                    groupType = groupTypeService.Get( hfGroupTypeId.Value.AsInteger() );
                }

                groupType.Name = tbGroupTypeName.Text;
                groupType.Description = tbGroupTypeDescription.Text;

                rockContext.SaveChanges();

                // Reload to check for setting default role
                groupType = groupTypeService.Get( groupType.Id );
                if ( groupType != null && !groupType.DefaultGroupRoleId.HasValue && groupType.Roles.Any() )
                {
                    groupType.DefaultGroupRoleId = groupType.Roles.First().Id;
                    rockContext.SaveChanges();
                }

            }

            mdAddEditCheckinGroupType.Hide();

            BindGrid();
        }
        /// <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 )
        {
            if ( !Page.IsValid )
            {
                return;
            }

            GroupType groupType = null;

            var rockContext = new RockContext();
            GroupTypeService groupTypeService = new GroupTypeService( rockContext );
            AttributeService attributeService = new AttributeService( rockContext );
            AttributeQualifierService attributeQualifierService = new AttributeQualifierService( rockContext );

            int? groupTypeId = hfGroupTypeId.ValueAsInt();
            if ( groupTypeId.HasValue && groupTypeId.Value > 0 )
            {
                groupType = groupTypeService.Get( groupTypeId.Value );
            }

            bool newGroupType = false;

            if ( groupType == null )
            {
                groupType = new GroupType();
                groupTypeService.Add( groupType );

                var templatePurpose = DefinedValueCache.Read( Rock.SystemGuid.DefinedValue.GROUPTYPE_PURPOSE_CHECKIN_TEMPLATE.AsGuid() );
                if ( templatePurpose != null )
                {
                    groupType.GroupTypePurposeValueId = templatePurpose.Id;
                }

                newGroupType = true;
            }

            if ( groupType != null )
            {
                groupType.Name = tbName.Text;
                groupType.Description = tbDescription.Text;

                groupType.LoadAttributes( rockContext );
                Rock.Attribute.Helper.GetEditValues( phAttributeEdits, groupType );

                groupType.SetAttributeValue( "core_checkin_AgeRequired", cbAgeRequired.Checked.ToString() );
                groupType.SetAttributeValue( "core_checkin_GradeRequired", cbGradeRequired.Checked.ToString() );
                groupType.SetAttributeValue( "core_checkin_HidePhotos", cbHidePhotos.Checked.ToString() );
                groupType.SetAttributeValue( "core_checkin_PreventDuplicateCheckin", cbPreventDuplicateCheckin.Checked.ToString() );
                groupType.SetAttributeValue( "core_checkin_PreventInactivePeople", cbPreventInactivePeople.Checked.ToString() );
                groupType.SetAttributeValue( "core_checkin_CheckInType", ddlType.SelectedValue );
                groupType.SetAttributeValue( "core_checkin_DisplayLocationCount", cbDisplayLocCount.Checked.ToString() );
                groupType.SetAttributeValue( "core_checkin_EnableManagerOption", cbEnableManager.Checked.ToString() );
                groupType.SetAttributeValue( "core_checkin_EnableOverride", cbEnableOverride.Checked.ToString() );
                groupType.SetAttributeValue( "core_checkin_MaximumPhoneSearchLength", nbMaxPhoneLength.Text );
                groupType.SetAttributeValue( "core_checkin_MaxSearchResults", nbMaxResults.Text );
                groupType.SetAttributeValue( "core_checkin_MinimumPhoneSearchLength", nbMinPhoneLength.Text );
                groupType.SetAttributeValue( "core_checkin_UseSameOptions", cbUseSameOptions.Checked.ToString() );
                groupType.SetAttributeValue( "core_checkin_PhoneSearchType", ddlPhoneSearchType.SelectedValue );
                groupType.SetAttributeValue( "core_checkin_RefreshInterval", nbRefreshInterval.Text );
                groupType.SetAttributeValue( "core_checkin_RegularExpressionFilter", tbSearchRegex.Text );
                groupType.SetAttributeValue( "core_checkin_ReuseSameCode", cbReuseCode.Checked.ToString() );

                var searchType = DefinedValueCache.Read( ddlSearchType.SelectedValueAsInt() ?? 0 );
                if ( searchType != null )
                {
                    groupType.SetAttributeValue( "core_checkin_SearchType", searchType.Guid.ToString() );
                }
                else
                {
                    groupType.SetAttributeValue( "core_checkin_SearchType", Rock.SystemGuid.DefinedValue.CHECKIN_SEARCH_TYPE_PHONE_NUMBER );
                }

                groupType.SetAttributeValue( "core_checkin_SecurityCodeLength", nbSecurityCodeLength.Text );
                groupType.SetAttributeValue( "core_checkin_AutoSelectDaysBack", nbAutoSelectDaysBack.Text );

                rockContext.WrapTransaction( () =>
                {
                    rockContext.SaveChanges();
                    groupType.SaveAttributeValues( rockContext );
                } );

                if ( newGroupType )
                {
                    var pageRef = new PageReference( CurrentPageReference.PageId, CurrentPageReference.RouteId );
                    pageRef.Parameters.Add( "CheckinTypeId", groupType.Id.ToString() );
                    NavigateToPage( pageRef );
                }
                else
                {
                    groupType = groupTypeService.Get( groupType.Id );
                    ShowReadonlyDetails( groupType );
                }

                GroupTypeCache.Flush( groupType.Id );
                Rock.CheckIn.KioskDevice.FlushAll();
            }
        }
        /// <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 )
        {
            bool hasValidationErrors = false;

            var rockContext = new RockContext();

            GroupTypeService groupTypeService = new GroupTypeService( rockContext );
            GroupService groupService = new GroupService( rockContext );
            AttributeService attributeService = new AttributeService( rockContext );
            GroupLocationService groupLocationService = new GroupLocationService( rockContext );

            int parentGroupTypeId = hfParentGroupTypeId.ValueAsInt();

            var groupTypeUIList = new List<GroupType>();

            foreach ( var checkinGroupTypeEditor in phCheckinGroupTypes.Controls.OfType<CheckinGroupTypeEditor>().ToList() )
            {
                var groupType = checkinGroupTypeEditor.GetCheckinGroupType( rockContext );
                groupTypeUIList.Add( groupType );
            }

            var groupTypeDBList = new List<GroupType>();

            var groupTypesToDelete = new List<GroupType>();
            var groupsToDelete = new List<Group>();

            var groupTypesToAddUpdate = new List<GroupType>();
            var groupsToAddUpdate = new List<Group>();

            GroupType parentGroupTypeDB = groupTypeService.Get( parentGroupTypeId );
            GroupType parentGroupTypeUI = parentGroupTypeDB.Clone( false );
            parentGroupTypeUI.ChildGroupTypes = groupTypeUIList;

            PopulateDeleteLists( groupTypesToDelete, groupsToDelete, parentGroupTypeDB, parentGroupTypeUI );
            PopulateAddUpdateLists( groupTypesToAddUpdate, groupsToAddUpdate, parentGroupTypeUI );

            int binaryFileFieldTypeID = FieldTypeCache.Read( Rock.SystemGuid.FieldType.BINARY_FILE.AsGuid() ).Id;

            rockContext.WrapTransaction( () =>
            {
                // delete in reverse order to get deepest child items first
                groupsToDelete.Reverse();
                foreach ( var groupToDelete in groupsToDelete )
                {
                    groupService.Delete( groupToDelete );
                }

                // delete in reverse order to get deepest child items first
                groupTypesToDelete.Reverse();
                foreach ( var groupTypeToDelete in groupTypesToDelete )
                {
                    groupTypeService.Delete( groupTypeToDelete );
                }

                rockContext.SaveChanges();

                // Add/Update grouptypes and groups that are in the UI
                // Note:  We'll have to save all the groupTypes without changing the DB value of ChildGroupTypes, then come around again and save the ChildGroupTypes
                // since the ChildGroupTypes may not exist in the database yet
                foreach ( GroupType groupTypeUI in groupTypesToAddUpdate )
                {
                    GroupType groupTypeDB = groupTypeService.Get( groupTypeUI.Guid );
                    if ( groupTypeDB == null )
                    {
                        groupTypeDB = new GroupType();
                        groupTypeDB.Id = 0;
                        groupTypeDB.Guid = groupTypeUI.Guid;
                        groupTypeDB.IsSystem = false;
                        groupTypeDB.ShowInNavigation = false;
                        groupTypeDB.ShowInGroupList = false;
                        groupTypeDB.TakesAttendance = true;
                        groupTypeDB.AttendanceRule = AttendanceRule.None;
                        groupTypeDB.AttendancePrintTo = PrintTo.Default;
                        groupTypeDB.AllowMultipleLocations = true;
                        groupTypeDB.EnableLocationSchedules = true;
                    }

                    groupTypeDB.Name = groupTypeUI.Name;
                    groupTypeDB.Order = groupTypeUI.Order;
                    groupTypeDB.InheritedGroupTypeId = groupTypeUI.InheritedGroupTypeId;

                    groupTypeDB.Attributes = groupTypeUI.Attributes;
                    groupTypeDB.AttributeValues = groupTypeUI.AttributeValues;

                    if ( groupTypeDB.Id == 0 )
                    {
                        groupTypeService.Add( groupTypeDB );
                    }

                    if ( !groupTypeDB.IsValid )
                    {
                        hasValidationErrors = true;
                        CheckinGroupTypeEditor groupTypeEditor = phCheckinGroupTypes.ControlsOfTypeRecursive<CheckinGroupTypeEditor>().First( a => a.GroupTypeGuid == groupTypeDB.Guid );
                        groupTypeEditor.Expanded = true;

                        return;
                    }

                    rockContext.SaveChanges();

                    groupTypeDB.SaveAttributeValues( rockContext );

                    // get fresh from database to make sure we have Id so we can update the CheckinLabel Attributes
                    groupTypeDB = groupTypeService.Get( groupTypeDB.Guid );

                    // rebuild the CheckinLabel attributes from the UI (brute-force)
                    foreach ( var labelAttributeDB in CheckinGroupTypeEditor.GetCheckinLabelAttributes( groupTypeDB.Attributes, rockContext ) )
                    {
                        var attribute = attributeService.Get( labelAttributeDB.Value.Guid );
                        Rock.Web.Cache.AttributeCache.Flush( attribute.Id );
                        attributeService.Delete( attribute );
                    }

                    rockContext.SaveChanges();

                    foreach ( var checkinLabelAttributeInfo in GroupTypeCheckinLabelAttributesState[groupTypeUI.Guid] )
                    {
                        var attribute = new Rock.Model.Attribute();
                        attribute.AttributeQualifiers.Add( new AttributeQualifier { Key = "binaryFileType", Value = Rock.SystemGuid.BinaryFiletype.CHECKIN_LABEL } );
                        attribute.Guid = Guid.NewGuid();
                        attribute.FieldTypeId = binaryFileFieldTypeID;
                        attribute.EntityTypeId = EntityTypeCache.GetId( typeof( GroupType ) );
                        attribute.EntityTypeQualifierColumn = "Id";
                        attribute.EntityTypeQualifierValue = groupTypeDB.Id.ToString();
                        attribute.DefaultValue = checkinLabelAttributeInfo.BinaryFileGuid.ToString();
                        attribute.Key = checkinLabelAttributeInfo.AttributeKey;
                        attribute.Name = checkinLabelAttributeInfo.FileName;

                        if ( !attribute.IsValid )
                        {
                            hasValidationErrors = true;
                            CheckinGroupTypeEditor groupTypeEditor = phCheckinGroupTypes.ControlsOfTypeRecursive<CheckinGroupTypeEditor>().First( a => a.GroupTypeGuid == groupTypeDB.Guid );
                            groupTypeEditor.Expanded = true;

                            return;
                        }

                        attributeService.Add( attribute );
                    }

                    rockContext.SaveChanges();
                }

                // Add/Update Groups
                foreach ( var groupUI in groupsToAddUpdate )
                {
                    Group groupDB = groupService.Get( groupUI.Guid );
                    if ( groupDB == null )
                    {
                        groupDB = new Group();
                        groupDB.Guid = groupUI.Guid;
                    }

                    groupDB.Name = groupUI.Name;

                    // delete any GroupLocations that were removed in the UI
                    foreach ( var groupLocationDB in groupDB.GroupLocations.ToList() )
                    {
                        if ( !groupUI.GroupLocations.Select( a => a.LocationId ).Contains( groupLocationDB.LocationId ) )
                        {
                            groupLocationService.Delete( groupLocationDB );
                        }
                    }

                    // add any GroupLocations that were added in the UI
                    foreach ( var groupLocationUI in groupUI.GroupLocations )
                    {
                        if ( !groupDB.GroupLocations.Select( a => a.LocationId ).Contains( groupLocationUI.LocationId ) )
                        {
                            GroupLocation groupLocationDB = new GroupLocation { LocationId = groupLocationUI.LocationId };
                            groupDB.GroupLocations.Add( groupLocationDB );
                        }
                    }

                    groupDB.Order = groupUI.Order;

                    // get GroupTypeId from database in case the groupType is new
                    groupDB.GroupTypeId = groupTypeService.Get( groupUI.GroupType.Guid ).Id;
                    groupDB.Attributes = groupUI.Attributes;
                    groupDB.AttributeValues = groupUI.AttributeValues;

                    if ( groupDB.Id == 0 )
                    {
                        groupService.Add( groupDB );
                    }

                    if ( !groupDB.IsValid )
                    {
                        hasValidationErrors = true;
                        hasValidationErrors = true;
                        CheckinGroupEditor groupEditor = phCheckinGroupTypes.ControlsOfTypeRecursive<CheckinGroupEditor>().First( a => a.GroupGuid == groupDB.Guid );
                        groupEditor.Expanded = true;

                        return;
                    }

                    rockContext.SaveChanges();

                    groupDB.SaveAttributeValues();
                }

                /* now that we have all the grouptypes saved, now lets go back and save them again with the current UI ChildGroupTypes */

                // save main parentGroupType with current UI ChildGroupTypes
                parentGroupTypeDB.ChildGroupTypes = new List<GroupType>();
                parentGroupTypeDB.ChildGroupTypes.Clear();
                foreach ( var childGroupTypeUI in parentGroupTypeUI.ChildGroupTypes )
                {
                    var childGroupTypeDB = groupTypeService.Get( childGroupTypeUI.Guid );
                    parentGroupTypeDB.ChildGroupTypes.Add( childGroupTypeDB );
                }

                rockContext.SaveChanges();

                // loop thru all the other GroupTypes in the UI and save their childgrouptypes
                foreach ( var groupTypeUI in groupTypesToAddUpdate )
                {
                    var groupTypeDB = groupTypeService.Get( groupTypeUI.Guid );
                    groupTypeDB.ChildGroupTypes = new List<GroupType>();
                    groupTypeDB.ChildGroupTypes.Clear();
                    foreach ( var childGroupTypeUI in groupTypeUI.ChildGroupTypes )
                    {
                        var childGroupTypeDB = groupTypeService.Get( childGroupTypeUI.Guid );
                        groupTypeDB.ChildGroupTypes.Add( childGroupTypeDB );
                    }
                }

                rockContext.SaveChanges();
            } );

            if ( !hasValidationErrors )
            {
                NavigateToParentPage();
            }
        }
        /// <summary>
        /// Handles the SaveClick event of the mdAddCheckinGroupType 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 mdAddCheckinGroupType_SaveClick( object sender, EventArgs e )
        {
            var rockContext = new RockContext();
            var groupTypeService = new GroupTypeService( rockContext );
            
            GroupType groupType = new GroupType();
            groupType.Name = tbGroupTypeName.Text;
            groupType.GroupTypePurposeValueId = DefinedValueCache.Read( Rock.SystemGuid.DefinedValue.GROUPTYPE_PURPOSE_CHECKIN_TEMPLATE ).Id;
            groupType.ShowInNavigation = false;
            groupType.ShowInGroupList = false;

            groupTypeService.Add( groupType );

            rockContext.SaveChanges();

            mdAddCheckinGroupType.Hide();

            BindGrid();
        }
        /// <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 )
        {
            GroupType groupType;

            using ( new UnitOfWorkScope() )
            {
                GroupTypeService groupTypeService = new GroupTypeService();
                GroupTypeRoleService groupTypeRoleService = new GroupTypeRoleService();
                AttributeService attributeService = new AttributeService();
                AttributeQualifierService qualifierService = new AttributeQualifierService();
                CategoryService categoryService = new CategoryService();

                int groupTypeId = int.Parse( hfGroupTypeId.Value );

                if ( groupTypeId == 0 )
                {
                    groupType = new GroupType();
                    groupTypeService.Add( groupType, CurrentPersonId );
                }
                else
                {
                    groupType = groupTypeService.Get( groupTypeId );

                    // selected roles
                    var selectedRoleGuids = GroupTypeRolesState.Select( r => r.Guid );
                    foreach ( var role in groupType.Roles.Where( r => !selectedRoleGuids.Contains( r.Guid ) ).ToList() )
                    {
                        groupType.Roles.Remove( role );
                        groupTypeRoleService.Delete( role, CurrentPersonId );
                    }
                }

                foreach ( var roleState in GroupTypeRolesState )
                {
                    GroupTypeRole role = groupType.Roles.Where( r => r.Guid == roleState.Guid ).FirstOrDefault();
                    if ( role == null )
                    {
                        role = new GroupTypeRole();
                        groupType.Roles.Add( role );
                    }
                    else
                    {
                        roleState.Id = role.Id;
                        roleState.Guid = role.Guid;
                    }

                    role.CopyPropertiesFrom( roleState );
                }

                GroupLocationPickerMode locationSelectionMode = GroupLocationPickerMode.None;
                foreach(ListItem li in cblLocationSelectionModes.Items)
                {
                    if ( li.Selected )
                    {
                        locationSelectionMode = locationSelectionMode | (GroupLocationPickerMode)li.Value.AsInteger().Value;
                    }
                }

                groupType.Name = tbName.Text;
                groupType.Description = tbDescription.Text;
                groupType.GroupTerm = tbGroupTerm.Text;
                groupType.GroupMemberTerm = tbGroupMemberTerm.Text;
                groupType.ShowInGroupList = cbShowInGroupList.Checked;
                groupType.ShowInNavigation = cbShowInNavigation.Checked;
                groupType.IconCssClass = tbIconCssClass.Text;
                groupType.TakesAttendance = cbTakesAttendance.Checked;
                groupType.AttendanceRule = ddlAttendanceRule.SelectedValueAsEnum<AttendanceRule>();
                groupType.AttendancePrintTo = ddlAttendancePrintTo.SelectedValueAsEnum<PrintTo>();
                groupType.LocationSelectionMode = locationSelectionMode;
                groupType.GroupTypePurposeValueId = ddlGroupTypePurpose.SelectedValueAsInt();
                groupType.AllowMultipleLocations = cbAllowMultipleLocations.Checked;
                groupType.InheritedGroupTypeId = gtpInheritedGroupType.SelectedGroupTypeId;

                groupType.ChildGroupTypes = new List<GroupType>();
                groupType.ChildGroupTypes.Clear();
                foreach ( var item in ChildGroupTypesDictionary )
                {
                    var childGroupType = groupTypeService.Get( item.Key );
                    if ( childGroupType != null )
                    {
                        groupType.ChildGroupTypes.Add( childGroupType );
                    }
                }

                DefinedValueService definedValueService = new DefinedValueService();

                groupType.LocationTypes = new List<GroupTypeLocationType>();
                groupType.LocationTypes.Clear();
                foreach ( var item in LocationTypesDictionary )
                {
                    var locationType = definedValueService.Get( item.Key );
                    if ( locationType != null )
                    {
                        groupType.LocationTypes.Add( new GroupTypeLocationType { LocationTypeValueId = locationType.Id } );
                    }
                }

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

                RockTransactionScope.WrapTransaction( () =>
                    {
                        groupTypeService.Save( groupType, CurrentPersonId );

                        /* Save Attributes */
                        string qualifierValue = groupType.Id.ToString();
                        SaveAttributes( new GroupType().TypeId, "Id", qualifierValue, GroupTypeAttributesState, attributeService, qualifierService, categoryService );
                        SaveAttributes( new Group().TypeId, "GroupTypeId", qualifierValue, GroupAttributesState, attributeService, qualifierService, categoryService );
                        SaveAttributes( new GroupMember().TypeId, "GroupTypeId", qualifierValue, GroupMemberAttributesState, attributeService, qualifierService, categoryService );

                        // Reload to save default role
                        groupType = groupTypeService.Get( groupType.Id );
                        groupType.DefaultGroupRole = groupType.Roles.FirstOrDefault( r => r.Guid.Equals( DefaultRoleGuid ) );
                        if ( groupType.DefaultGroupRole == null )
                        {
                            groupType.DefaultGroupRole = groupType.Roles.FirstOrDefault();
                        }
                        groupTypeService.Save( groupType, CurrentPersonId );

                        // Reload the roles and apply their attribute values
                        foreach ( var role in groupTypeRoleService.GetByGroupTypeId( groupType.Id ) )
                        {
                            role.LoadAttributes();
                            var roleState = GroupTypeRolesState.Where( r => r.Guid.Equals( role.Guid ) ).FirstOrDefault();
                            if ( roleState != null && roleState.AttributeValues != null )
                            {
                                foreach ( var attributeValue in roleState.AttributeValues )
                                {
                                    role.SetAttributeValue( attributeValue.Key, roleState.GetAttributeValue( attributeValue.Key ) );
                                }
                                Helper.SaveAttributeValues( role, CurrentPersonId );
                            }
                        }

                    } );

                GroupTypeCache.Flush( groupType.Id );

            }

            NavigateToParentPage();
        }