/// <summary> /// Shows the configuration dialog. /// </summary> private void ShowConfigurationDialog() { // even though it'll be in the background, show the roster (so it doesn't look like it disappeared) // also, since there will be postbacks, let's enable viewstate when the configuration dialog is showing PopulateRoster(ViewStateMode.Enabled); // don't do the live refresh when the configuration dialog is showing UpdateLiveRefreshConfiguration(false); RosterConfiguration rosterConfiguration = this.GetBlockUserPreference(UserPreferenceKey.RosterConfigurationJSON) .FromJsonOrNull <RosterConfiguration>(); if (rosterConfiguration == null) { rosterConfiguration = new RosterConfiguration(); } gpGroups.SetValues(rosterConfiguration.PickedGroupIds ?? new int[0]); cbIncludeChildGroups.Checked = rosterConfiguration.IncludeChildGroups; UpdateIncludeChildGroupsVisibility(); UpdateScheduleList(); lbSchedules.SetValues(rosterConfiguration.ScheduleIds ?? new int[0]); UpdateLocationListFromSelectedSchedules(); cblLocations.SetValues(rosterConfiguration.LocationIds ?? new int[0]); cbDisplayRole.Checked = rosterConfiguration.DisplayRole; mdRosterConfiguration.Show(); }
/// <summary> /// Updates the configuration from URL. /// </summary> private void UpdateConfigurationFromUrl() { /* 2020-07-21 MDP * If PageParameters are used, we use the same behavior as the various Analytics blocks which is * - Set UserPrefs from URL when first loaded, then Edit/Use/Save UserPrefs like usual */ RosterConfiguration rosterConfiguration = this.GetBlockUserPreference(UserPreferenceKey.RosterConfigurationJSON) .FromJsonOrNull <RosterConfiguration>() ?? new RosterConfiguration(); if (this.PageParameter(PageParameterKey.GroupIds).IsNotNullOrWhiteSpace()) { // if GroupIds is a page parameter, set GroupIds and IncludeChildGroups from the URL rosterConfiguration.PickedGroupIds = this.PageParameter(PageParameterKey.GroupIds).Split(',').AsIntegerList().ToArray(); } if (this.PageParameter(PageParameterKey.IncludeChildGroups).IsNotNullOrWhiteSpace()) { rosterConfiguration.IncludeChildGroups = this.PageParameter(PageParameterKey.IncludeChildGroups).AsBoolean(); } if (this.PageParameter(PageParameterKey.LocationIds).IsNotNullOrWhiteSpace()) { // if LocationIds is a page parameter, use that as the LocationIds rosterConfiguration.LocationIds = this.PageParameter(PageParameterKey.LocationIds).Split(',').AsIntegerList().ToArray(); } if (this.PageParameter(PageParameterKey.ScheduleIds).IsNotNullOrWhiteSpace()) { // if ScheduleIds is a page parameter, use that as the ScheduleIds rosterConfiguration.ScheduleIds = this.PageParameter(PageParameterKey.ScheduleIds).Split(',').AsIntegerList().ToArray(); } // just in case URL updated any configuration, save it back to user preferences this.SetBlockUserPreference(UserPreferenceKey.RosterConfigurationJSON, rosterConfiguration.ToJson()); }
/// <summary> /// Raises the <see cref="E:System.Web.UI.Control.Load" /> event. /// </summary> /// <param name="e">The <see cref="T:System.EventArgs" /> object that contains the event data.</param> protected override void OnLoad(EventArgs e) { base.OnLoad(e); RockPage.AddCSSLink("~/Themes/Rock/Styles/group-scheduler.css", true); if (!this.IsPostBack) { UpdateConfigurationFromUrl(); UpdateLiveRefreshConfiguration(this.GetAttributeValue(AttributeKey.EnableLiveRefresh).AsBoolean()); RosterConfiguration rosterConfiguration = this.GetBlockUserPreference(UserPreferenceKey.RosterConfigurationJSON) .FromJsonOrNull <RosterConfiguration>() ?? new RosterConfiguration(); if (!rosterConfiguration.IsConfigured()) { ShowConfigurationDialog(); } else { PopulateRoster(); } } else { HandleCustomPostbackEvents(); } }
/// <summary> /// Handles the SaveClick event of the mdRosterConfiguration control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> protected void mdRosterConfiguration_SaveClick(object sender, EventArgs e) { RosterConfiguration rosterConfiguration = this.GetBlockUserPreference(UserPreferenceKey.RosterConfigurationJSON) .FromJsonOrNull <RosterConfiguration>(); if (rosterConfiguration == null) { rosterConfiguration = new RosterConfiguration(); } rosterConfiguration.PickedGroupIds = gpGroups.SelectedValuesAsInt().ToArray(); rosterConfiguration.IncludeChildGroups = cbIncludeChildGroups.Checked; rosterConfiguration.LocationIds = cblLocations.SelectedValuesAsInt.ToArray(); rosterConfiguration.ScheduleIds = lbSchedules.SelectedValuesAsInt.ToArray(); rosterConfiguration.DisplayRole = cbDisplayRole.Checked; this.SetBlockUserPreference(UserPreferenceKey.RosterConfigurationJSON, rosterConfiguration.ToJson()); mdRosterConfiguration.Hide(); UpdateLiveRefreshConfiguration(this.GetAttributeValue(AttributeKey.EnableLiveRefresh).AsBoolean()); PopulateRoster(); }
/// <summary> /// Populates the roster. /// In cases where there won't be a postback, we can disable view state. The only time we need viewstate is when the Configuration Dialog /// is showing. /// </summary> private void PopulateRoster(ViewStateMode viewStateMode = ViewStateMode.Disabled) { RosterConfiguration rosterConfiguration = this.GetBlockUserPreference(UserPreferenceKey.RosterConfigurationJSON) .FromJsonOrNull <RosterConfiguration>() ?? new RosterConfiguration(); if (!rosterConfiguration.IsConfigured()) { return; } int[] scheduleIds = rosterConfiguration.ScheduleIds; int[] locationIds = rosterConfiguration.LocationIds; List <int> pickedGroupIds = rosterConfiguration.PickedGroupIds.ToList(); var allGroupIds = new List <int>(); allGroupIds.AddRange(pickedGroupIds); var rockContext = new RockContext(); // Only use teh ShowChildGroups option when there is 1 group selected if (rosterConfiguration.IncludeChildGroups && pickedGroupIds.Count == 1) { // if there is exactly one groupId we can avoid a 'Contains' (Contains has a small performance impact) var parentGroupId = pickedGroupIds[0]; var groupService = new GroupService(rockContext); // just the first level of child groups, not all decendants var childGroupIds = groupService.Queryable().Where(a => a.ParentGroupId == parentGroupId).Select(a => a.Id).ToList(); allGroupIds.AddRange(childGroupIds); } allGroupIds = allGroupIds.Distinct().ToList(); var attendanceOccurrenceService = new AttendanceOccurrenceService(rockContext); // An OccurrenceDate probably won't be included in the URL, but just in case DateTime?occurrenceDate = this.PageParameter(PageParameterKey.OccurrenceDate).AsDateTime(); if (!occurrenceDate.HasValue) { occurrenceDate = RockDateTime.Today; } // only show occurrences for the current day var attendanceOccurrenceQuery = attendanceOccurrenceService .Queryable() .Where(a => a.ScheduleId.HasValue && a.LocationId.HasValue && a.GroupId.HasValue) .WhereDeducedIsActive() .Where(a => allGroupIds.Contains(a.GroupId.Value) && a.OccurrenceDate == occurrenceDate && scheduleIds.Contains(a.ScheduleId.Value)); // if specific locations are specified, use those, otherwise just show all if (locationIds.Any()) { attendanceOccurrenceQuery = attendanceOccurrenceQuery.Where(a => locationIds.Contains(a.LocationId.Value)); } // limit attendees to ones that schedules (or are checked-in regardless of being scheduled) var confirmedAttendancesForOccurrenceQuery = attendanceOccurrenceQuery .SelectMany(a => a.Attendees) .Include(a => a.PersonAlias.Person) .Include(a => a.Occurrence.Group.Members) .WhereScheduledOrCheckedIn(); var confirmedScheduledIndividualsForOccurrenceId = confirmedAttendancesForOccurrenceQuery .AsNoTracking() .ToList() .GroupBy(a => a.OccurrenceId) .ToDictionary( k => k.Key, v => v.Select(a => new ScheduledIndividual { ScheduledAttendanceItemStatus = Attendance.GetScheduledAttendanceItemStatus(a.RSVP, a.ScheduledToAttend), Person = a.PersonAlias.Person, GroupMember = a.Occurrence.Group.Members.FirstOrDefault(gm => gm.PersonId == a.PersonAlias.PersonId), CurrentlyCheckedIn = a.DidAttend == true }) .ToList()); List <AttendanceOccurrence> attendanceOccurrenceList = attendanceOccurrenceQuery .Include(a => a.Schedule) .Include(a => a.Attendees) .Include(a => a.Group) .Include(a => a.Location) .AsNoTracking() .ToList() .OrderBy(a => a.OccurrenceDate) .ThenBy(a => a.Schedule.Order) .ThenBy(a => a.Schedule.GetNextStartDateTime(a.OccurrenceDate)) .ThenBy(a => a.Location.Name) .ToList(); var occurrenceRosterInfoList = new List <OccurrenceRosterInfo>(); foreach (var attendanceOccurrence in attendanceOccurrenceList) { var scheduleDate = attendanceOccurrence.Schedule.GetNextStartDateTime(attendanceOccurrence.OccurrenceDate); var scheduledIndividuals = confirmedScheduledIndividualsForOccurrenceId.GetValueOrNull(attendanceOccurrence.Id); if ((scheduleDate == null) || (scheduleDate.Value.Date != attendanceOccurrence.OccurrenceDate)) { // scheduleDate can be later than the OccurrenceDate (or null) if there are exclusions that cause the schedule // to not occur on the occurrence date. In this case, don't show the roster unless there are somehow individuals // scheduled for this occurrence. if (scheduledIndividuals == null || !scheduledIndividuals.Any()) { // no scheduleDate and no scheduled individuals, so continue on to the next attendanceOccurrence continue; } } var occurrenceRosterInfo = new OccurrenceRosterInfo { Group = attendanceOccurrence.Group, Location = attendanceOccurrence.Location, Schedule = attendanceOccurrence.Schedule, ScheduleDate = scheduleDate, ScheduledIndividuals = scheduledIndividuals }; occurrenceRosterInfoList.Add(occurrenceRosterInfo); } var mergeFields = LavaHelper.GetCommonMergeFields(this.RockPage); mergeFields.Add("OccurrenceList", occurrenceRosterInfoList); mergeFields.Add("DisplayRole", rosterConfiguration.DisplayRole); mergeFields.Add("OccurrenceDate", occurrenceDate); var rosterLavaTemplate = this.GetAttributeValue(AttributeKey.RosterLavaTemplate); var rosterHtml = rosterLavaTemplate.ResolveMergeFields(mergeFields); // by default, let's disable viewstate (except for when the configuration dialog is showing) lOccurrenceRosterHTML.ViewStateMode = viewStateMode; lOccurrenceRosterHTML.Text = rosterHtml; }