/// <summary> /// Executes the specified workflow. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="action">The workflow action.</param> /// <param name="entity">The entity.</param> /// <param name="errorMessages">The error messages.</param> /// <returns></returns> /// <exception cref="System.NotImplementedException"></exception> public override bool Execute(RockContext rockContext, Model.WorkflowAction action, Object entity, out List <string> errorMessages) { var checkInState = GetCheckInState(entity, out errorMessages); if (checkInState != null) { bool loadAll = GetAttributeValue(action, "LoadAll").AsBoolean(); foreach (var family in checkInState.CheckIn.GetFamilies(true)) { foreach (var person in family.GetPeople(!loadAll)) { var memberGroupIds = new GroupMemberService(rockContext) .Queryable() .AsNoTracking() .Where(m => m.GroupMemberStatus == GroupMemberStatus.Active && m.PersonId == person.Person.Id) .Select(m => m.GroupId) .ToList(); foreach (var groupType in person.GetGroupTypes(!loadAll)) { var kioskGroupType = checkInState.Kiosk.ActiveGroupTypes(checkInState.ConfiguredGroupTypes) .Where(g => g.GroupType.Id == groupType.GroupType.Id) .FirstOrDefault(); if (kioskGroupType != null) { foreach (var kioskGroup in kioskGroupType.KioskGroups.Where(g => g.IsCheckInActive)) { bool validGroup = true; var configuredKioskGroup = checkInState.ConfiguredGroups; if (configuredKioskGroup.Any()) { validGroup = configuredKioskGroup.Contains(kioskGroup.Group.Id); } if (validGroup && groupType.GroupType.AttendanceRule == AttendanceRule.AlreadyBelongs) { validGroup = memberGroupIds.Contains(kioskGroup.Group.Id); } if (validGroup && !groupType.Groups.Any(g => g.Group.Id == kioskGroup.Group.Id)) { var checkInGroup = new CheckInGroup(); checkInGroup.Group = kioskGroup.Group.Clone(false); checkInGroup.Group.CopyAttributesFrom(kioskGroup.Group); groupType.Groups.Add(checkInGroup); } } } } } } return(true); } return(false); }
/// <summary> /// Job that will sync groups. /// /// Called by the <see cref="IScheduler" /> when a /// <see cref="ITrigger" /> fires that is associated with /// the <see cref="IJob" />. /// </summary> public virtual void Execute(IJobExecutionContext context) { // Get the job setting(s) JobDataMap dataMap = context.JobDetail.JobDataMap; bool requirePasswordReset = dataMap.GetBoolean("RequirePasswordReset"); // Counters for displaying results int groupsSynced = 0; int groupsChanged = 0; string groupName = string.Empty; string dataViewName = string.Empty; var errors = new List <string>(); try { // get groups set to sync var activeSyncIds = new List <int>(); using (var rockContext = new RockContext()) { activeSyncIds = new GroupSyncService(rockContext) .Queryable().AsNoTracking() .Select(x => x.Id) .ToList(); } foreach (var syncId in activeSyncIds) { bool hasSyncChanged = false; // Use a fresh rockContext per syc so that ChangeTracker doesn't get bogged down using (var rockContext = new RockContext()) { // increase the timeout just in case the dataview source is slow rockContext.Database.CommandTimeout = 180; rockContext.SourceOfChange = "Group Sync"; // Get the Sync var sync = new GroupSyncService(rockContext) .Queryable().AsNoTracking() .FirstOrDefault(s => s.Id == syncId); // Ensure that the group's Sync Data View is a person dataview if (sync != null && sync.SyncDataView.EntityTypeId == EntityTypeCache.Get(typeof(Person)).Id) { List <string> syncErrors = new List <string>(); dataViewName = sync.SyncDataView.Name; groupName = sync.Group.Name; // Get the person id's from the dataview (source) var personService = new PersonService(rockContext); var parameterExpression = personService.ParameterExpression; var whereExpression = sync.SyncDataView.GetExpression(personService, parameterExpression, out syncErrors); var sourcePersonIds = new PersonService(rockContext) .Get(parameterExpression, whereExpression) .Select(q => q.Id) .ToList(); // If any error occurred, just skip this sync for now. if (syncErrors.Count > 0) { errors.AddRange(syncErrors); ExceptionLogService.LogException(new Exception(string.Format("An error occurred while trying to GroupSync group '{0}' and data view '{1}' so the sync was skipped. Error: {2}", groupName, dataViewName, String.Join(",", syncErrors)))); continue; } // Get the person id's in the group (target) var targetPersonIds = new GroupMemberService(rockContext) .Queryable().AsNoTracking() .Where(gm => gm.GroupId == sync.GroupId) .Select(gm => gm.PersonId) .ToList(); // Delete people from the group/role that are no longer in the dataview foreach (var personId in targetPersonIds.Where(t => !sourcePersonIds.Contains(t))) { // Use a new context to limit the amount of change-tracking required using (var groupMemberContext = new RockContext()) { // Delete any group members for the role with the person id var groupMemberService = new GroupMemberService(groupMemberContext); foreach (var groupMember in groupMemberService .Queryable() .Where(m => m.GroupId == sync.GroupId && m.GroupRoleId == sync.GroupTypeRoleId && m.PersonId == personId) .ToList()) { groupMemberService.Delete(groupMember); } groupMemberContext.SaveChanges(); // If the Group has an exit email, and person has an email address, send them the exit email if (sync.ExitSystemEmail != null) { var person = new PersonService(groupMemberContext).Get(personId); if (person.Email.IsNotNullOrWhiteSpace()) { // Send the exit email var mergeFields = new Dictionary <string, object>(); mergeFields.Add("Group", sync.Group); mergeFields.Add("Person", person); var emailMessage = new RockEmailMessage(sync.ExitSystemEmail); emailMessage.AddRecipient(new RecipientData(person.Email, mergeFields)); var emailErrors = new List <string>(); emailMessage.Send(out emailErrors); errors.AddRange(emailErrors); } } } hasSyncChanged = true; } foreach (var personId in sourcePersonIds.Where(s => !targetPersonIds.Contains(s))) { // Use a new context to limit the amount of change-tracking required using (var groupMemberContext = new RockContext()) { // Add new person to the group with the role specified in the sync var groupMemberService = new GroupMemberService(groupMemberContext); var newGroupMember = new GroupMember { Id = 0 }; newGroupMember.PersonId = personId; newGroupMember.GroupId = sync.GroupId; newGroupMember.GroupMemberStatus = GroupMemberStatus.Active; newGroupMember.GroupRoleId = sync.GroupTypeRoleId; groupMemberService.Add(newGroupMember); groupMemberContext.SaveChanges(); // If the Group has a welcome email, and person has an email address, send them the welcome email and possibly create a login if (sync.WelcomeSystemEmail != null) { var person = new PersonService(groupMemberContext).Get(personId); if (person.Email.IsNotNullOrWhiteSpace()) { // If the group is configured to add a user account for anyone added to the group, and person does not yet have an // account, add one for them. string newPassword = string.Empty; bool createLogin = sync.AddUserAccountsDuringSync; // Only create a login if requested, no logins exist and we have enough information to generate a username. if (createLogin && !person.Users.Any() && !string.IsNullOrWhiteSpace(person.NickName) && !string.IsNullOrWhiteSpace(person.LastName)) { newPassword = System.Web.Security.Membership.GeneratePassword(9, 1); string username = Rock.Security.Authentication.Database.GenerateUsername(person.NickName, person.LastName); UserLogin login = UserLoginService.Create( groupMemberContext, person, AuthenticationServiceType.Internal, EntityTypeCache.Get(Rock.SystemGuid.EntityType.AUTHENTICATION_DATABASE.AsGuid()).Id, username, newPassword, true, requirePasswordReset); } // Send the welcome email var mergeFields = new Dictionary <string, object>(); mergeFields.Add("Group", sync.Group); mergeFields.Add("Person", person); mergeFields.Add("NewPassword", newPassword); mergeFields.Add("CreateLogin", createLogin); var emailMessage = new RockEmailMessage(sync.WelcomeSystemEmail); emailMessage.AddRecipient(new RecipientData(person.Email, mergeFields)); var emailErrors = new List <string>(); emailMessage.Send(out emailErrors); errors.AddRange(emailErrors); } } } hasSyncChanged = true; } // Increment Groups Changed Counter (if people were deleted or added to the group) if (hasSyncChanged) { groupsChanged++; } // Increment the Groups Synced Counter groupsSynced++; } } } // Format the result message var resultMessage = string.Empty; if (groupsSynced == 0) { resultMessage = "No groups to sync"; } else if (groupsSynced == 1) { resultMessage = "1 group was sync'ed"; } else { resultMessage = string.Format("{0} groups were sync'ed", groupsSynced); } resultMessage += string.Format(" and {0} groups were changed", groupsChanged); if (errors.Any()) { StringBuilder sb = new StringBuilder(); sb.AppendLine(); sb.Append("Errors: "); errors.ForEach(e => { sb.AppendLine(); sb.Append(e); }); string errorMessage = sb.ToString(); resultMessage += errorMessage; throw new Exception(errorMessage); } context.Result = resultMessage; } catch (System.Exception ex) { HttpContext context2 = HttpContext.Current; ExceptionLogService.LogException(ex, context2); throw; } }
/// <summary> /// Executes the specified workflow. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="action">The workflow action.</param> /// <param name="entity">The entity.</param> /// <param name="errorMessages">The error messages.</param> /// <returns></returns> /// <exception cref="System.NotImplementedException"></exception> public override bool Execute(RockContext rockContext, Rock.Model.WorkflowAction action, Object entity, out List <string> errorMessages) { var checkInState = GetCheckInState(entity, out errorMessages); if (checkInState == null) { return(false); } var roomBalanceGroupTypes = GetAttributeValue(action, "RoomBalanceGrouptypes").SplitDelimitedValues().AsGuidList(); bool useGroupMembership = GetAttributeValue(action, "PrioritizeGroupMembership").AsBoolean(); int roomBalanceOverride = GetAttributeValue(action, "BalancingOverride").AsIntegerOrNull() ?? 5; var excludedLocations = GetAttributeValue(action, "ExcludedLocations").SplitDelimitedValues(false) .Select(s => s.Trim()); // get admin-selected attribute keys instead of using a hardcoded key var personSpecialNeedsKey = string.Empty; var personSpecialNeedsGuid = GetAttributeValue(action, "PersonSpecialNeedsAttribute").AsGuid(); if (personSpecialNeedsGuid != Guid.Empty) { personSpecialNeedsKey = AttributeCache.Read(personSpecialNeedsGuid, rockContext).Key; } var groupSpecialNeedsKey = string.Empty; var groupSpecialNeedsGuid = GetAttributeValue(action, "GroupSpecialNeedsAttribute").AsGuid(); if (personSpecialNeedsGuid != Guid.Empty) { groupSpecialNeedsKey = AttributeCache.Read(groupSpecialNeedsGuid, rockContext).Key; } var groupAgeRangeKey = string.Empty; var groupAgeRangeGuid = GetAttributeValue(action, "GroupAgeRangeAttribute").AsGuid(); if (personSpecialNeedsGuid != Guid.Empty) { groupAgeRangeKey = AttributeCache.Read(groupAgeRangeGuid, rockContext).Key; } var groupGradeRangeKey = string.Empty; var groupGradeRangeGuid = GetAttributeValue(action, "GroupGradeRangeAttribute").AsGuid(); if (personSpecialNeedsGuid != Guid.Empty) { groupGradeRangeKey = AttributeCache.Read(groupGradeRangeGuid, rockContext).Key; } // log a warning if any of the attributes are missing or invalid if (string.IsNullOrWhiteSpace(personSpecialNeedsKey)) { action.AddLogEntry(string.Format("The Person Special Needs attribute is not selected or invalid for '{0}'.", action.ActionType.Name)); } if (string.IsNullOrWhiteSpace(groupSpecialNeedsKey)) { action.AddLogEntry(string.Format("The Group Special Needs attribute is not selected or invalid for '{0}'.", action.ActionType.Name)); } if (string.IsNullOrWhiteSpace(groupAgeRangeKey)) { action.AddLogEntry(string.Format("The Group Age Range attribute is not selected or invalid for '{0}'.", action.ActionType.Name)); } if (string.IsNullOrWhiteSpace(groupGradeRangeKey)) { action.AddLogEntry(string.Format("The Group Grade Range attribute is not selected or invalid for '{0}'.", action.ActionType.Name)); } var family = checkInState.CheckIn.Families.FirstOrDefault(f => f.Selected); if (family != null) { // don't process people who already have assignments foreach (var person in family.People.Where(f => f.Selected && !f.GroupTypes.Any(gt => gt.Selected))) { decimal baseVariance = 100; char[] delimiter = { ',' }; // check if this person has special needs var hasSpecialNeeds = person.Person.GetAttributeValue(personSpecialNeedsKey).AsBoolean(); // get a list of room balanced grouptype ID's since CheckInGroup model is a shallow clone var roomBalanceGroupTypeIds = person.GroupTypes.Where(gt => roomBalanceGroupTypes.Contains(gt.GroupType.Guid)) .Select(gt => gt.GroupType.Id).ToList(); if (person.GroupTypes.Count > 0) { CheckInGroupType bestGroupType = null; IEnumerable <CheckInGroup> validGroups; if (person.GroupTypes.Count == 1) { bestGroupType = person.GroupTypes.FirstOrDefault(); validGroups = bestGroupType.Groups; } else { // Start with unfiltered groups since one criteria may not match exactly ( SN > Grade > Age ) validGroups = person.GroupTypes.SelectMany(gt => gt.Groups); } // check how many groups exist without getting the whole list int numValidGroups = validGroups.Take(2).Count(); if (numValidGroups > 0) { CheckInGroup bestGroup = null; IEnumerable <CheckInLocation> validLocations; if (numValidGroups == 1) { bestGroup = validGroups.FirstOrDefault(); validLocations = bestGroup.Locations; } else { // Select by group assignment first if (useGroupMembership) { var personAssignments = new GroupMemberService(rockContext).GetByPersonId(person.Person.Id) .Select(gm => gm.Group.Id).ToList(); if (personAssignments.Count > 0) { bestGroup = validGroups.FirstOrDefault(g => personAssignments.Contains(g.Group.Id)); } } // Select group by best fit if (bestGroup == null) { // Check age and special needs CheckInGroup closestAgeGroup = null; CheckInGroup closestNeedsGroup = null; var ageGroups = validGroups.Where(g => g.Group.AttributeValues.ContainsKey(groupAgeRangeKey) && g.Group.AttributeValues[groupAgeRangeKey].Value != null && g.Group.AttributeValues.ContainsKey(personSpecialNeedsKey) == hasSpecialNeeds ) .ToList() .Select(g => new { Group = g, AgeRange = g.Group.AttributeValues[groupAgeRangeKey].Value .Split(delimiter, StringSplitOptions.None) .Where(av => !string.IsNullOrEmpty(av)) .Select(av => av.AsType <decimal>()) }) .ToList(); if (ageGroups.Count > 0) { if (person.Person.Age != null) { baseVariance = 100; decimal personAge = (decimal)person.Person.AgePrecise; foreach (var ageGroup in ageGroups.Where(g => g.AgeRange.Any())) { var minAge = ageGroup.AgeRange.First(); var maxAge = ageGroup.AgeRange.Last(); var ageVariance = maxAge - minAge; if (maxAge >= personAge && minAge <= personAge && ageVariance < baseVariance) { closestAgeGroup = ageGroup.Group; baseVariance = ageVariance; if (hasSpecialNeeds) { closestNeedsGroup = closestAgeGroup; } } } } else if (hasSpecialNeeds) { // person has special needs but not an age, assign to first special needs group closestNeedsGroup = ageGroups.FirstOrDefault().Group; } } // Check grade CheckInGroup closestGradeGroup = null; if (person.Person.GradeOffset != null) { var gradeValues = DefinedTypeCache.Read(new Guid(Rock.SystemGuid.DefinedType.SCHOOL_GRADES)).DefinedValues; var gradeGroups = validGroups.Where(g => g.Group.AttributeValues.ContainsKey(groupGradeRangeKey) && g.Group.AttributeValues[groupGradeRangeKey].Value != null) .ToList() .Select(g => new { Group = g, GradeOffsets = g.Group.AttributeValues[groupGradeRangeKey].Value .Split(delimiter, StringSplitOptions.None) .Where(av => !string.IsNullOrEmpty(av)) .Select(av => gradeValues.FirstOrDefault(v => v.Guid == new Guid(av))) .Select(av => av.Value.AsDecimal()) }) .ToList(); // Only check groups that have valid grade offsets if (person.Person.GradeOffset != null && gradeGroups.Count > 0) { baseVariance = 100; decimal gradeOffset = (decimal)person.Person.GradeOffset.Value; foreach (var gradeGroup in gradeGroups.Where(g => g.GradeOffsets.Any())) { var minGradeOffset = gradeGroup.GradeOffsets.First(); var maxGradeOffset = gradeGroup.GradeOffsets.Last(); var gradeVariance = minGradeOffset - maxGradeOffset; if (minGradeOffset >= gradeOffset && maxGradeOffset <= gradeOffset && gradeVariance < baseVariance) { closestGradeGroup = gradeGroup.Group; baseVariance = gradeVariance; } } /* ======================================================== * * optional scenario: find the next closest grade group * ========================================================= * * if (grade > max) * grade - max * else if (grade < min) * min - grade * else 0; * * // add a tiny variance to offset larger groups: * result += ((max - min)/100) * ========================================================= */ } } // Assignment priority: Group Membership, then Ability, then Grade, then Age, then the first non-excluded group // NOTE: if group member is prioritized (and membership exists) this section is skipped entirely bestGroup = closestNeedsGroup ?? closestGradeGroup ?? closestAgeGroup ?? validGroups.FirstOrDefault(g => !g.ExcludedByFilter); // room balance if they fit into multiple groups if (bestGroup != null && roomBalanceGroupTypeIds.Contains(bestGroup.Group.GroupTypeId)) { int?bestScheduleId = null; var availableSchedules = validGroups.SelectMany(g => g.Locations.SelectMany(l => l.Schedules)).DistinctBy(s => s.Schedule.Id).ToList(); if (availableSchedules.Any()) { bestScheduleId = availableSchedules.OrderBy(s => s.StartTime).Select(s => s.Schedule.Id).FirstOrDefault(); } if (bestScheduleId != null) { validGroups = validGroups.Where(g => g.AvailableForSchedule.Contains((int)bestScheduleId)); } var currentGroupAttendance = bestGroup.Locations.Select(l => Helpers.ReadAttendanceBySchedule(l.Location.Id, bestScheduleId)).Sum(); var lowestGroup = validGroups.Where(g => !g.ExcludedByFilter && !excludedLocations.Contains(g.Group.Name)) .Select(g => new { Group = g, Attendance = g.Locations.Select(l => Helpers.ReadAttendanceBySchedule(l.Location.Id, bestScheduleId)).Sum() }) .OrderBy(g => g.Attendance) .FirstOrDefault(); if (lowestGroup != null && lowestGroup.Attendance < (currentGroupAttendance - roomBalanceOverride)) { bestGroup = lowestGroup.Group; } } } validLocations = bestGroup.Locations; } // check how many locations exist without getting the whole list int numValidLocations = validLocations.Take(2).Count(); if (numValidLocations > 0) { CheckInLocation bestLocation = null; if (numValidLocations == 1) { bestLocation = validLocations.FirstOrDefault(); } else { var filteredLocations = validLocations.Where(l => !l.ExcludedByFilter && !excludedLocations.Contains(l.Location.Name)); // room balance if they fit into multiple locations if (roomBalanceGroupTypeIds.Contains(bestGroup.Group.GroupTypeId)) { int?bestScheduleId = null; var availableSchedules = filteredLocations.SelectMany(l => l.Schedules).DistinctBy(s => s.Schedule.Id).ToList(); if (availableSchedules.Any()) { bestScheduleId = availableSchedules.OrderBy(s => s.StartTime).Select(s => s.Schedule.Id).FirstOrDefault(); } if (bestScheduleId != null) { filteredLocations = filteredLocations.Where(l => l.AvailableForSchedule.Contains((int)bestScheduleId)); } filteredLocations = filteredLocations.OrderBy(l => Helpers.ReadAttendanceBySchedule(l.Location.Id, bestScheduleId)); } bestLocation = filteredLocations.FirstOrDefault(); } if (bestLocation != null && bestLocation.Schedules.Any()) { // finished finding assignment, verify we can select everything var bestSchedule = bestLocation.Schedules.OrderBy(s => s.Schedule.StartTimeOfDay).FirstOrDefault(); if (bestSchedule != null) { bestSchedule.Selected = true; bestSchedule.PreSelected = true; if (bestLocation != null) { bestLocation.PreSelected = true; bestLocation.Selected = true; if (bestGroup != null) { bestGroup.PreSelected = true; bestGroup.Selected = true; bestGroupType = person.GroupTypes.FirstOrDefault(gt => gt.GroupType.Id == bestGroup.Group.GroupTypeId); if (bestGroupType != null) { bestGroupType.Selected = true; bestGroupType.PreSelected = true; person.Selected = true; person.PreSelected = true; } } } } } } } } } } return(true); }
private void BindData() { using (var rockContext = new RockContext()) { var dataView = new DataViewService(rockContext).Get(_dataViewGuid ?? Guid.Empty); if (dataView != null) { var personService = new PersonService(rockContext); // Filter people by dataview var errorMessages = new List <string>(); var paramExpression = personService.ParameterExpression; var whereExpression = dataView.GetExpression(personService, paramExpression, out errorMessages); var personQry = personService .Queryable(false, false).AsNoTracking() .Where(paramExpression, whereExpression, null); var dvPersonIdQry = personQry.Select(p => p.Id); bool filteredQry = false; // Filter by first name string firstName = tbFirstName.Text.Trim(); if (!string.IsNullOrWhiteSpace(firstName)) { personQry = personQry.Where(p => p.FirstName.StartsWith(firstName) || p.NickName.StartsWith(firstName)); filteredQry = true; } // Filter by last name string lastName = tbLastName.Text.Trim(); if (!string.IsNullOrWhiteSpace(lastName)) { personQry = personQry.Where(p => p.LastName.StartsWith(lastName)); filteredQry = true; } if (filteredQry || _showAllPeople) { if (_optOutGroupGuid.HasValue) { var optOutPersonIdQry = new GroupMemberService(rockContext) .Queryable().AsNoTracking() .Where(g => g.Group.Guid.Equals(_optOutGroupGuid.Value)) .Select(g => g.PersonId); personQry = personQry.Where(p => !optOutPersonIdQry.Contains(p.Id)); } if (_showFamily) { BindFamilies(rockContext, personQry, dvPersonIdQry); } else { BindPeople(rockContext, personQry); } } else { rptPeople.Visible = false; rptFamilies.Visible = false; } } else { rptPeople.Visible = false; rptFamilies.Visible = false; ShowMessages(new List <string> { "This block requires a valid Data View setting." }); } if (CurrentPerson != null && _optOutGroupGuid.HasValue) { bool optedOut = new GroupMemberService(rockContext) .Queryable().AsNoTracking() .Any(m => m.PersonId == CurrentPerson.Id && m.Group.Guid.Equals(_optOutGroupGuid.Value)); lbOptInOut.Text = optedOut ? "Opt in to the Directory" : "Opt Out of the Directory"; } } }
public IQueryable <GuestFamily> GetGuestsForFamily(int groupId) { Guid knownRelationshipGuid = new Guid(Rock.SystemGuid.GroupType.GROUPTYPE_KNOWN_RELATIONSHIPS); Guid knownRelationshipOwner = new Guid(Rock.SystemGuid.GroupRole.GROUPROLE_KNOWN_RELATIONSHIPS_OWNER); Guid knownRelationshipCanCheckin = new Guid(Rock.SystemGuid.GroupRole.GROUPROLE_KNOWN_RELATIONSHIPS_CAN_CHECK_IN); RockContext rockContext = new RockContext(); GroupMemberService groupMemberService = new GroupMemberService(rockContext); PersonService personService = new PersonService(rockContext); var familyMembers = groupMemberService.Queryable() .Where(f => f.GroupId == groupId) .Select(f => f.PersonId); var familyMembersKnownRelationshipGroups = new GroupMemberService(rockContext).Queryable() .Where(g => g.Group.GroupType.Guid == knownRelationshipGuid && g.GroupRole.Guid == knownRelationshipOwner && familyMembers.Contains(g.PersonId)) .Select(m => m.GroupId); rockContext.Database.Log = s => System.Diagnostics.Debug.WriteLine(s); var guests = groupMemberService.Queryable() .Where(g => g.GroupRole.Guid == knownRelationshipCanCheckin && familyMembersKnownRelationshipGroups.Contains(g.GroupId)) .Select(g => g.PersonId) .Distinct().ToList(); var guestFamilies = new List <GuestFamily>(); rockContext.Database.Log = null; foreach (var guestPersonId in guests) { var families = personService.GetFamilies(guestPersonId); foreach (var family in families) { if (!guestFamilies.Select(f => f.Id).Contains(family.Id)) { GuestFamily guestFamily = new GuestFamily(); guestFamily.Id = family.Id; guestFamily.Guid = family.Guid; guestFamily.Name = family.Name; guestFamily.FamilyMembers = new List <GuestFamilyMember>(); foreach (var familyMember in family.Members) { GuestFamilyMember guestFamilyMember = new GuestFamilyMember(); guestFamilyMember.Id = familyMember.PersonId; guestFamilyMember.PersonAliasId = familyMember.Person.PrimaryAliasId.Value; guestFamilyMember.Guid = familyMember.Person.Guid; guestFamilyMember.FirstName = familyMember.Person.NickName; guestFamilyMember.LastName = familyMember.Person.LastName; guestFamilyMember.PhotoUrl = familyMember.Person.PhotoUrl; guestFamilyMember.CanCheckin = guests.Contains(familyMember.PersonId); guestFamilyMember.Role = familyMember.GroupRole.Name; guestFamilyMember.Age = familyMember.Person.Age; guestFamilyMember.Gender = familyMember.Person.Gender; guestFamily.FamilyMembers.Add(guestFamilyMember); } guestFamilies.Add(guestFamily); } } } return(guestFamilies.AsQueryable()); }
/// <summary> /// Executes the specified context. /// </summary> /// <param name="context">The context.</param> public void Execute(IJobExecutionContext context) { JobDataMap dataMap = context.JobDetail.JobDataMap; Guid? groupGuid = dataMap.GetString("EligibleFollowers").AsGuidOrNull(); Guid? systemEmailGuid = dataMap.GetString("EmailTemplate").AsGuidOrNull(); int followingEventsSent = 0; if (groupGuid.HasValue && systemEmailGuid.HasValue) { var exceptionMsgs = new List <string>(); using (var rockContext = new RockContext()) { var followingService = new FollowingService(rockContext); var followingEventTypeService = new FollowingEventTypeService(rockContext); var followingEventNotificationService = new FollowingEventNotificationService(rockContext); // Get all the active event types var eventTypes = followingEventTypeService .Queryable().AsNoTracking() .Where(e => e.EntityTypeId.HasValue && e.IsActive) .OrderBy(e => e.Order) .ToList(); // Get the required event types var requiredEventTypes = eventTypes .Where(e => e.IsNoticeRequired) .ToList(); // The people who are eligible to get following event notices based on the group type setting for this job var eligiblePersonIds = new GroupMemberService(rockContext) .Queryable().AsNoTracking() .Where(m => m.Group != null && m.Group.Guid.Equals(groupGuid.Value) && m.GroupMemberStatus == GroupMemberStatus.Active && m.Person != null && m.Person.Email != null && m.Person.Email != string.Empty && m.Person.EmailPreference != EmailPreference.DoNotEmail && m.Person.IsEmailActive ) .Select(m => m.PersonId) .Distinct() .ToList(); // Get all the subscriptions for the eligible people var eventSubscriptions = new FollowingEventSubscriptionService(rockContext) .Queryable("PersonAlias").AsNoTracking() .Where(f => eligiblePersonIds.Contains(f.PersonAlias.PersonId)) .ToList(); // Dictionaries used to store information that will be used to create notification var personSubscriptions = new Dictionary <int, List <int> >(); // Key: personId, Value: list of event type ids that person subscribes to var personFollowings = new Dictionary <int, List <int> >(); // Key: personId, Value: list of following ids that person follows var eventsThatHappened = new Dictionary <int, Dictionary <int, string> >(); // Key: event type id Value: Dictionary of entity id and formatted event notice for the entity //Get the subscriptions for each person foreach (int personId in eligiblePersonIds) { var personEventTypes = eventSubscriptions .Where(s => s.PersonAlias.PersonId == personId) .Select(s => s.EventType) .ToList(); personEventTypes.AddRange(requiredEventTypes); if (personEventTypes.Any()) { personSubscriptions.AddOrIgnore(personId, personEventTypes .OrderBy(e => e.Order) .ThenBy(e => e.Name) .Select(e => e.Id) .Distinct() .ToList()); } } // Get a distinct list of each entitytype/entity that is being followed by anyone that subscribes to events var followings = followingService .Queryable("PersonAlias").AsNoTracking() .Where(f => personSubscriptions.Keys.Contains(f.PersonAlias.PersonId)) .ToList(); // group the followings by their type var followedEntityIds = new Dictionary <int, List <int> >(); foreach (var followedEntity in followings .Select(f => new { f.EntityTypeId, f.EntityId }) .Distinct()) { followedEntityIds.AddOrIgnore(followedEntity.EntityTypeId, new List <int>()); followedEntityIds[followedEntity.EntityTypeId].Add(followedEntity.EntityId); } // group the followings by the follower foreach (int personId in personSubscriptions.Select(s => s.Key)) { var personFollowing = followings .Where(f => f.PersonAlias.PersonId == personId) .Select(f => f.Id) .ToList(); personFollowings.Add(personId, personFollowing); } var timestamp = RockDateTime.Now; // foreach followed entitytype foreach (var keyVal in followedEntityIds) { // Get the entitytype EntityTypeCache itemEntityType = EntityTypeCache.Get(keyVal.Key); if (itemEntityType != null && itemEntityType.AssemblyName != null) { // get the actual type of what is being followed Type entityType = itemEntityType.GetEntityType(); if (entityType != null) { var dbContext = Reflection.GetDbContextForEntityType(entityType); if (dbContext != null) { var serviceInstance = Reflection.GetServiceForEntityType(entityType, dbContext); if (serviceInstance != null) { MethodInfo qryMethod = serviceInstance.GetType().GetMethod("Queryable", new Type[] { }); var entityQry = qryMethod.Invoke(serviceInstance, new object[] { }) as IQueryable <IEntity>; // If looking at person alias following, make sure to exclude deceased people if (entityType == typeof(Rock.Model.PersonAlias)) { var personAliasQry = entityQry as IQueryable <PersonAlias>; if (personAliasQry != null) { entityQry = personAliasQry.Where(p => !p.Person.IsDeceased); } } var entityList = entityQry.Where(q => keyVal.Value.Contains(q.Id)).ToList(); // If there are any followed entities of this type if (entityList.Any()) { // Get the active event types for this entity type foreach (var eventType in eventTypes.Where(e => e.FollowedEntityTypeId == keyVal.Key)) { try { // Get the component var eventComponent = eventType.GetEventComponent(); if (eventComponent != null) { // Get the previous notifications for this event type var previousNotifications = followingEventNotificationService .Queryable() .Where(n => n.FollowingEventTypeId == eventType.Id) .ToList(); // check each entity that is followed (by anyone) foreach (IEntity entity in entityList) { var previousNotification = previousNotifications .Where(n => n.EntityId == entity.Id) .FirstOrDefault(); DateTime?lastNotification = previousNotification != null ? previousNotification.LastNotified : (DateTime?)null; // If ok to send on this day var today = RockDateTime.Today; if (eventType.SendOnWeekends || (today.DayOfWeek != DayOfWeek.Saturday && today.DayOfWeek != DayOfWeek.Sunday)) { // if the event happened if (eventComponent.HasEventHappened(eventType, entity, lastNotification)) { // Store the event type id and the entity for later processing of notifications eventsThatHappened.AddOrIgnore(eventType.Id, new Dictionary <int, string>()); eventsThatHappened[eventType.Id].Add(entity.Id, eventComponent.FormatEntityNotification(eventType, entity)); if (previousNotification == null) { previousNotification = new FollowingEventNotification(); previousNotification.FollowingEventTypeId = eventType.Id; previousNotification.EntityId = entity.Id; followingEventNotificationService.Add(previousNotification); } previousNotification.LastNotified = timestamp; } } } rockContext.SaveChanges(); } eventType.LastCheckDateTime = RockDateTime.Now; } catch (Exception ex) { exceptionMsgs.Add(string.Format("An exception occurred calculating events for the '{0}' suggestion type:{1} {2}", eventType.Name, Environment.NewLine, ex.Messages().AsDelimited(Environment.NewLine + " "))); ExceptionLogService.LogException(ex, System.Web.HttpContext.Current); } } } } } } } } // send notifications var possibleRecipients = new PersonService(rockContext) .Queryable().AsNoTracking() .Where(p => personSubscriptions.Keys.Contains(p.Id)) .ToList(); // Loop through the possible recipients that actually subscribe to events foreach (var personSubscription in personSubscriptions) { // Get the recipient person int personId = personSubscription.Key; var person = possibleRecipients.Where(p => p.Id == personId).FirstOrDefault(); if (person != null) { try { // Make sure person is actually following anything if (personFollowings.ContainsKey(personId)) { // Dictionary to store the entities that had an event for each event type var personEventTypeNotices = new List <FollowingEventTypeNotices>(); // Get the event types that person subscribes to foreach (var eventType in eventsThatHappened.Where(e => personSubscription.Value.Contains(e.Key))) { // Get the EntityTypeId for this event type int entityTypeId = eventTypes .Where(e => e.Id == eventType.Key) .Select(e => e.FollowedEntityTypeId.Value) .FirstOrDefault(); // Find all the entities with this event type that the person follows var personFollowedEntityIds = followings .Where(f => personFollowings[personId].Contains(f.Id) && f.EntityTypeId == entityTypeId) .Select(f => f.EntityId) .ToList(); // Get any of those entities that had an event happen var personFollowedEntities = eventType.Value .Where(e => personFollowedEntityIds.Contains(e.Key)) .ToList(); // If any were found if (personFollowedEntities.Any()) { // Add the entry var eventTypeObj = eventTypes.Where(e => e.Id == eventType.Key).FirstOrDefault(); if (eventTypeObj != null) { personEventTypeNotices.Add(new FollowingEventTypeNotices(eventTypeObj, personFollowedEntities.Select(e => e.Value).ToList())); } } } // If there are any events for any of the entities that this person follows, send a notification if (personEventTypeNotices.Any()) { // Send the notice var mergeFields = new Dictionary <string, object>(); mergeFields.Add("Person", person); mergeFields.Add("EventTypes", personEventTypeNotices.OrderBy(e => e.EventType.Order).ToList()); var emailMessage = new RockEmailMessage(systemEmailGuid.Value); emailMessage.AddRecipient(new RockEmailMessageRecipient(person, mergeFields)); var errors = new List <string>(); emailMessage.Send(out errors); exceptionMsgs.AddRange(errors); if (!errors.Any()) { followingEventsSent++; } } } } catch (Exception ex) { exceptionMsgs.Add(string.Format("An exception occurred sending event notice to '{0}':{1} {2}", person.FullName, Environment.NewLine, ex.Messages().AsDelimited(Environment.NewLine + " "))); ExceptionLogService.LogException(ex, System.Web.HttpContext.Current); } } } } context.Result = string.Format("{0} following events emails sent", followingEventsSent); if (exceptionMsgs.Any()) { throw new Exception("One or more exceptions occurred calculating following events..." + Environment.NewLine + exceptionMsgs.AsDelimited(Environment.NewLine)); } } }
/// <summary> /// Executes the specified context. /// </summary> /// <param name="context">The context.</param> public void Execute(IJobExecutionContext context) { var exceptionMsgs = new List <string>(); JobDataMap dataMap = context.JobDetail.JobDataMap; Guid? groupGuid = dataMap.GetString("EligibleFollowers").AsGuidOrNull(); Guid? systemEmailGuid = dataMap.GetString("EmailTemplate").AsGuidOrNull(); int followingSuggestionsEmailsSent = 0; int followingSuggestionsSuggestionsTotal = 0; if (groupGuid.HasValue && systemEmailGuid.HasValue) { using (var rockContext = new RockContext()) { var followingService = new FollowingService(rockContext); // The people who are eligible to get following suggestions based on the group type setting for this job var eligiblePersonIds = new GroupMemberService(rockContext) .Queryable().AsNoTracking() .Where(m => m.Group != null && m.Group.Guid.Equals(groupGuid.Value) && m.GroupMemberStatus == GroupMemberStatus.Active && m.Person != null && m.Person.Email != null && m.Person.Email != "") .Select(m => m.PersonId) .Distinct(); // check to see if there are any event types that require notification var followerPersonIds = new List <int>(); if (new FollowingEventTypeService(rockContext) .Queryable().AsNoTracking() .Any(e => e.IsNoticeRequired)) { // if so, include all eligible people followerPersonIds = eligiblePersonIds.ToList(); } else { // if not, filter the list of eligible people down to only those that actually have subscribed to one or more following events followerPersonIds = new FollowingEventSubscriptionService(rockContext) .Queryable().AsNoTracking() .Where(f => eligiblePersonIds.Contains(f.PersonAlias.PersonId)) .Select(f => f.PersonAlias.PersonId) .Distinct() .ToList(); } if (followerPersonIds.Any()) { // Get the primary person alias id for each of the followers var primaryAliasIds = new Dictionary <int, int>(); new PersonAliasService(rockContext) .Queryable().AsNoTracking() .Where(a => followerPersonIds.Contains(a.PersonId) && a.PersonId == a.AliasPersonId) .ToList() .ForEach(a => primaryAliasIds.AddOrIgnore(a.PersonId, a.Id)); // Get current date/time. var timestamp = RockDateTime.Now; var suggestionTypes = new FollowingSuggestionTypeService(rockContext) .Queryable().AsNoTracking() .Where(s => s.IsActive) .OrderBy(s => s.Name) .ToList(); var components = new Dictionary <int, SuggestionComponent>(); var suggestedEntities = new Dictionary <int, Dictionary <int, IEntity> >(); foreach (var suggestionType in suggestionTypes) { try { // Get the suggestion type component var suggestionComponent = suggestionType.GetSuggestionComponent(); if (suggestionComponent != null) { components.Add(suggestionType.Id, suggestionComponent); // Get the entitytype for this suggestion type var suggestionEntityType = EntityTypeCache.Read(suggestionComponent.FollowedType); if (suggestionEntityType != null) { var entityIds = new List <int>(); // Call the components method to return all of it's suggestions var personEntitySuggestions = suggestionComponent.GetSuggestions(suggestionType, followerPersonIds); // If any suggestions were returned by the component if (personEntitySuggestions.Any()) { int entityTypeId = suggestionEntityType.Id; string reasonNote = suggestionType.ReasonNote; // Get the existing followings for any of the followers var existingFollowings = new Dictionary <int, List <int> >(); foreach (var following in followingService.Queryable("PersonAlias").AsNoTracking() .Where(f => f.EntityTypeId == entityTypeId && followerPersonIds.Contains(f.PersonAlias.PersonId))) { existingFollowings.AddOrIgnore(following.PersonAlias.PersonId, new List <int>()); existingFollowings[following.PersonAlias.PersonId].Add(following.EntityId); } // Loop through each follower foreach (var followerPersonId in personEntitySuggestions .Select(s => s.PersonId) .Distinct()) { using (var suggestionContext = new RockContext()) { var followingSuggestedService = new FollowingSuggestedService(suggestionContext); // Read all the existing suggestions for this type and the returned followers var existingSuggestions = followingSuggestedService .Queryable("PersonAlias") .Where(s => s.SuggestionTypeId == suggestionType.Id && s.PersonAlias.PersonId == followerPersonId) .ToList(); // Look through the returned suggestions foreach (var followedEntityId in personEntitySuggestions .Where(s => s.PersonId == followerPersonId) .Select(s => s.EntityId)) { // Make sure person isn't already following this entity if (!existingFollowings.ContainsKey(followerPersonId) || !existingFollowings[followerPersonId].Contains(followedEntityId)) { // If this person had a primary alias id if (primaryAliasIds.ContainsKey(followerPersonId)) { entityIds.Add(followedEntityId); // Look for existing suggestion for this person and entity var suggestion = existingSuggestions .Where(s => s.EntityId == followedEntityId) .OrderByDescending(s => s.StatusChangedDateTime) .FirstOrDefault(); // If not found, add one if (suggestion == null) { suggestion = new FollowingSuggested(); suggestion.EntityTypeId = entityTypeId; suggestion.EntityId = followedEntityId; suggestion.PersonAliasId = primaryAliasIds[followerPersonId]; suggestion.SuggestionTypeId = suggestionType.Id; suggestion.Status = FollowingSuggestedStatus.PendingNotification; suggestion.StatusChangedDateTime = timestamp; followingSuggestedService.Add(suggestion); } else { // If found, and it has not been ignored, and it's time to promote again, update the promote date if (suggestion.Status != FollowingSuggestedStatus.Ignored && ( !suggestionType.ReminderDays.HasValue || !suggestion.LastPromotedDateTime.HasValue || suggestion.LastPromotedDateTime.Value.AddDays(suggestionType.ReminderDays.Value) <= timestamp )) { if (suggestion.Status != FollowingSuggestedStatus.PendingNotification) { suggestion.StatusChangedDateTime = timestamp; suggestion.Status = FollowingSuggestedStatus.PendingNotification; } } } } } } // Save the suggestions for this type suggestionContext.SaveChanges(); } } } // If any entities are being suggested for this type, query database for them and save to dictionary if (entityIds.Any()) { if (suggestionEntityType.AssemblyName != null) { // get the actual type of what is being followed Type entityType = suggestionEntityType.GetEntityType(); if (entityType != null) { // Get generic queryable method and query all the entities that are being followed Type[] modelType = { entityType }; Type genericServiceType = typeof(Rock.Data.Service <>); Type modelServiceType = genericServiceType.MakeGenericType(modelType); Rock.Data.IService serviceInstance = Activator.CreateInstance(modelServiceType, new object[] { rockContext }) as IService; MethodInfo qryMethod = serviceInstance.GetType().GetMethod("Queryable", new Type[] { }); var entityQry = qryMethod.Invoke(serviceInstance, new object[] { }) as IQueryable <IEntity>; var entityList = entityQry.AsNoTracking().Where(q => entityIds.Contains(q.Id)).ToList(); if (entityList != null && entityList.Any()) { var entities = new Dictionary <int, IEntity>(); entityList.ForEach(e => entities.Add(e.Id, e)); suggestedEntities.Add(suggestionType.Id, entities); } } } } } } } catch (Exception ex) { exceptionMsgs.Add(string.Format("An exception occurred calculating suggestions for the '{0}' suggestion type:{1} {2}", suggestionType.Name, Environment.NewLine, ex.Messages().AsDelimited(Environment.NewLine + " "))); ExceptionLogService.LogException(ex, System.Web.HttpContext.Current); } } var allSuggestions = new FollowingSuggestedService(rockContext) .Queryable("PersonAlias") .Where(s => s.Status == FollowingSuggestedStatus.PendingNotification) .ToList(); var suggestionPersonIds = allSuggestions .Where(s => followerPersonIds.Contains(s.PersonAlias.PersonId)) .Select(s => s.PersonAlias.PersonId) .Distinct() .ToList(); var appRoot = Rock.Web.Cache.GlobalAttributesCache.Read(rockContext).GetValue("ExternalApplicationRoot"); foreach (var person in new PersonService(rockContext) .Queryable().AsNoTracking() .Where(p => suggestionPersonIds.Contains(p.Id)) .ToList()) { try { var personSuggestionNotices = new List <FollowingSuggestionNotices>(); foreach (var suggestionType in suggestionTypes) { var component = components.ContainsKey(suggestionType.Id) ? components[suggestionType.Id] : null; if (component != null && suggestedEntities.ContainsKey(suggestionType.Id)) { var entities = new List <IEntity>(); foreach (var suggestion in allSuggestions .Where(s => s.PersonAlias.PersonId == person.Id && s.SuggestionTypeId == suggestionType.Id) .ToList()) { if (suggestedEntities[suggestionType.Id].ContainsKey(suggestion.EntityId)) { entities.Add(suggestedEntities[suggestionType.Id][suggestion.EntityId]); suggestion.LastPromotedDateTime = timestamp; suggestion.Status = FollowingSuggestedStatus.Suggested; } } var notices = new List <string>(); foreach (var entity in component.SortEntities(entities)) { notices.Add(component.FormatEntityNotification(suggestionType, entity)); } if (notices.Any()) { personSuggestionNotices.Add(new FollowingSuggestionNotices(suggestionType, notices)); } } } if (personSuggestionNotices.Any()) { // Send the notice var recipients = new List <RecipientData>(); var mergeFields = new Dictionary <string, object>(); mergeFields.Add("Person", person); mergeFields.Add("Suggestions", personSuggestionNotices.OrderBy(s => s.SuggestionType.Order).ToList()); recipients.Add(new RecipientData(person.Email, mergeFields)); Email.Send(systemEmailGuid.Value, recipients, appRoot); followingSuggestionsEmailsSent += recipients.Count(); followingSuggestionsSuggestionsTotal += personSuggestionNotices.Count(); } rockContext.SaveChanges(); } catch (Exception ex) { exceptionMsgs.Add(string.Format("An exception occurred sending suggestions to '{0}':{1} {2}", person.FullName, Environment.NewLine, ex.Messages().AsDelimited(Environment.NewLine + " "))); ExceptionLogService.LogException(ex, System.Web.HttpContext.Current); } } } } } context.Result = string.Format("A total of {0} following suggestions sent to {1} people", followingSuggestionsSuggestionsTotal, followingSuggestionsEmailsSent); if (exceptionMsgs.Any()) { throw new Exception("One or more exceptions occurred calculating suggestions..." + Environment.NewLine + exceptionMsgs.AsDelimited(Environment.NewLine)); } }
/// <summary> /// Shows the view. /// </summary> protected void ShowView() { var rockContext = new RockContext(); int resultCount = Int32.Parse(GetAttributeValue("ResultCount")); int pageNumber = 0; if (!String.IsNullOrEmpty(PageParameter("page"))) { pageNumber = Int32.Parse(PageParameter("page")); } var skipCount = pageNumber * resultCount; var query = new NcoaHistoryService(rockContext).Queryable(); var processed = gfNcoaFilter.GetUserPreference("Processed").ConvertToEnumOrNull <Processed>(); if (processed.HasValue) { if (processed.Value != Processed.All && processed.Value != Processed.ManualUpdateRequiredOrNotProcessed) { query = query.Where(i => i.Processed == processed); } else if (processed.Value == Processed.ManualUpdateRequiredOrNotProcessed) { query = query.Where(i => i.Processed == Processed.ManualUpdateRequired || i.Processed == Processed.NotProcessed); } } var moveDateRange = SlidingDateRangePicker.CalculateDateRangeFromDelimitedValues(gfNcoaFilter.GetUserPreference("Move Date")); if (moveDateRange.Start.HasValue) { query = query.Where(e => e.MoveDate.HasValue && e.MoveDate.Value >= moveDateRange.Start.Value); } if (moveDateRange.End.HasValue) { query = query.Where(e => e.MoveDate.HasValue && e.MoveDate.Value < moveDateRange.End.Value); } var ncoaDateRange = SlidingDateRangePicker.CalculateDateRangeFromDelimitedValues(gfNcoaFilter.GetUserPreference("NCOA Processed Date")); if (ncoaDateRange.Start.HasValue) { query = query.Where(e => e.NcoaRunDateTime >= ncoaDateRange.Start.Value); } if (ncoaDateRange.End.HasValue) { query = query.Where(e => e.NcoaRunDateTime < ncoaDateRange.End.Value); } var moveType = gfNcoaFilter.GetUserPreference("Move Type").ConvertToEnumOrNull <MoveType>(); if (moveType.HasValue) { query = query.Where(i => i.MoveType == moveType); } var addressStatus = gfNcoaFilter.GetUserPreference("Address Status").ConvertToEnumOrNull <AddressStatus>(); if (addressStatus.HasValue) { query = query.Where(i => i.AddressStatus == addressStatus); } var addressInvalidReason = gfNcoaFilter.GetUserPreference("Address Invalid Reason").ConvertToEnumOrNull <AddressInvalidReason>(); if (addressInvalidReason.HasValue) { query = query.Where(i => i.AddressInvalidReason == addressInvalidReason); } decimal?moveDistance = gfNcoaFilter.GetUserPreference("Move Distance").AsDecimalOrNull(); if (moveDistance.HasValue) { query = query.Where(i => i.MoveDistance <= moveDistance.Value); } string lastName = gfNcoaFilter.GetUserPreference("Last Name"); if (!string.IsNullOrWhiteSpace(lastName)) { var personAliasQuery = new PersonAliasService(rockContext) .Queryable() .Where(p => p.Person != null && p.Person.LastName.Contains(lastName)) .Select(p => p.Id); query = query.Where(i => personAliasQuery.Contains(i.PersonAliasId)); } var campusId = gfNcoaFilter.GetUserPreference("Campus").AsIntegerOrNull(); if (campusId.HasValue) { var familyGroupType = GroupTypeCache.Get(Rock.SystemGuid.GroupType.GROUPTYPE_FAMILY.AsGuid()); var personAliasQuery = new PersonAliasService(rockContext).Queryable().AsNoTracking(); var campusQuery = new GroupMemberService(rockContext) .Queryable().AsNoTracking() .Where(m => m.Group.GroupTypeId == familyGroupType.Id && m.Group.CampusId.HasValue && m.Group.CampusId.Value == campusId.Value) .Select(m => m.PersonId) .Join(personAliasQuery, m => m, p => p.PersonId, (m, p) => p.Id); query = query.Where(i => campusQuery.Contains(i.PersonAliasId)); } var filteredRecords = query.ToList(); lTotal.Text = string.Format("Records: {0}", filteredRecords.Count()); #region Grouping rows var ncoaRows = filteredRecords .Where(a => a.MoveType != MoveType.Individual) .GroupBy(a => new { a.FamilyId, a.MoveType, a.MoveDate }) .Select(a => new NcoaRow { Id = a.Select(b => b.Id).Max(), FamilyMemberPersonAliasIds = a.Select(b => b.PersonAliasId).ToList() }).ToList(); var ncoaIndividualRows = filteredRecords .Where(a => a.MoveType == MoveType.Individual) .Select(a => new NcoaRow { Id = a.Id, IndividualPersonAliasId = a.PersonAliasId }).ToList(); ncoaRows.AddRange(ncoaIndividualRows); #endregion var pagedNcoaRows = ncoaRows.OrderBy(a => a.Id).Skip(skipCount).Take(resultCount + 1).ToList(); var familyMemberPersonAliasIds = pagedNcoaRows.SelectMany(r => r.FamilyMemberPersonAliasIds).ToList(); var individualPersonAliasIds = pagedNcoaRows.Select(r => r.IndividualPersonAliasId).ToList(); var people = new PersonAliasService(rockContext) .Queryable().AsNoTracking() .Where(p => familyMemberPersonAliasIds.Contains(p.Id) || individualPersonAliasIds.Contains(p.Id)) .Select(p => new { PersonAliasId = p.Id, Person = p.Person }) .ToList(); foreach (var ncoaRow in pagedNcoaRows) { ncoaRow.FamilyMembers = people .Where(p => ncoaRow.FamilyMemberPersonAliasIds.Contains(p.PersonAliasId)) .Select(p => p.Person) .ToList(); ncoaRow.Individual = people .Where(p => p.PersonAliasId == ncoaRow.IndividualPersonAliasId) .Select(p => p.Person) .FirstOrDefault(); var ncoaHistoryRecord = filteredRecords.Single(a => a.Id == ncoaRow.Id); ncoaRow.OriginalAddress = FormattedAddress(ncoaHistoryRecord.OriginalStreet1, ncoaHistoryRecord.OriginalStreet2, ncoaHistoryRecord.OriginalCity, ncoaHistoryRecord.OriginalState, ncoaHistoryRecord.OriginalPostalCode) .ConvertCrLfToHtmlBr(); ncoaRow.Status = ncoaHistoryRecord.Processed == Processed.Complete ? "Processed" : "Not Processed"; ncoaRow.StatusCssClass = ncoaHistoryRecord.Processed == Processed.Complete ? "label-success" : "label-default"; ncoaRow.ShowButton = false; var family = new GroupService(rockContext).Get(ncoaHistoryRecord.FamilyId); var person = ncoaRow.Individual ?? ncoaRow.FamilyMembers.First(); if (family == null) { family = person.GetFamily(rockContext); } var personService = new PersonService(rockContext); ncoaRow.FamilyName = family.Name; ncoaRow.HeadOftheHousehold = personService.GetHeadOfHousehold(person, family); if (ncoaHistoryRecord.MoveType != MoveType.Individual) { ncoaRow.FamilyMembers = personService.GetFamilyMembers(family, person.Id, true).Select(a => a.Person).ToList(); } else { ncoaRow.FamilyMembers = personService.GetFamilyMembers(family, person.Id, false).Select(a => a.Person).ToList(); } if (ncoaHistoryRecord.AddressStatus == AddressStatus.Invalid) { ncoaRow.TagLine = "Invalid Address"; ncoaRow.TagLineCssClass = "label-warning"; if (ncoaHistoryRecord.Processed != Processed.Complete) { ncoaRow.CommandName = "MarkAddressAsPrevious"; ncoaRow.CommandText = "Mark Address As Previous"; ncoaRow.ShowButton = true; } } if (ncoaHistoryRecord.NcoaType == NcoaType.Month48Move) { ncoaRow.TagLine = "48 Month Move"; ncoaRow.TagLineCssClass = "label-info"; if (ncoaHistoryRecord.Processed != Processed.Complete) { ncoaRow.CommandName = "MarkAddressAsPrevious"; ncoaRow.CommandText = "Mark Address As Previous"; ncoaRow.ShowButton = true; } } if (ncoaHistoryRecord.NcoaType == NcoaType.Move) { ncoaRow.TagLine = ncoaHistoryRecord.MoveType.ConvertToString(); ncoaRow.TagLineCssClass = "label-success"; ncoaRow.MoveDate = ncoaHistoryRecord.MoveDate; ncoaRow.MoveDistance = ncoaHistoryRecord.MoveDistance; ncoaRow.NewAddress = FormattedAddress(ncoaHistoryRecord.UpdatedStreet1, ncoaHistoryRecord.UpdatedStreet2, ncoaHistoryRecord.UpdatedCity, ncoaHistoryRecord.UpdatedState, ncoaHistoryRecord.UpdatedPostalCode) .ConvertCrLfToHtmlBr(); if (ncoaHistoryRecord.Processed != Processed.Complete) { ncoaRow.CommandText = "Mark Processed"; ncoaRow.CommandName = "MarkProcessed"; ncoaRow.ShowButton = true; } } } rptNcoaResultsFamily.DataSource = pagedNcoaRows.Take(resultCount).GroupBy(n => n.FamilyName); rptNcoaResultsFamily.DataBind(); if (pagedNcoaRows.Count() > resultCount) { hlNext.Visible = hlNext.Enabled = true; Dictionary <string, string> queryStringNext = new Dictionary <string, string>(); queryStringNext.Add("page", (pageNumber + 1).ToString()); var pageReferenceNext = new Rock.Web.PageReference(CurrentPageReference.PageId, CurrentPageReference.RouteId, queryStringNext); hlNext.NavigateUrl = pageReferenceNext.BuildUrl(); } else { hlNext.Visible = hlNext.Enabled = false; } // build prev button if (pageNumber == 0) { hlPrev.Visible = hlPrev.Enabled = false; } else { hlPrev.Visible = hlPrev.Enabled = true; Dictionary <string, string> queryStringPrev = new Dictionary <string, string>(); queryStringPrev.Add("page", (pageNumber - 1).ToString()); var pageReferencePrev = new Rock.Web.PageReference(CurrentPageReference.PageId, CurrentPageReference.RouteId, queryStringPrev); hlPrev.NavigateUrl = pageReferencePrev.BuildUrl(); } }
/// <summary> /// Job that will sync groups. /// /// Called by the <see cref="IScheduler" /> when a /// <see cref="ITrigger" /> fires that is associated with /// the <see cref="IJob" />. /// </summary> public virtual void Execute(IJobExecutionContext context) { // Get the job setting(s) JobDataMap dataMap = context.JobDetail.JobDataMap; bool requirePasswordReset = dataMap.GetBoolean("RequirePasswordReset"); // Counters for displaying results int groupsSynced = 0; int groupsChanged = 0; try { // get groups set to sync var groupIdsThatSync = new List <int>(); using (var rockContext = new RockContext()) { groupIdsThatSync = new GroupService(rockContext) .Queryable().AsNoTracking() .Where(g => g.SyncDataViewId != null) .Select(a => a.Id) .ToList(); } foreach (var syncGroupId in groupIdsThatSync) { bool hasGroupChanged = false; // Use a fresh rockContext per group so that ChangeTracker doesn't get bogged down using (var rockContext = new RockContext()) { // increase the timeout just in case the dataview source is slow rockContext.Database.CommandTimeout = 180; // Get the Group var syncGroup = new GroupService(rockContext) .Queryable().AsNoTracking() .FirstOrDefault(t => t.Id == syncGroupId); // Ensure that the group's Sync Data View is a person dataview if (syncGroup.SyncDataView.EntityTypeId == EntityTypeCache.Read(typeof(Person)).Id) { List <string> errorMessages = new List <string>(); // Get the person id's from the dataview (source) var personService = new PersonService(rockContext); var parameterExpression = personService.ParameterExpression; var whereExpression = syncGroup.SyncDataView.GetExpression(personService, parameterExpression, out errorMessages); var sourcePersonIds = new PersonService(rockContext) .Get(parameterExpression, whereExpression) .Select(q => q.Id) .ToList(); // Get the person id's in the group (target) var targetPersonIds = new GroupMemberService(rockContext) .Queryable().AsNoTracking() .Where(gm => gm.GroupId == syncGroup.Id) .Select(gm => gm.PersonId) .ToList(); // Delete people from the group that are no longer in the dataview foreach (var personId in targetPersonIds.Where(t => !sourcePersonIds.Contains(t))) { // Use a new context to limit the amount of change-tracking required using (var groupMemberContext = new RockContext()) { // Delete any group members with the person id var groupMemberService = new GroupMemberService(groupMemberContext); foreach (var groupMember in groupMemberService .Queryable() .Where(m => m.GroupId == syncGroupId && m.PersonId == personId) .ToList()) { groupMemberService.Delete(groupMember); } groupMemberContext.SaveChanges(); // If the Group has an exit email, and person has an email address, send them the exit email if (syncGroup.ExitSystemEmail != null) { var person = new PersonService(groupMemberContext).Get(personId); if (person.Email.IsNotNullOrWhitespace()) { // Send the exit email var mergeFields = new Dictionary <string, object>(); mergeFields.Add("Group", syncGroup); mergeFields.Add("Person", person); var emailMessage = new RockEmailMessage(syncGroup.ExitSystemEmail); emailMessage.AddRecipient(new RecipientData(person.Email, mergeFields)); emailMessage.Send(); } } } hasGroupChanged = true; } // Add people to the group that are in the dataview and not currently in the group int groupRoleId = syncGroup.GroupType.DefaultGroupRoleId ?? syncGroup.GroupType.Roles.FirstOrDefault().Id; foreach (var personId in sourcePersonIds.Where(s => !targetPersonIds.Contains(s))) { // Use a new context to limit the amount of change-tracking required using (var groupMemberContext = new RockContext()) { // Add new person to the group var groupMemberService = new GroupMemberService(groupMemberContext); var newGroupMember = new GroupMember { Id = 0 }; newGroupMember.PersonId = personId; newGroupMember.GroupId = syncGroup.Id; newGroupMember.GroupMemberStatus = GroupMemberStatus.Active; newGroupMember.GroupRoleId = groupRoleId; groupMemberService.Add(newGroupMember); groupMemberContext.SaveChanges(); // If the Group has a welcome email, and person has an email address, send them the welcome email and possibly create a login if (syncGroup.WelcomeSystemEmail != null) { var person = new PersonService(groupMemberContext).Get(personId); if (person.Email.IsNotNullOrWhitespace()) { // If the group is configured to add a user account for anyone added to the group, and person does not yet have an // account, add one for them. string newPassword = string.Empty; bool createLogin = syncGroup.AddUserAccountsDuringSync ?? false; // Only create a login if requested, no logins exist and we have enough information to generate a username. if (createLogin && !person.Users.Any() && !string.IsNullOrWhiteSpace(person.NickName) && !string.IsNullOrWhiteSpace(person.LastName)) { newPassword = System.Web.Security.Membership.GeneratePassword(9, 1); string username = Rock.Security.Authentication.Database.GenerateUsername(person.NickName, person.LastName); UserLogin login = UserLoginService.Create( groupMemberContext, person, AuthenticationServiceType.Internal, EntityTypeCache.Read(Rock.SystemGuid.EntityType.AUTHENTICATION_DATABASE.AsGuid()).Id, username, newPassword, true, requirePasswordReset); } // Send the welcome email var mergeFields = new Dictionary <string, object>(); mergeFields.Add("Group", syncGroup); mergeFields.Add("Person", person); mergeFields.Add("NewPassword", newPassword); mergeFields.Add("CreateLogin", createLogin); var emailMessage = new RockEmailMessage(syncGroup.WelcomeSystemEmail); emailMessage.AddRecipient(new RecipientData(person.Email, mergeFields)); emailMessage.Send(); } } } hasGroupChanged = true; } // Increment Groups Changed Counter (if people were deleted or added to the group) if (hasGroupChanged) { groupsChanged++; } // Increment the Groups Synced Counter groupsSynced++; // If the group changed, and it was a security group, flush the security for the group if (hasGroupChanged && (syncGroup.IsSecurityRole || syncGroup.GroupType.Guid.Equals(Rock.SystemGuid.GroupType.GROUPTYPE_SECURITY_ROLE.AsGuid()))) { Rock.Security.Role.Flush(syncGroup.Id); } } } } // Format the result message var resultMessage = string.Empty; if (groupsSynced == 0) { resultMessage = "No groups to sync"; } else if (groupsSynced == 1) { resultMessage = "1 group was sync'ed"; } else { resultMessage = string.Format("{0} groups were sync'ed", groupsSynced); } resultMessage += string.Format(" and {0} groups were changed", groupsChanged); context.Result = resultMessage; } catch (System.Exception ex) { HttpContext context2 = HttpContext.Current; ExceptionLogService.LogException(ex, context2); throw; } }
/// <summary> /// Executes the specified workflow. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="action">The workflow action.</param> /// <param name="entity">The entity.</param> /// <param name="errorMessages">The error messages.</param> /// <returns></returns> /// <exception cref="System.NotImplementedException"></exception> public override bool Execute(RockContext rockContext, Rock.Model.WorkflowAction action, Object entity, out List <string> errorMessages) { var checkInState = GetCheckInState(entity, out errorMessages); if (checkInState == null) { return(false); } bool roomBalance = GetAttributeValue(action, "RoomBalance").AsBoolean(); bool useGroupMembership = GetAttributeValue(action, "PrioritizeGroupMembership").AsBoolean(); int balanceOverride = GetAttributeValue(action, "DifferentialOverride").AsIntegerOrNull() ?? 10; var family = checkInState.CheckIn.Families.FirstOrDefault(f => f.Selected); if (family != null) { // don't run for people who already have attendance assignments foreach (var person in family.People.Where(f => f.Selected && !f.GroupTypes.Any(gt => gt.Selected))) { decimal baseVariance = 100; char[] delimiter = { ',' }; // variable must be a string to compare to group attribute value var specialNeedsValue = person.Person.GetAttributeValue("IsSpecialNeeds").ToStringSafe(); var isSpecialNeeds = specialNeedsValue.AsBoolean(); if (person.GroupTypes.Count > 0) { CheckInGroupType bestGroupType = null; IEnumerable <CheckInGroup> validGroups; if (person.GroupTypes.Count == 1) { bestGroupType = person.GroupTypes.FirstOrDefault(); validGroups = bestGroupType.Groups; } else { // Start with unfiltered groups since one criteria may not match exactly ( SN > Grade > Age ) validGroups = person.GroupTypes.SelectMany(gt => gt.Groups); } // check how many groups exist without getting the whole list int numValidGroups = validGroups.Take(2).Count(); if (numValidGroups > 0) { CheckInGroup bestGroup = null; IEnumerable <CheckInLocation> validLocations; if (numValidGroups == 1) { bestGroup = validGroups.FirstOrDefault(); validLocations = bestGroup.Locations; } else { // Select by group assignment first if (useGroupMembership) { var personAssignments = new GroupMemberService(rockContext).GetByPersonId(person.Person.Id) .Select(gm => gm.Group.Id).ToList(); if (personAssignments.Count > 0) { bestGroup = validGroups.FirstOrDefault(g => personAssignments.Contains(g.Group.Id)); } } // Select group by best fit if (bestGroup == null) { // Check age and special needs CheckInGroup closestAgeGroup = null; CheckInGroup closestNeedsGroup = null; var ageGroups = validGroups.Where(g => g.Group.AttributeValues.ContainsKey("AgeRange") && g.Group.AttributeValues["AgeRange"].Value != null && g.Group.AttributeValues.ContainsKey("IsSpecialNeeds") == isSpecialNeeds ) .ToList() .Select(g => new { Group = g, AgeRange = g.Group.AttributeValues["AgeRange"].Value .Split(delimiter, StringSplitOptions.None) .Where(av => !string.IsNullOrEmpty(av)) .Select(av => av.AsType <decimal>()) }) .ToList(); if (ageGroups.Count > 0) { if (person.Person.Age != null) { baseVariance = 100; decimal personAge = (decimal)person.Person.AgePrecise; foreach (var ageGroup in ageGroups.Where(g => g.AgeRange.Any())) { var minAge = ageGroup.AgeRange.First(); var maxAge = ageGroup.AgeRange.Last(); var ageVariance = maxAge - minAge; if (maxAge >= personAge && minAge <= personAge && ageVariance < baseVariance) { closestAgeGroup = ageGroup.Group; baseVariance = ageVariance; if (isSpecialNeeds) { closestNeedsGroup = closestAgeGroup; } } } } else if (isSpecialNeeds) { // special needs was checked but no age, assign to first special needs group closestNeedsGroup = ageGroups.FirstOrDefault().Group; } } // Check grade CheckInGroup closestGradeGroup = null; if (person.Person.GradeOffset != null) { var gradeValues = DefinedTypeCache.Read(new Guid(Rock.SystemGuid.DefinedType.SCHOOL_GRADES)).DefinedValues; var gradeGroups = validGroups.Where(g => g.Group.AttributeValues.ContainsKey("GradeRange") && g.Group.AttributeValues["GradeRange"].Value != null) .ToList() .Select(g => new { Group = g, GradeOffsets = g.Group.AttributeValues["GradeRange"].Value .Split(delimiter, StringSplitOptions.None) .Where(av => !string.IsNullOrEmpty(av)) .Select(av => gradeValues.FirstOrDefault(v => v.Guid == new Guid(av))) .Select(av => av.Value.AsDecimal()) }) .ToList(); // Only check groups that have valid grade offsets if (person.Person.GradeOffset != null && gradeGroups.Count > 0) { baseVariance = 100; decimal gradeOffset = (decimal)person.Person.GradeOffset.Value; foreach (var gradeGroup in gradeGroups.Where(g => g.GradeOffsets.Any())) { var minGradeOffset = gradeGroup.GradeOffsets.First(); var maxGradeOffset = gradeGroup.GradeOffsets.Last(); var gradeVariance = minGradeOffset - maxGradeOffset; if (minGradeOffset >= gradeOffset && maxGradeOffset <= gradeOffset && gradeVariance < baseVariance) { closestGradeGroup = gradeGroup.Group; baseVariance = gradeVariance; } } /* ======================================================== * * optional scenario: find the next closest grade group * ========================================================= * * if (grade > max) * grade - max * else if (grade < min) * min - grade * else 0; * * // add a tiny variance to offset larger groups: * result += ((max - min)/100) * ========================================================= */ } } // assignment priority: Ability, then Grade, then Age, then 1st available bestGroup = closestNeedsGroup ?? closestGradeGroup ?? closestAgeGroup ?? validGroups.FirstOrDefault(g => !g.ExcludedByFilter); // room balance if they fit into multiple groups if (bestGroup != null && roomBalance) { var currentGroupAttendance = bestGroup.Locations.Select(l => KioskLocationAttendance.Read(l.Location.Id).CurrentCount).Sum(); var lowestGroup = validGroups.Where(g => !g.ExcludedByFilter) .Select(g => new { Group = g, Attendance = g.Locations.Select(l => KioskLocationAttendance.Read(l.Location.Id).CurrentCount).Sum() }) .OrderBy(g => g.Attendance) .FirstOrDefault(); if (lowestGroup != null && lowestGroup.Attendance < (currentGroupAttendance - balanceOverride)) { bestGroup = lowestGroup.Group; } } } validLocations = bestGroup.Locations; } // check how many locations exist without getting the whole list int numValidLocations = validLocations.Take(2).Count(); if (numValidLocations > 0) { CheckInLocation bestLocation = null; IEnumerable <CheckInSchedule> validSchedules; if (numValidLocations == 1) { bestLocation = validLocations.FirstOrDefault(); validSchedules = bestLocation.Schedules; } else { var orderedLocations = validLocations.Where(l => !l.ExcludedByFilter && l.Schedules.Any(s => s.Schedule.IsCheckInActive)); if (roomBalance) { orderedLocations = orderedLocations.OrderBy(l => KioskLocationAttendance.Read(l.Location.Id).CurrentCount); } bestLocation = orderedLocations.FirstOrDefault(); validSchedules = bestLocation.Schedules; } // check how many schedules exist without getting the whole list int numValidSchedules = validSchedules.Take(2).Count(); if (numValidSchedules > 0) { var bestSchedule = validSchedules.FirstOrDefault(); bestSchedule.Selected = true; bestSchedule.PreSelected = true; if (bestLocation != null) { bestLocation.PreSelected = true; bestLocation.Selected = true; if (bestGroup != null) { bestGroup.PreSelected = true; bestGroup.Selected = true; bestGroupType = person.GroupTypes.FirstOrDefault(gt => gt.GroupType.Id == bestGroup.Group.GroupTypeId); if (bestGroupType != null) { bestGroupType.Selected = true; bestGroupType.PreSelected = true; person.PreSelected = true; } } } } } } } } } return(true); }
/// <summary> /// Binds the repeater. /// </summary> protected void BindRepeater() { if (this.CurrentPersonId == null) { return; } int communicationListGroupTypeId = GroupTypeCache.Get(Rock.SystemGuid.GroupType.GROUPTYPE_COMMUNICATIONLIST.AsGuid()).Id; int?communicationListGroupTypeDefaultRoleId = GroupTypeCache.Get(communicationListGroupTypeId).DefaultGroupRoleId; var rockContext = new RockContext(); var memberOfList = new GroupMemberService(rockContext).GetByPersonId(CurrentPersonId.Value).AsNoTracking().Select(a => a.GroupId).ToList(); // Get a list of syncs for the communication list groups where the default role is sync'd AND the current person is NOT a member of // This is used to filter out the list of communication lists. var commGroupSyncsForDefaultRole = new GroupSyncService(rockContext) .Queryable() .Where(a => a.Group.GroupTypeId == communicationListGroupTypeId) .Where(a => a.GroupTypeRoleId == communicationListGroupTypeDefaultRoleId) .Where(a => !memberOfList.Contains(a.GroupId)) .Select(a => a.GroupId) .ToList(); var communicationLists = new GroupService(rockContext) .Queryable() .Where(a => a.GroupTypeId == communicationListGroupTypeId && !commGroupSyncsForDefaultRole.Contains(a.Id)) .ToList(); var categoryGuids = this.GetAttributeValue("CommunicationListCategories").SplitDelimitedValues().AsGuidList(); var viewableCommunicationLists = new List <Group>(); foreach (var communicationList in communicationLists) { communicationList.LoadAttributes(rockContext); if (!categoryGuids.Any()) { // if no categories where specified, only show lists that the person has VIEW auth if (communicationList.IsAuthorized(Rock.Security.Authorization.VIEW, this.CurrentPerson)) { viewableCommunicationLists.Add(communicationList); } } else { Guid?categoryGuid = communicationList.GetAttributeValue("Category").AsGuidOrNull(); if (categoryGuid.HasValue && categoryGuids.Contains(categoryGuid.Value)) { viewableCommunicationLists.Add(communicationList); } } } viewableCommunicationLists = viewableCommunicationLists.OrderBy(a => { var name = a.GetAttributeValue("PublicName"); if (name.IsNullOrWhiteSpace()) { name = a.Name; } return(name); }).ToList(); var groupIds = viewableCommunicationLists.Select(a => a.Id).ToList(); var personId = this.CurrentPersonId.Value; showMediumPreference = this.GetAttributeValue("ShowMediumPreference").AsBoolean(); personCommunicationListsMember = new GroupMemberService(rockContext) .Queryable() .AsNoTracking() .Where(a => groupIds.Contains(a.GroupId) && a.PersonId == personId) .GroupBy(a => a.GroupId) .ToList() .ToDictionary(k => k.Key, v => v.FirstOrDefault()); rptCommunicationLists.DataSource = viewableCommunicationLists; rptCommunicationLists.DataBind(); nbNoCommunicationLists.Visible = !viewableCommunicationLists.Any(); pnlCommunicationPreferences.Visible = viewableCommunicationLists.Any(); }
/// <summary> /// Gets the subscription options for the current person. /// </summary> /// <returns>A collection of <see cref="Subscription"/> objects.</returns> protected virtual IEnumerable <Subscription> GetSubscriptions() { int communicationListGroupTypeId = GroupTypeCache.Get(Rock.SystemGuid.GroupType.GROUPTYPE_COMMUNICATIONLIST.AsGuid()).Id; int?communicationListGroupTypeDefaultRoleId = GroupTypeCache.Get(communicationListGroupTypeId).DefaultGroupRoleId; if (RequestContext.CurrentPerson == null) { return(new List <Subscription>()); } var rockContext = new RockContext(); // // Get all the lists this person is already a member of. // var memberOfList = new GroupMemberService(rockContext) .GetByPersonId(RequestContext.CurrentPerson.Id) .AsNoTracking() .Where(a => a.Group.GroupTypeId == communicationListGroupTypeId) .Select(a => a.GroupId) .ToList(); // // Get a list of syncs for the communication list groups // where the default role is sync'd AND the current person // is NOT a member of. // This is used to filter out the list of communication lists. // var commGroupSyncsForDefaultRole = new GroupSyncService(rockContext) .Queryable() .Where(a => a.Group.GroupTypeId == communicationListGroupTypeId) .Where(a => a.GroupTypeRoleId == communicationListGroupTypeDefaultRoleId) .Where(a => !memberOfList.Contains(a.GroupId)) .Select(a => a.GroupId) .ToList(); // // Get all the communication lists, excluding those from the above // query about synced groups. // var communicationLists = new GroupService(rockContext) .Queryable() .Where(a => a.GroupTypeId == communicationListGroupTypeId) .Where(a => !commGroupSyncsForDefaultRole.Contains(a.Id)) .ToList(); var categoryGuids = CommunicationListCategories; var viewableCommunicationLists = communicationLists .Where(a => { a.LoadAttributes(rockContext); if (!categoryGuids.Any()) { // // If no categories where specified, only show // lists that the person has VIEW auth to. // if (a.IsAuthorized(Rock.Security.Authorization.VIEW, RequestContext.CurrentPerson)) { return(true); } } else { // // If categories were specified, ensure that this // communication list has a category and is one of // the specified categories. // Guid?categoryGuid = a.GetAttributeValue("Category").AsGuidOrNull(); if (categoryGuid.HasValue && categoryGuids.Contains(categoryGuid.Value)) { return(true); } } return(false); }) .ToList(); var groupIds = viewableCommunicationLists.Select(a => a.Id).ToList(); var personId = RequestContext.CurrentPerson.Id; var communicationListsMember = new GroupMemberService(rockContext) .Queryable() .AsNoTracking() .Where(a => groupIds.Contains(a.GroupId) && a.PersonId == personId) .GroupBy(a => a.GroupId) .ToList() .ToDictionary(k => k.Key, v => v.FirstOrDefault()); return(viewableCommunicationLists .Select(a => { var publicName = a.GetAttributeValue("PublicName"); return new Subscription { DisplayName = publicName.IsNotNullOrWhiteSpace() ? publicName : a.Name, CommunicationList = a, Member = communicationListsMember.GetValueOrDefault(a.Id, null) }; }) .OrderBy(a => a.DisplayName) .ToList()); }