Пример #1
0
        /// <summary>
        /// Gets all information needed and merges the data through the Lava
        /// template to get the final content to be displayed.
        /// </summary>
        /// <param name="rockContext">The rock context.</param>
        /// <param name="filter">The filter options.</param>
        /// <returns>The content to be displayed by the client.</returns>
        private string GetResultContent(RockContext rockContext, GroupFinderFilter filter)
        {
            var mergeFields = RequestContext.GetCommonMergeFields();
            var groups      = GetGroups(rockContext, filter)
                              .Include(g => g.GroupLocations.Select(gl => gl.Location))
                              .ToList();

            // If they provided a location then attempt to filter and sort by
            // distance.
            if (filter.Location?.Latitude != null && filter.Location?.Longitude != null)
            {
                var distances = GetDistances(groups, GroupTypesLocationType, filter.Location.Latitude.Value, filter.Location.Longitude.Value);

                // Only show groups with a known location, and sort those by distance.
                // Filtering is done to keep in parity with the Web version.
                groups = groups.Where(a => distances.ContainsKey(a.Id))
                         .OrderBy(a => distances[a.Id])
                         .ThenBy(a => a.Name)
                         .ToList();

                mergeFields.AddOrReplace("Distances", distances);
            }

            if (MaxResults > 0)
            {
                groups = groups.Take(MaxResults).ToList();
            }

            mergeFields.AddOrReplace("DetailPage", DetailPageGuid);
            mergeFields.AddOrReplace("Groups", groups);

            return(Template.ResolveMergeFields(mergeFields));
        }
Пример #2
0
 public BlockActionResult GetGroups(GroupFinderFilter filter)
 {
     using (var rockContext = new RockContext())
     {
         return(ActionOk(new GetGroupsResult
         {
             Content = GetResultContent(rockContext, filter)
         }));
     }
 }
Пример #3
0
        /// <summary>
        /// Gets the groups that match the filter options.
        /// </summary>
        /// <param name="rockContext">The rock context.</param>
        /// <param name="filter">The filter options.</param>
        /// <returns>A queryable of matching groups.</returns>
        private IQueryable <Group> GetGroups(RockContext rockContext, GroupFinderFilter filter)
        {
            var groupService    = new GroupService(rockContext);
            var validAttributes = GetValidGroupAttributes(rockContext);

            if (!GroupTypeGuids.Any())
            {
                return(groupService.Queryable().Where(g => false));
            }

            var groupTypeGuids = GroupTypeGuids;
            var daysOfWeek     = filter.DayOfWeek.HasValue ? new List <DayOfWeek> {
                filter.DayOfWeek.Value
            } : null;
            var timePeriodsOfDay = filter.TimePeriodOfDay.HasValue ? new List <TimePeriodOfDay> {
                filter.TimePeriodOfDay.Value
            } : null;
            var campuses = filter.CampusGuid.HasValue ? new List <Guid> {
                filter.CampusGuid.Value
            } : null;
            var requiredSpotsAvailable = HideOvercapacityGroups ? ( int? )1 : null;
            var attributeFilters       = filter.Attributes
                                         .Select(f => new
            {
                Attribute = validAttributes.FirstOrDefault(a => a.Key == f.Key),
                f.Value
            })
                                         .Where(a => a.Attribute != null && a.Value != null)
                                         .ToDictionary(a => a.Attribute, a => a.Value);

            // -- Everything below this line is common logic that can be moved to
            // the service layer.


            // Initial query only includes active and public groups.
            var groupQry = groupService.Queryable()
                           .Include(g => g.GroupLocations.Select(l => l.Location))
                           .Where(g => g.IsActive && g.IsPublic);

            // If any group types were specified then filter to only groups
            // that match one of the group types.
            if (groupTypeGuids != null && groupTypeGuids.Any())
            {
                groupQry = groupQry.Where(g => groupTypeGuids.Contains(g.GroupType.Guid));
            }

            // If any days of the week were specified then filter to only groups
            // that match one of those days of the week.
            if (daysOfWeek != null && daysOfWeek.Any())
            {
                groupQry = groupQry
                           .Where(g => g.Schedule.WeeklyDayOfWeek.HasValue &&
                                  daysOfWeek.Contains(g.Schedule.WeeklyDayOfWeek.Value));
            }

            // If any time periods were specified then filter to only groups
            // that match one of those time periods.
            if (timePeriodsOfDay != null && timePeriodsOfDay.Any())
            {
                groupQry = groupQry.WhereTimePeriodIsOneOf(timePeriodsOfDay, g => g.Schedule.WeeklyTimeOfDay.Value);
            }

            // If any campuses were specified then filter to only groups
            // that belong to one of those campuses.
            if (campuses != null && campuses.Any())
            {
                groupQry = groupQry.Where(g => campuses.Contains(g.Campus.Guid));
            }

            // If it has been requested to hide groups that are already over
            // capacity then do so.
            if (requiredSpotsAvailable.HasValue)
            {
                // Filter by the overall group size.
                groupQry = groupQry.Where(g => !g.GroupCapacity.HasValue ||
                                          g.GroupType.GroupCapacityRule == GroupCapacityRule.None ||
                                          (g.Members.Where(m => m.GroupMemberStatus == GroupMemberStatus.Active).Count() + requiredSpotsAvailable.Value) <= g.GroupCapacity);

                // Filter by the default role that new members would be placed into.
                groupQry = groupQry.Where(g => g.GroupType == null ||
                                          g.GroupType.GroupCapacityRule == GroupCapacityRule.None ||
                                          g.GroupType.DefaultGroupRole == null ||
                                          g.GroupType.DefaultGroupRole.MaxCount == null ||
                                          (g.Members.Where(m => m.GroupRoleId == g.GroupType.DefaultGroupRoleId && m.GroupMemberStatus == GroupMemberStatus.Active).Count() + requiredSpotsAvailable.Value) <= g.GroupType.DefaultGroupRole.MaxCount);
            }

            // Filter query by any configured attribute filters
            if (attributeFilters != null && attributeFilters.Any())
            {
                var processedAttributes = new HashSet <string>();

                /*
                 *  07/01/2021 - MSB
                 *
                 *  This section of code creates an expression for each attribute in the search. The attributes from the same
                 *  Group Type get grouped and &&'d together. Then the grouped Expressions will get ||'d together so that results
                 *  will be returned across Group Types.
                 *
                 *  If we don't do this, when the Admin adds attributes from two different Group Types and then the user enters data
                 *  for both attributes they would get no results because Attribute A from Group Type A doesn't exists in Group Type B.
                 *
                 *  Reason: Queries across Group Types
                 */
                var filters             = new Dictionary <string, Expression>();
                var parameterExpression = groupService.ParameterExpression;

                foreach (var attributeFilter in attributeFilters)
                {
                    var attribute = attributeFilter.Key;
                    var values    = attributeFilter.Value;
                    var queryKey  = $"{attribute.EntityTypeQualifierColumn}_{attribute.EntityTypeQualifierValue}";

                    Expression leftExpression = null;
                    if (filters.ContainsKey(queryKey))
                    {
                        leftExpression = filters[queryKey];
                    }

                    var expression = Rock.Utility.ExpressionHelper.BuildExpressionFromFieldType <Group>(values.ToList(), attribute, groupService, parameterExpression);
                    if (expression != null)
                    {
                        if (leftExpression == null)
                        {
                            filters[queryKey] = expression;
                        }
                        else
                        {
                            filters[queryKey] = Expression.And(leftExpression, expression);
                        }
                    }
                }

                // If we have a single filter group then just filter by the
                // expression. If we have more than one then OR each group
                // together and apply the resulting expression.
                if (filters.Count == 1)
                {
                    groupQry = groupQry.Where(parameterExpression, filters.FirstOrDefault().Value);
                }
                else if (filters.Count > 1)
                {
                    var keys       = filters.Keys.ToList();
                    var expression = filters[keys[0]];

                    for (var i = 1; i < filters.Count; i++)
                    {
                        expression = Expression.Or(expression, filters[keys[i]]);
                    }

                    groupQry = groupQry.Where(parameterExpression, expression);
                }
            }

            return(groupQry);
        }