Exemple #1
0
        /// <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);
        }
Exemple #2
0
        /// <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;
            }
        }
Exemple #3
0
        /// <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));
            }
        }
Exemple #8
0
        /// <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();
            }
        }
Exemple #9
0
        /// <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;
            }
        }
Exemple #10
0
        /// <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();
        }
Exemple #12
0
        /// <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());
        }