Esempio n. 1
0
        /// <summary>
        /// Returns a IQueryable of T based on the dataViewId
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="rockContext">The rock context.</param>
        /// <param name="dataViewId">The data view identifier.</param>
        /// <returns></returns>
        /// <exception cref="System.Exception">Invalid DataView EntityType</exception>
        /// <exception cref="AggregateException"></exception>
        /// <exception cref="System.ArgumentException">Invalid DataViewId: {this.DataViewId}</exception>
        internal static IQueryable <T> QueryFromDataView <T>(RockContext rockContext, int dataViewId) where T : Rock.Data.IEntity
        {
            IQueryable <T> entityQuery;
            var            dataView = new DataViewService(rockContext).GetNoTracking(dataViewId);

            if (dataView != null)
            {
                var dataViewGetQueryArgs = new DataViewGetQueryArgs
                {
                    DbContext = rockContext
                };

                entityQuery = dataView.GetQuery(dataViewGetQueryArgs) as IQueryable <T>;
                if (entityQuery == null)
                {
                    throw new Exception("Invalid DataView EntityType");
                }
            }
            else
            {
                throw new ArgumentException($"Invalid DataViewId: {dataViewId} ");
            }

            return(entityQuery);
        }
Esempio n. 2
0
        public bool InDataView(int dataViewId, int entityId)
        {
            var rockContext = new RockContext();

            var dataView = new DataViewService(rockContext).Get(dataViewId);

            ValidateDataView(dataView);

            var dataViewGetQueryArgs = new DataViewGetQueryArgs
            {
                DbContext = rockContext
            };

            var qryGroupsInDataView = dataView.GetQuery(dataViewGetQueryArgs) as IQueryable <T>;

            qryGroupsInDataView = qryGroupsInDataView.Where(d => d.Id == entityId);

            return(qryGroupsInDataView.Any());
        }
Esempio n. 3
0
        /// <summary>
        /// Renders the specified writer.
        /// </summary>
        /// <param name="badge">The badge.</param>
        /// <param name="writer">The writer.</param>
        public override void Render(BadgeCache badge, System.Web.UI.HtmlTextWriter writer)
        {
            RockContext rockContext           = new RockContext();
            var         dataViewAttributeGuid = GetAttributeValue(badge, "DataView").AsGuid();
            var         dataViewService       = new DataViewService(rockContext);

            if (dataViewAttributeGuid != Guid.Empty)
            {
                var dataView = dataViewService.Get(dataViewAttributeGuid);
                if (dataView != null)
                {
                    Stopwatch stopwatch            = Stopwatch.StartNew();
                    var       dataViewGetQueryArgs = new DataViewGetQueryArgs
                    {
                        DatabaseTimeoutSeconds = 30
                    };

                    var qry = dataView.GetQuery(dataViewGetQueryArgs);

                    var isEntityFound = false;
                    if (qry != null)
                    {
                        isEntityFound = qry.Where(e => e.Id == Entity.Id).Any();
                        stopwatch.Stop();
                        DataViewService.AddRunDataViewTransaction(dataView.Id,
                                                                  Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds));
                    }

                    if (isEntityFound)
                    {
                        Dictionary <string, object> mergeValues = new Dictionary <string, object>();
                        mergeValues.Add("Person", Person);
                        mergeValues.Add("Entity", Entity);
                        writer.Write(GetAttributeValue(badge, "BadgeContent").ResolveMergeFields(mergeValues));
                    }
                }
            }
        }
        /// <summary>
        /// Clears the existing data from class var attendances and repopulates it with data for the selected group and filter criteria.
        /// Data is organized by each person in the group.
        /// </summary>
        private List <Attendance> GetAttendanceData()
        {
            this.SetBlockUserPreference(UserPreferenceKey.SelectedDateRange, sdrpDateRange.DelimitedValues);
            this.SetBlockUserPreference(UserPreferenceKey.SelectedViewBy, hfTabs.Value);
            this.SetBlockUserPreference(UserPreferenceKey.SelectedGroupId, gpGroups.GroupId.ToString());
            this.SetBlockUserPreference(UserPreferenceKey.SelectedLocationIds, cblLocations.SelectedValues.AsDelimited(","));
            this.SetBlockUserPreference(UserPreferenceKey.SelectedScheduleIds, cblSchedules.SelectedValues.AsDelimited(","));
            this.SetBlockUserPreference(UserPreferenceKey.SelectedDataViewId, dvDataViews.SelectedValue);
            this.SetBlockUserPreference(UserPreferenceKey.SelectedPersonId, ppPerson.SelectedValue.ToString());

            // Create URL for selected settings
            var pageReference = CurrentPageReference;

            foreach (var setting in GetBlockUserPreferences())
            {
                pageReference.Parameters.AddOrReplace(setting.Key, setting.Value);
            }

            Uri uri = new Uri(Request.UrlProxySafe().ToString());

            btnCopyToClipboard.Attributes["data-clipboard-text"] = uri.GetLeftPart(UriPartial.Authority) + pageReference.BuildUrl();
            btnCopyToClipboard.Disabled = false;

            // Source data for all tables and graphs
            using (var rockContext = new RockContext())
            {
                var attendanceService = new AttendanceService(rockContext);
                var groupAttendances  = attendanceService
                                        .Queryable()
                                        .Include(a => a.PersonAlias)
                                        .AsNoTracking()
                                        .Where(a => a.RequestedToAttend == true);

                switch (hfTabs.Value)
                {
                case "group":
                    groupAttendances = groupAttendances.Where(a => a.Occurrence.GroupId == gpGroups.GroupId);

                    // add selected locations to the query
                    if (cblLocations.SelectedValues.Any())
                    {
                        groupAttendances = groupAttendances.Where(a => cblLocations.SelectedValuesAsInt.Contains(a.Occurrence.LocationId ?? -1));
                    }

                    // add selected schedules to the query
                    if (cblSchedules.SelectedValues.Any())
                    {
                        groupAttendances = groupAttendances.Where(a => cblSchedules.SelectedValuesAsInt.Contains(a.Occurrence.ScheduleId ?? -1));
                    }

                    break;

                case "person":
                    groupAttendances = groupAttendances.Where(a => a.PersonAlias.PersonId == ppPerson.PersonId);
                    break;

                case "dataview":
                    var dataView             = new DataViewService(rockContext).Get(dvDataViews.SelectedValueAsInt().Value);
                    var dataViewGetQueryArgs = new DataViewGetQueryArgs {
                        DbContext = rockContext
                    };
                    var personsFromDv  = dataView.GetQuery(dataViewGetQueryArgs) as IQueryable <Person>;
                    var personAliasIds = personsFromDv.Select(d => d.Aliases.Where(a => a.AliasPersonId == d.Id).Select(a => a.Id).FirstOrDefault()).ToList();

                    groupAttendances = groupAttendances.Where(a => personAliasIds.Contains(a.PersonAliasId.Value));

                    break;

                default:
                    break;
                }

                // parse the date range and add to query
                if (sdrpDateRange.DelimitedValues.IsNotNullOrWhiteSpace())
                {
                    var dateRange = SlidingDateRangePicker.CalculateDateRangeFromDelimitedValues(sdrpDateRange.DelimitedValues);
                    if (dateRange.Start.HasValue)
                    {
                        groupAttendances = groupAttendances.Where(a => DbFunctions.TruncateTime(a.StartDateTime) >= dateRange.Start.Value);
                    }

                    if (dateRange.End.HasValue)
                    {
                        groupAttendances = groupAttendances.Where(a => DbFunctions.TruncateTime(a.StartDateTime) <= dateRange.End.Value);
                    }
                }

                return(groupAttendances.ToList());
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Gets the filter person Ids based on dataview and optout group
        /// </summary>
        /// <param name="campaignConfiguration">The campaign configuration.</param>
        /// <param name="rockContext">The rock context.</param>
        /// <returns></returns>
        private static List <int> GetFilteredPersonIds(CampaignItem campaignConfiguration, RockContext rockContext)
        {
            var dataView               = new DataViewService(rockContext).Get(campaignConfiguration.DataViewGuid);
            var personService          = new PersonService(rockContext);
            int recordStatusInactiveId = DefinedValueCache.Get(Rock.SystemGuid.DefinedValue.PERSON_RECORD_STATUS_INACTIVE.AsGuid()).Id;
            var filteredPersonIds      = new List <int>();

            var dataViewGetQueryArgs = new DataViewGetQueryArgs();
            var personQuery          = dataView.GetQuery(dataViewGetQueryArgs).OfType <Rock.Model.Person>().Where(a => a.RecordStatusValueId != recordStatusInactiveId);

            if (campaignConfiguration.FamilyLimits == FamilyLimits.HeadOfHouse)
            {
                var familyMembersQuery = personQuery
                                         .Where(a => a.PrimaryFamily != null)
                                         .SelectMany(a => a.PrimaryFamily.Members)
                                         .Distinct();

                //// Get all family group Id and all it's family member in dictionary.
                //// We will all the family members to both figure out if might be opted out
                //// and to figure out the head of household
                var familyWithMembers = familyMembersQuery.AsNoTracking()
                                        .Select(a => new
                {
                    a.GroupId,
                    a.PersonId,
                    PersonIsActive   = a.Person.RecordStatusValueId != recordStatusInactiveId,
                    PersonIsDeceased = a.Person.IsDeceased,
                    GroupRoleOrder   = a.GroupRole.Order,
                    PersonGender     = a.Person.Gender
                })
                                        .ToList()
                                        .GroupBy(a => a.GroupId)
                                        .ToDictionary(k => k.Key, v => v);

                if (campaignConfiguration.OptOutGroupGuid.HasValue)
                {
                    var optOutGroup = new GroupService(rockContext).Get(campaignConfiguration.OptOutGroupGuid.Value);
                    if (optOutGroup != null)
                    {
                        var personIds = optOutGroup.ActiveMembers().Select(a => a.PersonId).ToList();

                        // exclude families in which any member is part of optOut Group.
                        familyWithMembers = familyWithMembers.Where(a => !a.Value.Any(b => personIds.Contains(b.PersonId))).ToDictionary(a => a.Key, b => b.Value);
                    }
                }

                foreach (var familyId in familyWithMembers.Keys)
                {
                    /* 2020-05-07 MDP
                     * It is possible that a person is the Head Of Household in more than one family. For example:
                     * -- Alex is in Ted Decker family. Ted Decker is Head of Household
                     * -- Ted Decker is in Grandpa Decker family, and also the head of household for that one too
                     *
                     * We'll deal with that by putting a Distinct on the filteredPersonIds
                     */

                    // Get all the head of house personIds of leftout family.
                    var headOfHouse = familyWithMembers[familyId]
                                      .Where(m => !m.PersonIsDeceased && m.PersonIsActive)
                                      .OrderBy(m => m.GroupRoleOrder)
                                      .ThenBy(m => m.PersonGender)
                                      .Select(a => a.PersonId)
                                      .FirstOrDefault();

                    if (headOfHouse != default(int))
                    {
                        filteredPersonIds.Add(headOfHouse);
                    }
                }
            }
            else
            {
                var personIdList = personQuery.Select(a => a.Id).ToList();
                if (campaignConfiguration.OptOutGroupGuid.HasValue)
                {
                    var optOutGroup = new GroupService(rockContext).Get(campaignConfiguration.OptOutGroupGuid.Value);
                    if (optOutGroup != null)
                    {
                        var personIds = optOutGroup.ActiveMembers().Select(a => a.PersonId).ToList();
                        personIdList = personIdList.Where(a => !personIds.Contains(a)).ToList();
                    }
                }

                filteredPersonIds = personIdList;
            }

            // just in case the same person is in multiple times (for example, head of household in multiple families), get just the distinct person ids
            filteredPersonIds = filteredPersonIds.Distinct().ToList();

            return(filteredPersonIds);
        }
Esempio n. 6
0
        /// <summary>
        /// Executes the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        public virtual void Execute(IJobExecutionContext context)
        {
            JobDataMap dataMap           = context.JobDetail.JobDataMap;
            var        emailTemplateGuid = dataMap.GetString("SystemEmail").AsGuidOrNull();
            var        dataViewGuid      = dataMap.GetString("DataView").AsGuidOrNull();

            if (dataViewGuid == null || emailTemplateGuid == null)
            {
                return;
            }

            var rockContext = new RockContext();
            var dataView    = new DataViewService(rockContext).Get(( Guid )dataViewGuid);

            List <IEntity> resultSet;
            Exception      dataViewException = null;

            try
            {
                var dataViewGetQueryArgs = new DataViewGetQueryArgs
                {
                    DatabaseTimeoutSeconds = dataMap.GetString("DatabaseTimeout").AsIntegerOrNull() ?? 180
                };

                var qry = dataView.GetQuery(dataViewGetQueryArgs);
                resultSet = qry.AsNoTracking().ToList();
            }
            catch (Exception exception)
            {
                dataViewException = exception;
                var sqlTimeoutException = ReportingHelper.FindSqlTimeoutException(exception);

                if (sqlTimeoutException != null)
                {
                    var exceptionMessage = $"The dataview did not complete in a timely manner. You can try again or adjust the timeout setting of this job.";
                    dataViewException = new RockDataViewFilterExpressionException(dataView.DataViewFilter, exceptionMessage, sqlTimeoutException);
                }

                HttpContext context2 = HttpContext.Current;
                ExceptionLogService.LogException(dataViewException, context2);
                context.Result = dataViewException.Message;
                throw dataViewException;
            }

            var recipients = new List <RockEmailMessageRecipient>();

            if (resultSet.Any())
            {
                foreach (Person person in resultSet)
                {
                    if (!person.IsEmailActive || person.Email.IsNullOrWhiteSpace() || person.EmailPreference == EmailPreference.DoNotEmail)
                    {
                        continue;
                    }

                    var mergeFields = Lava.LavaHelper.GetCommonMergeFields(null);
                    mergeFields.Add("Person", person);
                    recipients.Add(new RockEmailMessageRecipient(person, mergeFields));
                }
            }

            var emailMessage = new RockEmailMessage(emailTemplateGuid.Value);

            emailMessage.SetRecipients(recipients);

            var emailSendErrors = new List <string>();

            emailMessage.Send(out emailSendErrors);

            context.Result = string.Format("{0} emails sent", recipients.Count());

            if (emailSendErrors.Any())
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine();
                sb.Append(string.Format("{0} Errors: ", emailSendErrors.Count()));
                emailSendErrors.ForEach(e => { sb.AppendLine(); sb.Append(e); });
                string errorMessage = sb.ToString();
                context.Result += errorMessage;
                var         exception = new Exception(errorMessage);
                HttpContext context2  = HttpContext.Current;
                ExceptionLogService.LogException(exception, context2);
                throw exception;
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Shows the report.
        /// </summary>
        private void ShowMap()
        {
            string mapStylingFormat = @"
                        <style>
                            #map_wrapper {{
                                height: {0}px;
                            }}

                            #map_canvas {{
                                width: 100%;
                                height: 100%;
                                border-radius: var(--border-radius-base);
                            }}
                        </style>";

            lMapStyling.Text = string.Format(mapStylingFormat, GetAttributeValue("MapHeight"));

            DefinedValueCache dvcMapStyle = DefinedValueCache.Get(GetAttributeValue("MapStyle").AsGuid());

            // add styling to map
            if (dvcMapStyle != null)
            {
                this.StyleCode = dvcMapStyle.GetAttributeValue("DynamicMapStyle");
                if (this.StyleCode.IsNullOrWhiteSpace())
                {
                    this.StyleCode = "[]";
                }
            }
            else
            {
                this.StyleCode = "[]";
            }

            var    polygonColorList = GetAttributeValue("PolygonColors").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
            string polygonColors    = polygonColorList.AsDelimited(",");

            string latitude    = "39.8282";
            string longitude   = "-98.5795";
            string zoom        = "4";
            var    orgLocation = GlobalAttributesCache.Get().OrganizationLocation;

            if (orgLocation != null && orgLocation.GeoPoint != null)
            {
                latitude  = orgLocation.GeoPoint.Latitude.Value.ToString(System.Globalization.CultureInfo.GetCultureInfo("en-US"));
                longitude = orgLocation.GeoPoint.Longitude.Value.ToString(System.Globalization.CultureInfo.GetCultureInfo("en-US"));
                zoom      = "12";
            }

            var rockContext     = new RockContext();
            var campuses        = CampusCache.All();
            var locationService = new LocationService(rockContext);

            CampusMarkersData = string.Empty;
            if (cbShowCampusLocations.Checked)
            {
                foreach (var campus in campuses)
                {
                    if (campus.LocationId.HasValue)
                    {
                        var location = locationService.Get(campus.LocationId.Value);
                        if (location != null && location.GeoPoint != null)
                        {
                            CampusMarkersData += string.Format("{{ location: new google.maps.LatLng({0},{1}), campusName:'{2}' }},", location.GeoPoint.Latitude, location.GeoPoint.Longitude, HttpUtility.JavaScriptStringEncode(campus.Name));
                        }
                    }
                }

                CampusMarkersData.TrimEnd(new char[] { ',' });
            }

            // show geofences if a group was specified
            this.GroupId = this.PageParameter("GroupId").AsIntegerOrNull();
            if (!this.GroupId.HasValue && gpGroupToMap.Visible)
            {
                // if a page parameter wasn't specified, use the selected group from the filter
                this.GroupId = gpGroupToMap.SelectedValue.AsIntegerOrNull();
            }

            var groupMemberService      = new GroupMemberService(rockContext);
            var groupLocationTypeHome   = DefinedValueCache.Get(Rock.SystemGuid.DefinedValue.GROUP_LOCATION_TYPE_HOME.AsGuid());
            int groupLocationTypeHomeId = groupLocationTypeHome != null ? groupLocationTypeHome.Id : 0;
            var groupTypeFamily         = GroupTypeCache.GetFamilyGroupType();
            int groupTypeFamilyId       = groupTypeFamily != null ? groupTypeFamily.Id : 0;

            DataView         dataView     = GetDataView();
            IQueryable <int> qryPersonIds = null;

            if (dataView != null)
            {
                var dataViewGetQueryArgs = new DataViewGetQueryArgs {
                    DbContext = rockContext
                };
                qryPersonIds = dataView.GetQuery(dataViewGetQueryArgs).OfType <Person>().Select(a => a.Id);
            }

            if (qryPersonIds == null)
            {
                // if no dataview was specified, show nothing
                qryPersonIds = new PersonService(rockContext).Queryable().Where(a => false).Select(a => a.Id);
            }

            var qryGroupMembers = groupMemberService.Queryable();

            var campusIds = cpCampuses.SelectedCampusIds;

            if (campusIds.Any())
            {
                qryGroupMembers = qryGroupMembers.Where(a => a.Group.CampusId.HasValue && campusIds.Contains(a.Group.CampusId.Value));
            }

            var qryLocationGroupMembers = qryGroupMembers
                                          .Where(a => a.Group.GroupTypeId == groupTypeFamilyId)
                                          .Where(a => a.Group.IsActive && !a.Group.IsArchived)
                                          .Select(a => new
            {
                GroupGeoPoint = a.Group.GroupLocations.Where(gl => gl.IsMappedLocation && gl.GroupLocationTypeValueId == groupLocationTypeHomeId && gl.Location.IsActive && gl.Location.GeoPoint != null).Select(x => x.Location.GeoPoint).FirstOrDefault(),
                a.Group.CampusId,
                a.PersonId
            })
                                          .Where(a => (a.GroupGeoPoint != null) && qryPersonIds.Contains(a.PersonId))
                                          .GroupBy(a => new { a.GroupGeoPoint.Latitude, a.GroupGeoPoint.Longitude })
                                          .Select(s => new
            {
                s.Key.Longitude,
                s.Key.Latitude,
                MemberCount = s.Count()
            });

            var locationList = qryLocationGroupMembers.ToList()
                               .Select(a => new LatLongWeighted(a.Latitude.Value, a.Longitude.Value, a.MemberCount))
                               .ToList();

            List <LatLongWeighted> points = locationList;

            // cluster points that are close together
            double?milesPerGrouping = this.GetAttributeValue("PointGrouping").AsDoubleOrNull();

            if (!milesPerGrouping.HasValue)
            {
                // default to a 1/10th of a mile
                milesPerGrouping = 0.10;
            }

            if (milesPerGrouping.HasValue && milesPerGrouping > 0)
            {
                var metersPerLatitudePHX  = 110886.79;
                var metersPerLongitudePHX = 94493.11;

                double metersPerMile = 1609.34;

                var squareLengthHeightMeters = metersPerMile * milesPerGrouping.Value;

                var longitudeRoundFactor = metersPerLongitudePHX / squareLengthHeightMeters;
                var latitudeRoundFactor  = metersPerLatitudePHX / squareLengthHeightMeters;

                // average the Lat/Lng, but make sure to round to 8 decimal points (otherwise Google Maps will silently not show the points due to too high of decimal precision)
                points = points.GroupBy(a => new
                {
                    rLat  = Math.Round(a.Lat * latitudeRoundFactor),
                    rLong = Math.Round(a.Lat * longitudeRoundFactor),
                }).Select(a => new LatLongWeighted(Math.Round(a.Average(x => x.Lat), 8), Math.Round(a.Average(x => x.Long), 8), a.Sum(x => x.Weight))).ToList();
            }

            this.HeatMapData = points.Select(a => a.Weight > 1
                ? string.Format("{{ location: new google.maps.LatLng({0}, {1}), weight: {2} }}", a.Lat, a.Long, a.Weight)
                : string.Format("new google.maps.LatLng({0}, {1})", a.Lat, a.Long)).ToList().AsDelimited(",\n");

            hfPolygonColors.Value   = polygonColors;
            hfCenterLatitude.Value  = latitude.ToString();
            hfCenterLongitude.Value = longitude.ToString();
            hfZoom.Value            = zoom.ToString();
        }
Esempio n. 8
0
        /// <summary>
        /// Processes the step type. Add steps for everyone in the dataview
        /// </summary>
        /// <param name="jobContext">The job context.</param>
        /// <param name="stepTypeView">The step type view.</param>
        /// <param name="minDaysBetweenSteps">The minimum days between steps.</param>
        /// <param name="addedResults">The added results.</param>
        /// <param name="updatedResults">The updated results.</param>
        /// <param name="errorMessages">The error message.</param>
        private void ProcessStepType(
            IJobExecutionContext jobContext,
            StepTypeView stepTypeView,
            int minDaysBetweenSteps,
            ConcurrentBag <int> addedResults,
            ConcurrentBag <int> updatedResults,
            out List <string> errorMessages)
        {
            errorMessages = new List <string>();
            var rockContextGetList = new RockContext();

            rockContextGetList.Database.CommandTimeout = SqlCommandTimeoutSeconds;

            // Steps are created with a status of "complete", so if we need to know the status id
            var stepStatusId = stepTypeView.CompletedStepStatusIds.FirstOrDefault();

            if (stepStatusId == default)
            {
                errorMessages.Add($"The Step Type with id {stepTypeView.StepTypeId} does not have a valid Complete Status to use");
                return;
            }

            // Get the dataview configured for the step type
            var dataViewService = new DataViewService(rockContextGetList);
            var dataview        = dataViewService.Get(stepTypeView.AutoCompleteDataViewId);

            if (dataview == null)
            {
                errorMessages.Add($"The dataview {stepTypeView.AutoCompleteDataViewId} for step type {stepTypeView.StepTypeId} did not resolve");
                return;
            }

            // We can use the dataview to get the person alias id query
            var dataViewGetQueryArgs = new DataViewGetQueryArgs
            {
                DbContext = rockContextGetList,
                DatabaseTimeoutSeconds = SqlCommandTimeoutSeconds
            };

            IQueryable <IEntity> dataviewQuery;

            try
            {
                dataviewQuery = dataview.GetQuery(dataViewGetQueryArgs);
            }
            catch (Exception ex)
            {
                errorMessages.Add(ex.Message);
                ExceptionLogService.LogException(ex);
                return;
            }

            if (dataviewQuery == null)
            {
                errorMessages.Add($"Generating a query for dataview {stepTypeView.AutoCompleteDataViewId} for step type {stepTypeView.StepTypeId} was not successful");
                return;
            }

            // This query contains person ids in the dataview
            var personIdQuery = dataviewQuery.AsNoTracking().Select(e => e.Id);

            // Get the query for people that cannot get a new step
            var personIdsThatCannotGetStepQuery = GetPersonIdsThatCannotGetStepQuery(rockContextGetList, stepTypeView, minDaysBetweenSteps);

            // Subtract the people that cannot get a new step
            personIdQuery = personIdQuery.Except(personIdsThatCannotGetStepQuery);

            // If there are prerequisites, then subtract the people that cannot get the step because of unmet prerequisites
            if (stepTypeView.PrerequisiteStepTypeIds.Any())
            {
                var personIdsThatHaveMetPrerequisitesQuery = GetPersonIdsThatHaveMetPrerequisitesQuery(rockContextGetList, stepTypeView);
                personIdQuery = personIdQuery.Intersect(personIdsThatHaveMetPrerequisitesQuery);
            }

            // Convert to person aliases ids
            var personAliasService = new PersonAliasService(rockContextGetList);
            var personInfoList     = personAliasService.GetPrimaryAliasQuery()
                                     .Where(a => personIdQuery.Contains(a.PersonId))
                                     .Select(a => new
            {
                PersonId       = a.PersonId,
                PrimaryAliasId = a.Id
            })
                                     .ToList();

            // Add or update steps for each of the remaining aliases that have met all the conditions
            var stepServiceGetList = new StepService(rockContextGetList);
            var now          = RockDateTime.Now;
            var addedCount   = 0;
            var updatedCount = 0;

            // Query for existing incomplete steps for the people
            var existingIncompleteStepIdsByPersonId = stepServiceGetList.Queryable()
                                                      .Where(s =>
                                                             s.StepTypeId == stepTypeView.StepTypeId &&
                                                             personIdQuery.Contains(s.PersonAlias.PersonId) &&
                                                             !s.CompletedDateTime.HasValue)
                                                      .Select(a => new
            {
                a.PersonAlias.PersonId,
                StepId = a.Id
            })
                                                      .ToList()
                                                      .GroupBy(a => a.PersonId)
                                                      .ToDictionary(
                k => k.Key,
                // just in case the Person is has more than one incomplete step for this step type, just use the latest one
                // it should clean it self up on subsequent runs since the other steps for this person wouldn't have been marked complete yet
                v => v.Max(s => s.StepId)
                );


            long totalCount    = personInfoList.Count;
            long progressCount = 0;

            foreach (var personIdInfo in personInfoList)
            {
                var personId             = personIdInfo.PersonId;
                var personPrimaryAliasId = personIdInfo.PrimaryAliasId;

                var existingStepId = existingIncompleteStepIdsByPersonId.GetValueOrNull(personId);

                using (var rockContextLoop = new RockContext())
                {
                    var  stepServiceLoop = new StepService(rockContextLoop);
                    Step step;
                    if (existingStepId.HasValue)
                    {
                        step = stepServiceLoop.Get(existingStepId.Value);
                    }
                    else
                    {
                        step = new Step
                        {
                            StepTypeId    = stepTypeView.StepTypeId,
                            Caption       = stepTypeView.Name,
                            StartDateTime = now,
                            PersonAliasId = personPrimaryAliasId
                        };
                    }

                    step.CompletedDateTime = now;
                    step.StepStatusId      = stepStatusId;

                    if (!existingStepId.HasValue)
                    {
                        stepServiceLoop.AddWithoutValidation(step);
                        addedCount++;
                    }
                    else
                    {
                        updatedCount++;
                    }

                    rockContextLoop.SaveChanges();
                }

                progressCount++;

                // Update the progress every 5 seconds
                if ((RockDateTime.Now - _lastProgressUpdate).TotalSeconds >= 5)
                {
                    try
                    {
                        jobContext.UpdateLastStatusMessage($"Processing {stepTypeView.Name } steps : {progressCount}/{totalCount}");
                    }
                    catch (Exception ex)
                    {
                        // ignore, but write to debug output
                        System.Diagnostics.Debug.WriteLine($"Error updating LastStatusMessage for ProcessStepType loop: {ex}");
                    }
                    finally
                    {
                        _lastProgressUpdate = RockDateTime.Now;
                    }
                }
            }

            addedResults.Add(addedCount);
            updatedResults.Add(updatedCount);
        }
        /// <summary>
        /// Binds the grid.
        /// </summary>
        private void BindGrid()
        {
            var dataViewId = hfDataViewId.Value.AsIntegerOrNull();

            pnlResultsGrid.Visible = false;

            pnlView.Visible = false;
            if (!dataViewId.HasValue)
            {
                return;
            }

            var dataView = new DataViewService(new RockContext()).Get(dataViewId.Value);

            if (dataView == null)
            {
                return;
            }

            if (!dataView.EntityTypeId.HasValue)
            {
                return;
            }

            var dataViewEntityType = EntityTypeCache.Get(dataView.EntityTypeId.Value);

            if (dataViewEntityType == null || dataViewEntityType.AssemblyName == null)
            {
                return;
            }

            Type dataViewEntityTypeType = dataViewEntityType.GetEntityType();

            if (dataViewEntityTypeType == null)
            {
                return;
            }

            pnlView.Visible = true;

            gDataViewResults.DataSource = null;

            // Only respect the ShowResults option if fetchRowCount is null
            var showResults = GetBlockUserPreference(UserPreferenceKey.ShowResults).AsBooleanOrNull() ?? true;

            if (showResults)
            {
                btnToggleResults.Text    = "Hide Results <i class='fa fa-chevron-up'></i>";
                btnToggleResults.ToolTip = "Hide Results";
                btnToggleResults.RemoveCssClass("btn-primary");
                btnToggleResults.AddCssClass("btn-default");
            }
            else
            {
                btnToggleResults.Text = "Show Results <i class='fa fa-chevron-down'></i>";
                btnToggleResults.RemoveCssClass("btn-default");
                btnToggleResults.AddCssClass("btn-primary");
                btnToggleResults.ToolTip = "Show Results";
            }

            if (!showResults)
            {
                return;
            }

            if (!dataView.IsAuthorized(Authorization.VIEW, CurrentPerson))
            {
                return;
            }

            gDataViewResults.EntityTypeId = dataView.EntityTypeId;
            bool isPersonDataSet = dataView.EntityTypeId == EntityTypeCache.GetId <Rock.Model.Person>();

            if (isPersonDataSet)
            {
                gDataViewResults.PersonIdField = "Id";
                gDataViewResults.DataKeyNames  = new string[] { "Id" };
            }
            else
            {
                gDataViewResults.PersonIdField = null;
            }

            var entityTypeCache = EntityTypeCache.Get(dataView.EntityTypeId.Value);

            if (entityTypeCache != null)
            {
                gDataViewResults.RowItemText = entityTypeCache.FriendlyName;
            }

            pnlResultsGrid.Visible = true;

            var enableCountingDataViewStatistics = this.GetAttributeValue(AttributeKey.EnableCountingDataViewStatistics).AsBooleanOrNull() ?? true;

            try
            {
                gDataViewResults.CreatePreviewColumns(dataViewEntityTypeType);
                var dbContext            = dataView.GetDbContext();
                var dataViewGetQueryArgs = new DataViewGetQueryArgs
                {
                    SortProperty            = gDataViewResults.SortProperty,
                    DbContext               = dbContext,
                    DatabaseTimeoutSeconds  = GetAttributeValue(AttributeKey.DatabaseTimeoutSeconds).AsIntegerOrNull() ?? 180,
                    DataViewFilterOverrides = new DataViewFilterOverrides
                    {
                        ShouldUpdateStatics = enableCountingDataViewStatistics
                    }
                };

                var qry = dataView.GetQuery(dataViewGetQueryArgs);

                gDataViewResults.SetLinqDataSource(qry.AsNoTracking());
                gDataViewResults.DataBind();
            }
            catch (Exception ex)
            {
                this.LogException(ex);
                var sqlTimeoutException = ReportingHelper.FindSqlTimeoutException(ex);
                var errorBox            = nbGridError;

                if (sqlTimeoutException != null)
                {
                    errorBox.NotificationBoxType = NotificationBoxType.Warning;
                    errorBox.Text = "This data view did not complete in a timely manner. You can try again or adjust the timeout setting of this block.";
                    return;
                }
                else
                {
                    if (ex is RockDataViewFilterExpressionException)
                    {
                        RockDataViewFilterExpressionException rockDataViewFilterExpressionException = ex as RockDataViewFilterExpressionException;
                        errorBox.Text = rockDataViewFilterExpressionException.GetFriendlyMessage(dataView);
                    }
                    else
                    {
                        errorBox.Text = "There was a problem with one of the filters for this data view.";
                    }

                    errorBox.NotificationBoxType = NotificationBoxType.Danger;

                    errorBox.Details = ex.Message;
                    errorBox.Visible = true;
                    return;
                }
            }

            gDataViewResults.RowItemText = dataViewEntityType.FriendlyName;

            if (gDataViewResults.DataSource != null)
            {
                gDataViewResults.ExportFilename = dataView.Name;
            }
        }
        /// <summary>
        /// Processes the connection type.
        /// </summary>
        /// <param name="connectionTypeView">The connection type view.</param>
        /// <param name="updatedResults">The updated results.</param>
        /// <param name="errorMessages">The error message.</param>
        private void ProcessConnectionType(
            ConnectionTypeView connectionTypeView,
            ConcurrentBag <int> updatedResults,
            out List <string> errorMessages)
        {
            errorMessages = new List <string>();
            var groupViews = new List <GroupView>();

            foreach (var connectionStatus in connectionTypeView.ConnectionStatuses)
            {
                foreach (var connectionStatusAutomation in connectionStatus.ConnectionStatusAutomations)
                {
                    var rockContext = new RockContext();
                    var connectionRequestService = new ConnectionRequestService(rockContext);
                    var connectionRequestQry     = connectionRequestService.Queryable().Include(a => a.AssignedGroup.Members).Where(a => a.ConnectionStatusId == connectionStatus.Id);
                    if (connectionStatusAutomation.DataViewId.HasValue)
                    {
                        // Get the dataview configured for the connection request
                        var dataViewService = new DataViewService(rockContext);
                        var dataview        = dataViewService.Get(connectionStatusAutomation.DataViewId.Value);

                        if (dataview == null)
                        {
                            errorMessages.Add($"The dataview {connectionStatusAutomation.DataViewId} for Connection Type {connectionTypeView.ConnectionTypeId} did not resolve");
                            continue;
                        }

                        // Now we'll filter our connection request query to only include the ones that are in the configured data view.
                        var dataViewGetQueryArgs = new DataViewGetQueryArgs
                        {
                            DbContext = rockContext
                        };

                        IQueryable <ConnectionRequest> dataviewQuery;
                        try
                        {
                            dataviewQuery = dataview.GetQuery(dataViewGetQueryArgs) as IQueryable <ConnectionRequest>;
                        }
                        catch (Exception ex)
                        {
                            errorMessages.Add(ex.Message);
                            ExceptionLogService.LogException(ex);
                            continue;
                        }

                        if (dataviewQuery == null)
                        {
                            errorMessages.Add($"Generating a query for dataview {connectionStatusAutomation.DataViewId} for Status {connectionStatus.Id} in Connection Type {connectionTypeView.ConnectionTypeId} was not successful");
                            continue;
                        }

                        connectionRequestQry = connectionRequestQry.Where(a => dataviewQuery.Any(b => b.Id == a.Id));
                    }

                    var eligibleConnectionRequests = new List <ConnectionRequest>();
                    if (connectionStatusAutomation.GroupRequirementsFilter != GroupRequirementsFilter.Ignore)
                    {
                        var connectionRequests = connectionRequestQry.ToList();
                        foreach (var connectionRequest in connectionRequests)
                        {
                            // Group Requirement can't be met when either placement group or placement group role id is missing
                            if (connectionRequest.AssignedGroupId.HasValue && connectionRequest.AssignedGroupMemberRoleId.HasValue)
                            {
                                var groupView = GetGroupView(connectionRequest, groupViews, rockContext);
                                if (groupView != null && groupView.HasGroupRequirement)
                                {
                                    var isRequirementMet = IsGroupRequirementMet(connectionRequest, groupView, rockContext);

                                    // connection request based on if group requirement is met or not is added to list for status update
                                    if ((connectionStatusAutomation.GroupRequirementsFilter == GroupRequirementsFilter.DoesNotMeet && !isRequirementMet) ||
                                        (connectionStatusAutomation.GroupRequirementsFilter == GroupRequirementsFilter.MustMeet && isRequirementMet))
                                    {
                                        eligibleConnectionRequests.Add(connectionRequest);
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        eligibleConnectionRequests = connectionRequestQry.ToList();
                    }

                    var updatedCount = 0;
                    foreach (var connectionRequest in eligibleConnectionRequests)
                    {
                        connectionRequest.ConnectionStatusId = connectionStatusAutomation.DestinationStatusId;
                        updatedCount++;
                    }

                    rockContext.SaveChanges();
                    updatedResults.Add(updatedCount);
                }
            }
        }
        /// <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 dataViewAttributeKey  = string.Empty;
            var dataViewAttributeGuid = GetAttributeValue(action, "DataViewGroupAttribute").AsGuid();

            if (dataViewAttributeGuid != Guid.Empty)
            {
                dataViewAttributeKey = AttributeCache.Get(dataViewAttributeGuid, rockContext).Key;
            }

            var dataViewService = new DataViewService(rockContext);

            var family = checkInState.CheckIn.CurrentFamily;

            if (family != null)
            {
                var remove = GetAttributeValue(action, "Remove").AsBoolean();

                foreach (var person in family.People)
                {
                    foreach (var groupType in person.GroupTypes.ToList())
                    {
                        foreach (var group in groupType.Groups.ToList())
                        {
                            if (group.ExcludedByFilter == true)
                            {
                                continue;
                            }

                            var dataviewGuids = group.Group.GetAttributeValue(dataViewAttributeKey);
                            if (string.IsNullOrWhiteSpace(dataviewGuids))
                            {
                                continue;
                            }

                            foreach (var dataviewGuid in dataviewGuids.SplitDelimitedValues())
                            {
                                DataView dataview = dataViewService.Get(dataviewGuid.AsGuid());
                                if (dataview == null)
                                {
                                    continue;
                                }

                                if (dataview.PersistedScheduleIntervalMinutes.HasValue && dataview.PersistedLastRefreshDateTime.HasValue)
                                {
                                    //Get record from persisted.
                                    var persistedValuesQuery = rockContext.DataViewPersistedValues.Where(a => a.DataViewId == dataview.Id);
                                    if (!persistedValuesQuery.Any(v => v.EntityId == person.Person.Id))
                                    {
                                        if (remove)
                                        {
                                            groupType.Groups.Remove(group);
                                        }
                                        else
                                        {
                                            group.ExcludedByFilter = true;
                                        }
                                        break;
                                    }
                                }
                                else
                                {
                                    //Qry dataview
                                    var dataViewGetQueryArgs = new DataViewGetQueryArgs
                                    {
                                        DatabaseTimeoutSeconds = 30
                                    };

                                    var approvedPeopleQry = dataview.GetQuery(dataViewGetQueryArgs);

                                    if (approvedPeopleQry != null)
                                    {
                                        var approvedPeopleList = approvedPeopleQry.Select(e => e.Id).ToList();

                                        if (approvedPeopleList != null && !approvedPeopleList.Contains(person.Person.Id))
                                        {
                                            if (remove)
                                            {
                                                groupType.Groups.Remove(group);
                                            }
                                            else
                                            {
                                                group.ExcludedByFilter = true;
                                            }
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return(true);
        }
Esempio n. 12
0
        public DataSet GetContributionPersonGroupAddress([FromBody] ContributionStatementOptions options)
        {
            Dictionary <string, object> parameters = new Dictionary <string, object>();
            var startDate = options.StartDate;
            var endDate   = options.EndDate ?? DateTime.MaxValue;

            // The SQL date type has a more limited range than C# DateTime. We need to conform the date values to fit
            // in the SQL date range or an error will be thrown in the stored procedure.
            if (startDate < SqlDateTime.MinValue.Value)
            {
                startDate = SqlDateTime.MinValue.Value;
            }

            if (endDate > SqlDateTime.MaxValue.Value)
            {
                endDate = SqlDateTime.MaxValue.Value;
            }

            if (options.AccountIds != null)
            {
                parameters.Add("accountIds", options.AccountIds.AsDelimited(","));
            }
            else
            {
                parameters.Add("accountIds", DBNull.Value);
            }

            if (options.PersonId.HasValue)
            {
                parameters.Add("personId", options.PersonId);
            }
            else
            {
                parameters.Add("personId", DBNull.Value);
            }

            if (options.IncludeIndividualsWithNoAddress)
            {
                parameters.Add("includeIndividualsWithNoAddress", options.IncludeIndividualsWithNoAddress);
            }
            else
            {
                parameters.Add("includeIndividualsWithNoAddress", false);
            }

            parameters.Add("orderByPostalCode", options.OrderByPostalCode);
            parameters.Add("startDate", startDate);
            parameters.Add("endDate", endDate);
            var result = DbService.GetDataSet("spFinance_ContributionStatementQuery", System.Data.CommandType.StoredProcedure, parameters);

            if (result.Tables.Count > 0)
            {
                var dataTable = result.Tables[0];
                dataTable.TableName = "contribution_person_group_address";

                if (options.DataViewId.HasValue)
                {
                    var dataView = new DataViewService(new RockContext()).Get(options.DataViewId.Value);
                    if (dataView != null)
                    {
                        var           dataViewGetQueryArgs = new DataViewGetQueryArgs();
                        var           personList           = dataView.GetQuery(dataViewGetQueryArgs).OfType <Rock.Model.Person>().Select(a => new { a.Id, a.GivingGroupId }).ToList();
                        HashSet <int> personIds            = new HashSet <int>(personList.Select(a => a.Id));
                        HashSet <int> groupsIds            = new HashSet <int>(personList.Where(a => a.GivingGroupId.HasValue).Select(a => a.GivingGroupId.Value).Distinct());

                        foreach (var row in dataTable.Rows.OfType <DataRow>().ToList())
                        {
                            var personId = row["PersonId"];
                            var groupId  = row["GroupId"];
                            if (personId != null && personId is int)
                            {
                                if (!personIds.Contains(( int )personId))
                                {
                                    dataTable.Rows.Remove(row);
                                }
                            }
                            else if (groupId != null && groupId is int)
                            {
                                if (!groupsIds.Contains(( int )groupId))
                                {
                                    dataTable.Rows.Remove(row);
                                }
                            }
                        }
                    }
                }
            }

            return(result);
        }
Esempio n. 13
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");
            var        commandTimeout       = dataMap.GetString("CommandTimeout").AsIntegerOrNull() ?? 180;

            // 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 activeSyncList = new List <GroupSyncInfo>();
                using (var rockContext = new RockContext())
                {
                    // Get groups that are not archived and are still active.
                    activeSyncList = new GroupSyncService(rockContext)
                                     .Queryable()
                                     .AsNoTracking()
                                     .AreNotArchived()
                                     .AreActive()
                                     .NeedToBeSynced()
                                     .Select(x => new GroupSyncInfo {
                        SyncId = x.Id, GroupName = x.Group.Name
                    })
                                     .ToList();
                }

                foreach (var syncInfo in activeSyncList)
                {
                    int  syncId         = syncInfo.SyncId;
                    bool hasSyncChanged = false;
                    context.UpdateLastStatusMessage($"Syncing group {syncInfo.GroupName}");

                    // Use a fresh rockContext per sync so that ChangeTracker doesn't get bogged down
                    using (var rockContext = new RockContext())
                    {
                        // increase the timeout just in case the data view source is slow
                        rockContext.Database.CommandTimeout = commandTimeout;
                        rockContext.SourceOfChange          = "Group Sync";

                        // Get the Sync
                        var sync = new GroupSyncService(rockContext)
                                   .Queryable()
                                   .Include(a => a.Group)
                                   .Include(a => a.SyncDataView)
                                   .AsNoTracking()
                                   .FirstOrDefault(s => s.Id == syncId);

                        if (sync == null || sync.SyncDataView.EntityTypeId != EntityTypeCache.Get(typeof(Person)).Id)
                        {
                            // invalid sync or invalid SyncDataView
                            continue;
                        }

                        dataViewName = sync.SyncDataView.Name;
                        groupName    = sync.Group.Name;

                        Stopwatch stopwatch = Stopwatch.StartNew();

                        // Get the person id's from the data view (source)
                        var dataViewGetQueryArgs = new DataViewGetQueryArgs
                        {
                            DbContext = rockContext,
                            DatabaseTimeoutSeconds = commandTimeout
                        };

                        List <int> sourcePersonIds;

                        try
                        {
                            var dataViewQry = sync.SyncDataView.GetQuery(dataViewGetQueryArgs);
                            sourcePersonIds = dataViewQry.Select(q => q.Id).ToList();
                        }
                        catch (Exception ex)
                        {
                            // If any error occurred trying get the 'where expression' from the sync-data-view,
                            // just skip trying to sync that particular group's Sync Data View for now.
                            var errorMessage = $"An error occurred while trying to GroupSync group '{groupName}' and data view '{dataViewName}' so the sync was skipped. Error: {ex.Message}";
                            errors.Add(errorMessage);
                            ExceptionLogService.LogException(new Exception(errorMessage, ex));
                            continue;
                        }

                        stopwatch.Stop();
                        DataViewService.AddRunDataViewTransaction(sync.SyncDataView.Id,
                                                                  Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds));

                        // Get the person id's in the group (target) for the role being synced.
                        // Note: targetPersonIds must include archived group members
                        // so we don't try to delete anyone who's already archived, and
                        // it must include deceased members so we can remove them if they
                        // are no longer in the data view.
                        var existingGroupMemberPersonList = new GroupMemberService(rockContext)
                                                            .Queryable(true, true).AsNoTracking()
                                                            .Where(gm => gm.GroupId == sync.GroupId)
                                                            .Where(gm => gm.GroupRoleId == sync.GroupTypeRoleId)
                                                            .Select(gm => new
                        {
                            PersonId   = gm.PersonId,
                            IsArchived = gm.IsArchived
                        })
                                                            .ToList();

                        var targetPersonIdsToDelete = existingGroupMemberPersonList.Where(t => !sourcePersonIds.Contains(t.PersonId) && t.IsArchived != true).ToList();
                        if (targetPersonIdsToDelete.Any())
                        {
                            context.UpdateLastStatusMessage($"Deleting {targetPersonIdsToDelete.Count()} group records in {syncInfo.GroupName} that are no longer in the sync data view");
                        }

                        int deletedCount = 0;

                        // Delete people from the group/role that are no longer in the data view --
                        // but not the ones that are already archived.
                        foreach (var targetPerson in targetPersonIdsToDelete)
                        {
                            deletedCount++;
                            if (deletedCount % 100 == 0)
                            {
                                context.UpdateLastStatusMessage($"Deleted {deletedCount} of {targetPersonIdsToDelete.Count()} group member records for group {syncInfo.GroupName}");
                            }

                            try
                            {
                                // Use a new context to limit the amount of change-tracking required
                                using (var groupMemberContext = new RockContext())
                                {
                                    // Delete the records for that person's group and role.
                                    // NOTE: just in case there are duplicate records, delete all group member records for that person and role
                                    var groupMemberService = new GroupMemberService(groupMemberContext);
                                    foreach (var groupMember in groupMemberService
                                             .Queryable(true, true)
                                             .Where(m =>
                                                    m.GroupId == sync.GroupId &&
                                                    m.GroupRoleId == sync.GroupTypeRoleId &&
                                                    m.PersonId == targetPerson.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.ExitSystemCommunication != null)
                                    {
                                        var person = new PersonService(groupMemberContext).Get(targetPerson.PersonId);
                                        if (person.CanReceiveEmail(false))
                                        {
                                            // Send the exit email
                                            var mergeFields = new Dictionary <string, object>();
                                            mergeFields.Add("Group", sync.Group);
                                            mergeFields.Add("Person", person);
                                            var emailMessage = new RockEmailMessage(sync.ExitSystemCommunication);
                                            emailMessage.AddRecipient(new RockEmailMessageRecipient(person, mergeFields));
                                            var emailErrors = new List <string>();
                                            emailMessage.Send(out emailErrors);
                                            errors.AddRange(emailErrors);
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                ExceptionLogService.LogException(ex);
                                continue;
                            }

                            hasSyncChanged = true;
                        }


                        // Now find all the people in the source list who are NOT already in the target list (as Unarchived)
                        var targetPersonIdsToAdd = sourcePersonIds.Where(s => !existingGroupMemberPersonList.Any(t => t.PersonId == s && t.IsArchived == false)).ToList();

                        // Make a list of PersonIds that have an Archived group member record
                        // if this person isn't already a member of the list as an Unarchived member, we can Restore the group member for that PersonId instead
                        var archivedTargetPersonIds = existingGroupMemberPersonList.Where(t => t.IsArchived == true).Select(a => a.PersonId).ToList();

                        context.UpdateLastStatusMessage($"Adding {targetPersonIdsToAdd.Count()} group member records for group {syncInfo.GroupName}");
                        int addedCount    = 0;
                        int notAddedCount = 0;
                        foreach (var personId in targetPersonIdsToAdd)
                        {
                            if ((addedCount + notAddedCount) % 100 == 0)
                            {
                                string notAddedMessage = string.Empty;
                                if (notAddedCount > 0)
                                {
                                    notAddedMessage = $"{Environment.NewLine} There are {notAddedCount} members that could not be added due to group requirements.";
                                }
                                context.UpdateLastStatusMessage($"Added {addedCount} of {targetPersonIdsToAdd.Count()} group member records for group {syncInfo.GroupName}. {notAddedMessage}");
                            }
                            try
                            {
                                // Use a new context to limit the amount of change-tracking required
                                using (var groupMemberContext = new RockContext())
                                {
                                    var groupMemberService = new GroupMemberService(groupMemberContext);
                                    var groupService       = new GroupService(groupMemberContext);

                                    // If this person is currently archived...
                                    if (archivedTargetPersonIds.Contains(personId))
                                    {
                                        // ...then we'll just restore them;
                                        GroupMember archivedGroupMember = groupService.GetArchivedGroupMember(sync.Group, personId, sync.GroupTypeRoleId);

                                        if (archivedGroupMember == null)
                                        {
                                            // shouldn't happen, but just in case
                                            continue;
                                        }

                                        archivedGroupMember.GroupMemberStatus = GroupMemberStatus.Active;
                                        if (archivedGroupMember.IsValidGroupMember(groupMemberContext))
                                        {
                                            addedCount++;
                                            groupMemberService.Restore(archivedGroupMember);
                                            groupMemberContext.SaveChanges();
                                        }
                                        else
                                        {
                                            notAddedCount++;
                                            // Validation errors will get added to the ValidationResults collection. Add those results to the log and then move on to the next person.
                                            var ex = new GroupMemberValidationException(string.Join(",", archivedGroupMember.ValidationResults.Select(r => r.ErrorMessage).ToArray()));
                                            ExceptionLogService.LogException(ex);
                                            continue;
                                        }
                                    }
                                    else
                                    {
                                        // ...otherwise we will add a new person to the group with the role specified in the sync.
                                        var newGroupMember = new GroupMember {
                                            Id = 0
                                        };
                                        newGroupMember.PersonId          = personId;
                                        newGroupMember.GroupId           = sync.GroupId;
                                        newGroupMember.GroupMemberStatus = GroupMemberStatus.Active;
                                        newGroupMember.GroupRoleId       = sync.GroupTypeRoleId;

                                        if (newGroupMember.IsValidGroupMember(groupMemberContext))
                                        {
                                            addedCount++;
                                            groupMemberService.Add(newGroupMember);
                                            groupMemberContext.SaveChanges();
                                        }
                                        else
                                        {
                                            notAddedCount++;
                                            // Validation errors will get added to the ValidationResults collection. Add those results to the log and then move on to the next person.
                                            var ex = new GroupMemberValidationException(string.Join(",", newGroupMember.ValidationResults.Select(r => r.ErrorMessage).ToArray()));
                                            ExceptionLogService.LogException(ex);
                                            continue;
                                        }
                                    }

                                    // 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.WelcomeSystemCommunication != null)
                                    {
                                        var person = new PersonService(groupMemberContext).Get(personId);
                                        if (person.CanReceiveEmail(false))
                                        {
                                            // 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 user name.
                                            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.WelcomeSystemCommunication);
                                            emailMessage.AddRecipient(new RockEmailMessageRecipient(person, mergeFields));
                                            var emailErrors = new List <string>();
                                            emailMessage.Send(out emailErrors);
                                            errors.AddRange(emailErrors);
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                ExceptionLogService.LogException(ex);
                                continue;
                            }

                            hasSyncChanged = true;
                        }

                        // Increment Groups Changed Counter (if people were deleted or added to the group)
                        if (hasSyncChanged)
                        {
                            groupsChanged++;
                        }

                        // Increment the Groups Synced Counter
                        groupsSynced++;
                    }

                    // Update last refresh datetime in different context to avoid side-effects.
                    using (var rockContext = new RockContext())
                    {
                        var sync = new GroupSyncService(rockContext)
                                   .Queryable()
                                   .FirstOrDefault(s => s.Id == syncId);

                        sync.LastRefreshDateTime = RockDateTime.Now;

                        rockContext.SaveChanges();
                    }
                }

                // Format the result message
                var resultMessage = string.Empty;
                if (groupsSynced == 0)
                {
                    resultMessage = "No groups to sync";
                }
                else if (groupsSynced == 1)
                {
                    resultMessage = "1 group was synced";
                }
                else
                {
                    resultMessage = string.Format("{0} groups were synced", 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;
            }
        }
Esempio n. 14
0
        /// <summary>
        /// Perform the job using the parameters supplied in the execution context.
        /// </summary>
        /// <param name="context"></param>
        public void Execute(IJobExecutionContext context)
        {
            // Get the configuration settings for this job instance.
            var dataMap = context.JobDetail.JobDataMap;

            var workflowTypeGuid = dataMap.GetString(AttributeKey.Workflow).AsGuidOrNull();
            var dataViewGuid     = dataMap.GetString(AttributeKey.DataView).AsGuidOrNull();

            if (dataViewGuid == null)
            {
                throw new Exception("Data view not selected");
            }

            var rockContext     = new RockContext();
            var dataViewService = new DataViewService(rockContext);
            var dataView        = dataViewService.Get(dataViewGuid.Value);

            if (dataView == null)
            {
                throw new Exception("Data view not found");
            }

            // Get the set of entity key values returned by the Data View.
            Stopwatch stopwatch            = Stopwatch.StartNew();
            var       dataViewGetQueryArgs = new DataViewGetQueryArgs
            {
                DbContext = rockContext
            };

            var qry = dataView.GetQuery(dataViewGetQueryArgs);

            var modelType = dataView.EntityType.GetType();

            if (qry == null)
            {
                throw new Exception("Data view results not found");
            }

            if (modelType == null)
            {
                throw new Exception("Entity type of data view not found");
            }

            var entityIds = qry.Select(e => e.Id).ToList();

            stopwatch.Stop();
            DataViewService.AddRunDataViewTransaction(dataView.Id,
                                                      Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds));

            var entityTypeId   = dataView.EntityTypeId.Value;
            var entityTypeName = modelType.GetFriendlyTypeName();

            int workflowsLaunched = 0;

            // For each entity, create a new transaction to launch a workflow.
            foreach (var entityId in entityIds)
            {
                var transaction = new LaunchEntityWorkflowTransaction(workflowTypeGuid.Value, string.Empty, entityTypeId, entityId);

                Rock.Transactions.RockQueue.TransactionQueue.Enqueue(transaction);


                workflowsLaunched++;
            }

            context.Result = string.Format("{0} workflows launched", workflowsLaunched);
        }