/// <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); nbPeople.Visible = false; if (!Page.IsPostBack) { int?setId = PageParameter("Set").AsIntegerOrNull(); if (setId.HasValue) { var selectedPersonIds = new EntitySetItemService(new RockContext()) .GetByEntitySetId(setId.Value) .Select(i => i.EntityId) .Distinct() .ToList(); // Get the people selected var people = new PersonService(new RockContext()).Queryable("CreatedByPersonAlias.Person,Users", true) .Where(p => selectedPersonIds.Contains(p.Id)) .ToList(); // Create the data structure used to build grid MergeData = new MergeData(people, headingKeys); BuildColumns(); BindGrid(); } } else { var primaryColIndex = hfSelectedColumn.Value.AsIntegerOrNull(); // Save the primary header radio button's selection foreach (var col in gValues.Columns.OfType <PersonMergeField>()) { col.OnDelete += personCol_OnDelete; if (primaryColIndex.HasValue && primaryColIndex.Value == col.ColumnIndex) { MergeData.PrimaryPersonId = col.PersonId; } } } }
/// <summary> /// Gets the merge object list for the current EntitySet /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="fetchCount">The fetch count.</param> /// <returns></returns> private List <object> GetMergeObjectList(RockContext rockContext, int?fetchCount = null) { int entitySetId = hfEntitySetId.Value.AsInteger(); var entitySetService = new EntitySetService(rockContext); var entitySet = entitySetService.Get(entitySetId); Dictionary <int, object> mergeObjectsDictionary = new Dictionary <int, object>(); // If this EntitySet contains IEntity Items, add those first if (entitySet.EntityTypeId.HasValue) { var qryEntity = entitySetService.GetEntityQuery(entitySetId); if (fetchCount.HasValue) { qryEntity = qryEntity.Take(fetchCount.Value); } var entityTypeCache = EntityTypeCache.Read(entitySet.EntityTypeId.Value); bool isPersonEntityType = entityTypeCache != null && entityTypeCache.Guid == Rock.SystemGuid.EntityType.PERSON.AsGuid(); bool isGroupMemberEntityType = entityTypeCache != null && entityTypeCache.Guid == Rock.SystemGuid.EntityType.GROUP_MEMBER.AsGuid(); bool combineFamilyMembers = cbCombineFamilyMembers.Visible && cbCombineFamilyMembers.Checked; if ((isGroupMemberEntityType || isPersonEntityType) && combineFamilyMembers) { IQueryable <IEntity> qryPersons; if (isGroupMemberEntityType) { qryPersons = qryEntity.OfType <GroupMember>().Select(a => a.Person).Distinct(); } else { qryPersons = qryEntity; } Guid familyGroupType = Rock.SystemGuid.GroupType.GROUPTYPE_FAMILY.AsGuid(); var qryFamilyGroupMembers = new GroupMemberService(rockContext).Queryable("GroupRole,Person").AsNoTracking() .Where(a => a.Group.GroupType.Guid == familyGroupType) .Where(a => qryPersons.Any(aa => aa.Id == a.PersonId)); var qryCombined = qryFamilyGroupMembers.Join( qryPersons, m => m.PersonId, p => p.Id, (m, p) => new { GroupMember = m, Person = p }) .GroupBy(a => a.GroupMember.GroupId) .Select(x => new { GroupId = x.Key, // Order People to match ordering in the GroupMembers.ascx block. Persons = // Adult Male x.Where(xx => xx.GroupMember.GroupRole.Guid.Equals(new Guid(Rock.SystemGuid.GroupRole.GROUPROLE_FAMILY_MEMBER_ADULT)) && xx.GroupMember.Person.Gender == Gender.Male).OrderByDescending(xx => xx.GroupMember.Person.BirthDate).Select(xx => xx.Person) // Adult Female .Concat(x.Where(xx => xx.GroupMember.GroupRole.Guid.Equals(new Guid(Rock.SystemGuid.GroupRole.GROUPROLE_FAMILY_MEMBER_ADULT)) && xx.GroupMember.Person.Gender != Gender.Male).OrderByDescending(xx => xx.GroupMember.Person.BirthDate).Select(xx => xx.Person)) // non-adults .Concat(x.Where(xx => !xx.GroupMember.GroupRole.Guid.Equals(new Guid(Rock.SystemGuid.GroupRole.GROUPROLE_FAMILY_MEMBER_ADULT))) .OrderByDescending(xx => xx.GroupMember.Person.BirthDate).Select(xx => xx.Person)) }); foreach (var combinedFamilyItem in qryCombined) { object mergeObject; string commaPersonIds = combinedFamilyItem.Persons.Select(a => a.Id).Distinct().ToList().AsDelimited(","); var primaryGroupPerson = combinedFamilyItem.Persons.FirstOrDefault() as Person; if (mergeObjectsDictionary.ContainsKey(primaryGroupPerson.Id)) { foreach (var person in combinedFamilyItem.Persons) { if (!mergeObjectsDictionary.ContainsKey(person.Id)) { primaryGroupPerson = person as Person; break; } } } // if we are combining from a GroupMemberEntityType list add the GroupMember attributes of the primary person in the combined list if (isGroupMemberEntityType) { var groupMember = qryEntity.OfType <GroupMember>().Where(a => a.PersonId == primaryGroupPerson.Id).FirstOrDefault(); primaryGroupPerson.AdditionalLavaFields = primaryGroupPerson.AdditionalLavaFields ?? new Dictionary <string, object>(); if (groupMember != null) { primaryGroupPerson.AdditionalLavaFields.AddOrIgnore("GroupMember", groupMember); } } if (combinedFamilyItem.Persons.Count() > 1) { var combinedPerson = primaryGroupPerson.ToJson().FromJsonOrNull <MergeTemplateCombinedPerson>(); var familyTitle = RockUdfHelper.ufnCrm_GetFamilyTitle(rockContext, null, combinedFamilyItem.GroupId, commaPersonIds, true); combinedPerson.FullName = familyTitle; var firstNameList = combinedFamilyItem.Persons.Select(a => (a as Person).FirstName).ToList(); var nickNameList = combinedFamilyItem.Persons.Select(a => (a as Person).NickName).ToList(); combinedPerson.FirstName = firstNameList.AsDelimited(", ", " & "); combinedPerson.NickName = nickNameList.AsDelimited(", ", " & "); combinedPerson.LastName = primaryGroupPerson.LastName; combinedPerson.SuffixValueId = null; combinedPerson.SuffixValue = null; mergeObject = combinedPerson; } else { mergeObject = primaryGroupPerson; } mergeObjectsDictionary.AddOrIgnore(primaryGroupPerson.Id, mergeObject); } } else if (isGroupMemberEntityType) { foreach (var groupMember in qryEntity.AsNoTracking().OfType <GroupMember>()) { var person = groupMember.Person; person.AdditionalLavaFields = new Dictionary <string, object>(); person.AdditionalLavaFields.Add("GroupMember", groupMember); mergeObjectsDictionary.AddOrIgnore(groupMember.PersonId, person); } } else { foreach (var item in qryEntity.AsNoTracking()) { mergeObjectsDictionary.AddOrIgnore(item.Id, item); } } } var entitySetItemService = new EntitySetItemService(rockContext); string[] emptyJson = new string[] { string.Empty, "{}" }; var entitySetItemMergeValuesQry = entitySetItemService.GetByEntitySetId(entitySetId, true).Where(a => !emptyJson.Contains(a.AdditionalMergeValuesJson)); if (fetchCount.HasValue) { entitySetItemMergeValuesQry = entitySetItemMergeValuesQry.Take(fetchCount.Value); } // the entityId to use for NonEntity objects int nonEntityId = 1; // now, add the additional MergeValues regardless of if the EntitySet contains IEntity items or just Non-IEntity items foreach (var additionalMergeValuesItem in entitySetItemMergeValuesQry.AsNoTracking()) { object mergeObject; int entityId; if (additionalMergeValuesItem.EntityId > 0) { entityId = additionalMergeValuesItem.EntityId; } else { // not pointing to an actual EntityId, so use the nonEntityId for ti entityId = nonEntityId++; } if (mergeObjectsDictionary.ContainsKey(entityId)) { mergeObject = mergeObjectsDictionary[entityId]; } else { if (entitySet.EntityTypeId.HasValue) { // if already have real entities in our list, don't add additional items to the mergeObjectsDictionary continue; } // non-Entity merge object, so just use Dictionary mergeObject = new Dictionary <string, object>(); mergeObjectsDictionary.AddOrIgnore(entityId, mergeObject); } foreach (var additionalMergeValue in additionalMergeValuesItem.AdditionalMergeValues) { if (mergeObject is IEntity) { // add the additional fields to AdditionalLavaFields IEntity mergeEntity = (mergeObject as IEntity); mergeEntity.AdditionalLavaFields = mergeEntity.AdditionalLavaFields ?? new Dictionary <string, object>(); object mergeValueObject = additionalMergeValue.Value; // if the mergeValueObject is a JArray (JSON Object), convert it into an ExpandoObject or List<ExpandoObject> so that Lava will work on it if (mergeValueObject is JArray) { var jsonOfObject = mergeValueObject.ToJson(); try { mergeValueObject = Rock.Lava.RockFilters.FromJSON(jsonOfObject); } catch (Exception ex) { LogException(new Exception("MergeTemplateEntry couldn't do a FromJSON", ex)); } } mergeEntity.AdditionalLavaFields.AddOrIgnore(additionalMergeValue.Key, mergeValueObject); } else if (mergeObject is IDictionary <string, object> ) { // anonymous object with no fields yet IDictionary <string, object> nonEntityObject = mergeObject as IDictionary <string, object>; nonEntityObject.AddOrIgnore(additionalMergeValue.Key, additionalMergeValue.Value); } else { throw new Exception(string.Format("Unexpected MergeObject Type: {0}", mergeObject)); } } } var result = mergeObjectsDictionary.Select(a => a.Value); if (fetchCount.HasValue) { // make sure the result is limited to fetchCount (even though the above queries are also limited to fetch count) result = result.Take(fetchCount.Value); } return(result.ToList()); }
/// <summary> /// Assigns the connection requests from the SelectedCampaign's entity set. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="selectedCampaignItem">The selected campaign item.</param> /// <param name="numberOfRequestsRemaining">The number of requests remaining.</param> /// <param name="connectorPerson">The connector person.</param> private static void AssignConnectionRequestsFromEntitySet(RockContext rockContext, CampaignItem selectedCampaignItem, ref int numberOfRequestsRemaining, Person connectorPerson) { var opportunityService = new ConnectionOpportunityService(rockContext); ConnectionOpportunity opportunity = opportunityService.Get(selectedCampaignItem.OpportunityGuid); if (opportunity == null || !opportunity.IsActive) { return; } int?defaultStatusId = opportunity.ConnectionType.ConnectionStatuses .Where(s => s.IsDefault) .Select(s => ( int? )s.Id) .FirstOrDefault(); // If opportunity doesn't have a default status, something is wrong if (defaultStatusId == null) { return; } var connectorCampusIds = GetConnectorCampusIds(selectedCampaignItem, connectorPerson); var connectionRequestService = new ConnectionRequestService(rockContext); var connectionRequestActivityService = new ConnectionRequestActivityService(rockContext); // get previous connections for the connector that have the same campus of the connector, or if the person's campus or connector person's campus is null var previousConnectedPersonIdsForCurrentPerson = connectionRequestService.Queryable() .Where(a => a.ConnectionOpportunityId == opportunity.Id) .Where(a => a.ConnectorPersonAlias.PersonId == connectorPerson.Id) .Where(a => (a.CampusId == null) || connectorCampusIds.Any(connectorCampusId => connectorCampusId == null || a.CampusId.Value == connectorCampusId)) .Select(a => a.PersonAlias.PersonId).Distinct().ToList(); var entitySetId = CampaignConnectionHelper.GetEntitySet(selectedCampaignItem); var entitySetItemService = new EntitySetItemService(rockContext); var entitySetItemList = entitySetItemService.Queryable().Where(a => a.EntitySetId == entitySetId).OrderBy(a => a.Order).Select(a => new { PersonId = a.EntityId, EntityItemOrder = a.Order }).ToList(); if (selectedCampaignItem.PreferPreviousConnector) { // sort them by any where the current person was assigned to this person before entitySetItemList = entitySetItemList .OrderBy(a => previousConnectedPersonIdsForCurrentPerson.Any(x => x == a.PersonId)) .ThenBy(a => a.EntityItemOrder).ToList(); } else { entitySetItemList = entitySetItemList.OrderBy(a => a.EntityItemOrder).ToList(); } var personService = new PersonService(rockContext); // get the last connection datetime. var lastConnectionDateTime = RockDateTime.Now.AddDays(-selectedCampaignItem.DaysBetweenConnection); // if DaysBetweenConnection is 0 then check for connection request for any time period. if (selectedCampaignItem.DaysBetweenConnection == default(int)) { lastConnectionDateTime = DateTime.MinValue; } foreach (var entitySetItem in entitySetItemList) { var entitySetPerson = personService.Get(entitySetItem.PersonId); if (entitySetPerson == null) { continue; } var entitySetPersonPrimaryCampusId = entitySetPerson.PrimaryCampusId; bool validCampus = IsValidCampus(connectorCampusIds, entitySetPersonPrimaryCampusId); if (!validCampus) { continue; } // double check that they haven't already been added bool personAlreadyHasConnectionRequest = PersonAlreadyHasConnectionRequest(opportunity.Id, rockContext, lastConnectionDateTime, entitySetPerson.Id); if (personAlreadyHasConnectionRequest) { continue; } var connectionRequest = new ConnectionRequest(); connectionRequest.ConnectionOpportunityId = opportunity.Id; /* * 3/30/2020 - NA * * When setting the connection request's Requester, we have to use the PrimaryAlias * to set the connectionRequest.PersonAlias property because the ConnectionRequestChangeTransaction * https://github.com/SparkabilityGroup/Rock/blob/a556a9285b7fdfe5594441286242f4feaa5847f2/Rock/Transactions/ConnectionRequestChangeTransaction.cs#L123 * (which handles triggered workflows) expects it. Also, it needs to be tracked by * the current rockContext... hence the change from GetAsNoTracking() to just Get() above: * var entitySetPerson = personService.Get( entitySetItem.PersonId ); * * In other words, this will not work correctly: * connectionRequest.PersonAliasId = entitySetPerson.PrimaryAliasId.Value; * * Reason: This plug-in cannot change Rock core assembly code. */ connectionRequest.PersonAlias = entitySetPerson.PrimaryAlias; connectionRequest.ConnectionState = ConnectionState.Active; connectionRequest.ConnectorPersonAliasId = connectorPerson.PrimaryAliasId; connectionRequest.CampusId = entitySetPersonPrimaryCampusId; connectionRequest.ConnectionStatusId = defaultStatusId.Value; if (selectedCampaignItem.RequestCommentsLavaTemplate.IsNotNullOrWhiteSpace()) { var mergeFields = new Dictionary <string, object>(); mergeFields.Add("Person", entitySetPerson); mergeFields.Add("Family", entitySetPerson.GetFamily()); connectionRequest.Comments = selectedCampaignItem.RequestCommentsLavaTemplate.ResolveMergeFields(mergeFields); } connectionRequestService.Add(connectionRequest); var connectionActivityTypeAssignedGuid = Rock.SystemGuid.ConnectionActivityType.ASSIGNED.AsGuid(); int?assignedActivityId = new ConnectionActivityTypeService(rockContext).GetId(connectionActivityTypeAssignedGuid); if (assignedActivityId.HasValue) { var connectionRequestActivity = new ConnectionRequestActivity(); connectionRequestActivity.ConnectionRequest = connectionRequest; connectionRequestActivity.ConnectionOpportunityId = connectionRequest.ConnectionOpportunityId; connectionRequestActivity.ConnectionActivityTypeId = assignedActivityId.Value; connectionRequestActivity.ConnectorPersonAliasId = connectorPerson.PrimaryAliasId; connectionRequestActivityService.Add(connectionRequestActivity); } numberOfRequestsRemaining--; if (numberOfRequestsRemaining <= 0) { break; } } }
/// <summary> /// creates or updates the entity set on the basis of campaign connection configuration, and returns the Id of the entitySetId /// </summary> /// <param name="campaignConfiguration">The campaign configuration.</param> /// <returns></returns> public static int GetEntitySet(CampaignItem campaignConfiguration) { var rockContext = new RockContext(); var connectionOpportunityService = new ConnectionOpportunityService(rockContext); var connectionRequestService = new ConnectionRequestService(rockContext); var entitySetService = new Rock.Model.EntitySetService(rockContext); var connectionOpportunity = connectionOpportunityService.Get(campaignConfiguration.OpportunityGuid); // list of person on the basis of Dataview result and optout group. var filteredPersonIds = GetFilteredPersonIds(campaignConfiguration, rockContext); // get the last connection datetime. var lastConnectionDateTime = RockDateTime.Now.AddDays(-campaignConfiguration.DaysBetweenConnection); // if DaysBetweenConnection is 0 then check for connection request for any time period. if (campaignConfiguration.DaysBetweenConnection == default(int)) { lastConnectionDateTime = DateTime.MinValue; } // list of person that has active connection request OR has connection closed in the past number of days between connection. var excludedPersonIds = connectionRequestService .Queryable() .Where(a => a.ConnectionOpportunityId == connectionOpportunity.Id && ( a.ConnectionState == ConnectionState.Active || a.ConnectionState == ConnectionState.FutureFollowUp || ((a.ConnectionState == ConnectionState.Connected || a.ConnectionState == ConnectionState.Inactive) && a.ModifiedDateTime > lastConnectionDateTime))) .Select(a => a.PersonAlias.PersonId) .ToList(); // filtered list of person removing all the personIds found in excludedPersonIds List filteredPersonIds = filteredPersonIds.Where(a => !excludedPersonIds.Contains(a)).ToList(); // get the ordered list of personIds based on the oldest previous connection request and connection opportunity /* 2020-05-06 MDP * If there are many filteredPersonIds, we'll get a SQL Exception, so let's get *all* the Connected connection Requests first, * and then use C# to filter. */ var orderedLastCompletedRequestForPerson = connectionRequestService .Queryable() .Where(a => a.ConnectionOpportunityId == connectionOpportunity.Id && a.ConnectionState == ConnectionState.Connected) .GroupBy(a => a.PersonAlias.PersonId) .Select(a => new { PersonId = a.Key, LastConnectionDateTime = a.OrderByDescending(b => b.ModifiedDateTime).Select(b => b.ModifiedDateTime).FirstOrDefault() }) .OrderBy(a => a.LastConnectionDateTime) .Select(a => a.PersonId).ToList(); // Use C# to filter persons so we can avoid a SQL Exception orderedLastCompletedRequestForPerson = orderedLastCompletedRequestForPerson.Where(a => filteredPersonIds.Contains(a)).ToList(); var random = new Random(); //// get the final ordered list of personIds based on the oldest previous connection request and //// connection opportunity otherwise order randomly for the person who don't have any previous connection request. var orderedPersonIds = filteredPersonIds .OrderBy(a => { var index = orderedLastCompletedRequestForPerson.IndexOf(a); if (index == -1) { return(random.Next(orderedLastCompletedRequestForPerson.Count, int.MaxValue)); } else { return(index); } }).ToList(); EntitySet entitySet = null; if (campaignConfiguration.EntitySetId != default(int)) { entitySet = entitySetService.Get(campaignConfiguration.EntitySetId); } List <Rock.Model.EntitySetItem> entitySetItems = new List <Rock.Model.EntitySetItem>(); var personEntityTypeId = EntityTypeCache.Get <Rock.Model.Person>().Id; if (entitySet == null || entitySet.EntityTypeId != personEntityTypeId) { entitySet = new Rock.Model.EntitySet(); entitySet.EntityTypeId = personEntityTypeId; entitySet.ExpireDateTime = null; entitySetService.Add(entitySet); } else { var entitySetItemQry = new EntitySetItemService(rockContext) .Queryable().AsNoTracking() .Where(i => i.EntitySetId == entitySet.Id); rockContext.BulkDelete(entitySetItemQry); } // Update the EntitySet name entitySet.Name = campaignConfiguration.Name; var orderIndex = 0; foreach (var personId in orderedPersonIds) { try { var item = new Rock.Model.EntitySetItem(); item.Order = orderIndex++; item.EntityId = personId; entitySetItems.Add(item); } catch { // ignore } } rockContext.SaveChanges(); entitySetItems.ForEach(a => { a.EntitySetId = entitySet.Id; }); rockContext.BulkInsert(entitySetItems); return(entitySet.Id); }
/// <summary> /// Creates the connection requests. /// </summary> /// <param name="context">The context.</param> /// <param name="campaignItem">The campaign item.</param> /// <returns></returns> private int CreateConnectionRequests(IJobExecutionContext context, CampaignItem campaignItem) { context.UpdateLastStatusMessage($"Processing create connection requests for {campaignItem.Name}"); // Skip creating connection requests if set to "AsNeeded" and DailyLimitAssigned is 0 or null if (campaignItem.CreateConnectionRequestOption == CreateConnectionRequestOptions.AsNeeded && (campaignItem.DailyLimitAssigned <= 0 || campaignItem.DailyLimitAssigned == null)) { return(0); } int recordsProcessed = 0; var rockContext = new RockContext(); var connectionOpportunityService = new ConnectionOpportunityService(rockContext); var personAliasService = new PersonAliasService(rockContext); var entitySetItemService = new EntitySetItemService(rockContext); var connectionOpportunity = connectionOpportunityService.Get(campaignItem.OpportunityGuid); if (connectionOpportunity == null) { return(0); } // get cutoff for the last connection datetime. var lastConnectionDateTime = RockDateTime.Now.AddDays(-campaignItem.DaysBetweenConnection); // if DaysBetweenConnection is 0 then check for connection request for any time period. if (campaignItem.DaysBetweenConnection == default(int)) { lastConnectionDateTime = DateTime.MinValue; } var entitySetItemsQry = entitySetItemService .Queryable() .Where(a => a.EntitySetId == campaignItem.EntitySetId) .OrderBy(a => a.Order); bool autoAssignment = false; var eligibleConnectors = new List <ConnectionConnector>(); if (campaignItem.DailyLimitAssigned.HasValue && campaignItem.DailyLimitAssigned != default(int)) { autoAssignment = true; eligibleConnectors = GetEligibleConnectorWithLimit(connectionOpportunity.Id, rockContext, campaignItem.DailyLimitAssigned.Value); } var connectionStatusId = GetConnectionStatus(connectionOpportunity); if (!connectionStatusId.HasValue) { return(0); } var entitySetItemList = entitySetItemsQry.ToList(); foreach (var entitySetItem in entitySetItemList) { var entityItemPersonAlias = personAliasService.GetPrimaryAlias(entitySetItem.EntityId); int?connectorPersonId = null; if (autoAssignment) { connectorPersonId = GetConnector(campaignItem, connectionOpportunity, eligibleConnectors, entityItemPersonAlias, rockContext); if (campaignItem.CreateConnectionRequestOption == CreateConnectionRequestOptions.AsNeeded && !connectorPersonId.HasValue) { continue; } } // double check that they haven't already been added var personAlreadyHasConnectionRequest = CampaignConnectionHelper.PersonAlreadyHasConnectionRequest(connectionOpportunity.Id, rockContext, lastConnectionDateTime, entitySetItem.EntityId); if (personAlreadyHasConnectionRequest) { continue; } int?connectorPersonAliasId = null; if (connectorPersonId.HasValue) { foreach (var connectionConnector in eligibleConnectors.Where(a => a.PersonId == connectorPersonId.Value)) { connectorPersonAliasId = connectionConnector.PersonAliasId; connectionConnector.Current += 1; } } CreateConnectionRequest(entityItemPersonAlias, campaignItem.RequestCommentsLavaTemplate, connectionOpportunity.Id, connectionStatusId, connectorPersonAliasId, rockContext); recordsProcessed += 1; } return(recordsProcessed); }
/// <summary> /// Creates the connection requests. /// </summary> /// <param name="context">The context.</param> /// <param name="campaignItem">The campaign item.</param> /// <returns></returns> private int CreateConnectionRequests(IJobExecutionContext context, CampaignItem campaignItem) { context.UpdateLastStatusMessage($"Processing create connection requests for {campaignItem.Name}"); // Skip creating connection requests if set to "AsNeeded" and DailyLimitAssigned is 0 or null if (campaignItem.CreateConnectionRequestOption == CreateConnectionRequestOptions.AsNeeded && (campaignItem.DailyLimitAssigned <= 0 || campaignItem.DailyLimitAssigned == null)) { return(0); } int recordsProcessed = 0; var rockContext = new RockContext(); var connectionOpportunityService = new ConnectionOpportunityService(rockContext); var personAliasService = new PersonAliasService(rockContext); var connectionRequestService = new ConnectionRequestService(rockContext); var connectionOpportunity = connectionOpportunityService.Get(campaignItem.OpportunityGuid); if (connectionOpportunity == null) { return(0); } // get cutoff for the last connection datetime. var lastConnectionDateTime = RockDateTime.Now.AddDays(-campaignItem.DaysBetweenConnection); // if DaysBetweenConnection is 0 then check for connection request for any time period. if (campaignItem.DaysBetweenConnection == default(int)) { lastConnectionDateTime = DateTime.MinValue; } var entitySetItemService = new EntitySetItemService(rockContext); var entitySetItemsQry = entitySetItemService .Queryable() .Where(a => a.EntitySetId == campaignItem.EntitySetId) .OrderBy(a => a.Order); bool autoAssignment = false; var eligibleConnectors = new List <ConnectionConnector>(); if (campaignItem.CreateConnectionRequestOption == CreateConnectionRequestOptions.AsNeeded && campaignItem.DailyLimitAssigned.HasValue && campaignItem.DailyLimitAssigned != default(int)) { autoAssignment = true; eligibleConnectors = GetEligibleConnectorWithLimit(connectionOpportunity.Id, rockContext, campaignItem.DailyLimitAssigned.Value); } int?connectionStatusId = connectionOpportunity.ConnectionType.ConnectionStatuses .Where(s => s.IsDefault) .Select(s => ( int? )s.Id) .FirstOrDefault(); // If opportunity doesn't have a default status, something is wrong if (connectionStatusId == null) { ExceptionLogService.LogException(new Exception($"Unable to determine default connection status for {connectionOpportunity.Name} while processing campaigns", null)); return(0); } var dayofWeek = RockDateTime.Now.DayOfWeek; var entitySetItemList = entitySetItemsQry.ToList(); foreach (var entitySetItem in entitySetItemList) { var personAlias = personAliasService.GetPrimaryAlias(entitySetItem.EntityId); var defaultCampus = personAlias.Person.GetCampus(); int?connectorPersonAliasId = null; if (autoAssignment) { int?connectorPersonId = null; if (campaignItem.PreferPreviousConnector) { var personIds = eligibleConnectors .Where(a => a.Limit - a.Current > 0 && (!a.DaysOfWeek.Any() || a.DaysOfWeek.Contains(dayofWeek)) && (!a.CampusId.HasValue || (a.CampusId.HasValue && defaultCampus != null && defaultCampus.Id == a.CampusId.Value))) .Select(a => a.PersonId) .ToList(); if (personIds.Any()) { var person = connectionRequestService .Queryable() .Where(a => a.ConnectionOpportunityId == connectionOpportunity.Id && a.PersonAlias.PersonId == personAlias.PersonId && a.ConnectionState == ConnectionState.Connected && a.ConnectorPersonAliasId.HasValue && personIds.Contains(a.ConnectorPersonAlias.PersonId)) .Select(a => a.ConnectorPersonAlias.Person) .FirstOrDefault(); if (person != null) { connectorPersonId = person.Id; } } } if (!connectorPersonId.HasValue) { var eligibleConnector = eligibleConnectors .Where(a => a.Limit - a.Current > 0 && (!a.DaysOfWeek.Any() || a.DaysOfWeek.Contains(dayofWeek)) && (!a.CampusId.HasValue || (a.CampusId.HasValue && defaultCampus != null && defaultCampus.Id == a.CampusId.Value))) .OrderBy(a => a.Current) // order from least assigned to most assigned .ThenBy(x => Guid.NewGuid()) // and then randomize .FirstOrDefault(); if (eligibleConnector != null) { connectorPersonId = eligibleConnector.PersonId; } } if (!connectorPersonId.HasValue) { continue; } foreach (var connectionConnector in eligibleConnectors.Where(a => a.PersonId == connectorPersonId.Value)) { connectorPersonAliasId = connectionConnector.PersonAliasId; connectionConnector.Current += 1; } } using (var insertRockContext = new RockContext()) { try { /* * 3/30/2020 - NA * * When setting the connection request's Requester, we have to use the PrimaryAlias * to set the connectionRequest.PersonAlias property because the ConnectionRequestChangeTransaction * https://github.com/SparkabilityGroup/Rock/blob/a556a9285b7fdfe5594441286242f4feaa5847f2/Rock/Transactions/ConnectionRequestChangeTransaction.cs#L123 * (which handles triggered workflows) expects it. Also, it needs to be tracked by * the current rockContext... * * In other words, this will not work correctly: * PersonAliasId = personAlias.Id, * * Reason: This plug-in cannot change Rock core assembly code. */ var personPrimaryAlias = new PersonAliasService(insertRockContext).GetPrimaryAlias(entitySetItem.EntityId); var connectionRequestActivityService = new ConnectionRequestActivityService(insertRockContext); var insertConnectionRequestService = new ConnectionRequestService(insertRockContext); // double check that they haven't already been added var personAlreadyHasConnectionRequest = CampaignConnectionHelper.PersonAlreadyHasConnectionRequest(connectionOpportunity.Id, rockContext, lastConnectionDateTime, entitySetItem.EntityId); if (personAlreadyHasConnectionRequest) { continue; } var connectionRequest = new ConnectionRequest() { ConnectionOpportunityId = connectionOpportunity.Id, PersonAlias = personPrimaryAlias, ConnectionState = ConnectionState.Active, ConnectorPersonAliasId = connectorPersonAliasId, CampusId = defaultCampus?.Id, ConnectionStatusId = connectionStatusId.Value, }; if (campaignItem.RequestCommentsLavaTemplate.IsNotNullOrWhiteSpace()) { var mergeFields = new Dictionary <string, object>(); mergeFields.Add("Person", personAlias.Person); mergeFields.Add("Family", personAlias.Person.GetFamily()); connectionRequest.Comments = campaignItem.RequestCommentsLavaTemplate.ResolveMergeFields(mergeFields); } insertConnectionRequestService.Add(connectionRequest); if (connectorPersonAliasId.HasValue) { var connectionActivityTypeAssignedGuid = Rock.SystemGuid.ConnectionActivityType.ASSIGNED.AsGuid(); int?assignedActivityId = new ConnectionActivityTypeService(rockContext).GetId(connectionActivityTypeAssignedGuid); if (assignedActivityId.HasValue) { var connectionRequestActivity = new ConnectionRequestActivity(); connectionRequestActivity.ConnectionRequest = connectionRequest; connectionRequestActivity.ConnectionOpportunityId = connectionRequest.ConnectionOpportunityId; connectionRequestActivity.ConnectionActivityTypeId = assignedActivityId.Value; connectionRequestActivity.ConnectorPersonAliasId = connectorPersonAliasId; connectionRequestActivityService.Add(connectionRequestActivity); } } insertRockContext.SaveChanges(); } catch (Exception ex) { // Log exception and keep on trucking. var exception = new Exception($"Exception occurred trying to create connection request:{personAlias.Id}.", ex); createConnectionRequestExceptions.Add(exception); ExceptionLogService.LogException(exception, null); } } recordsProcessed += 1; } return(recordsProcessed); }
/// <summary> /// Binds the grid. /// </summary> private void BindGrid() { var rockContext = new RockContext(); var campaignConnectionItems = Rock.Web.SystemSettings.GetValue(CampaignConnectionKey.CAMPAIGN_CONNECTION_CONFIGURATION).FromJsonOrNull <List <CampaignItem> >() ?? new List <CampaignItem>(); var relatedOpportunityIds = campaignConnectionItems.Select(a => a.OpportunityGuid).ToList(); List <ConnectionOpportunity> relatedOpportunities = new List <ConnectionOpportunity>(); Dictionary <Guid, int> activeOpportunityRequests = new Dictionary <Guid, int>(); if (relatedOpportunityIds.Any()) { var connectionOpportunityService = new ConnectionOpportunityService(rockContext); var connectionRequestService = new ConnectionRequestService(rockContext); var connectionRequestsQry = connectionRequestService.Queryable(); relatedOpportunities = connectionOpportunityService.GetListByGuids(relatedOpportunityIds); foreach (var connectionOpportunity in relatedOpportunities) { var activeRequestCount = connectionRequestsQry .Where(a => a.ConnectionOpportunityId == connectionOpportunity.Id && (a.ConnectionState == ConnectionState.Active || (a.ConnectionState == ConnectionState.FutureFollowUp && a.FollowupDate.HasValue && a.FollowupDate.Value < _midnightToday))) .Count(); activeOpportunityRequests.AddOrReplace(connectionOpportunity.Guid, activeRequestCount); } } var relatedDataViewIds = campaignConnectionItems.Select(a => a.DataViewGuid).ToList(); List <DataView> relatedDataViews = new List <DataView>(); if (relatedDataViewIds.Any()) { var dataViewService = new DataViewService(rockContext); relatedDataViews = dataViewService.GetListByGuids(relatedDataViewIds); } var campaignConnectionRows = new List <CampaignConnectionRow>(); var entitySetItemQry = new EntitySetItemService(rockContext).Queryable(); foreach (var campaignConnectionItem in campaignConnectionItems) { var campaignConnectionRow = new CampaignConnectionRow(); campaignConnectionRow.Guid = campaignConnectionItem.Guid; campaignConnectionRow.Name = campaignConnectionItem.Name; campaignConnectionRow.ConnectionOpportunity = relatedOpportunities.FirstOrDefault(a => a.Guid == campaignConnectionItem.OpportunityGuid); campaignConnectionRow.DataView = relatedDataViews.FirstOrDefault(a => a.Guid == campaignConnectionItem.DataViewGuid); campaignConnectionRow.ActiveRequests = activeOpportunityRequests.GetValueOrNull(campaignConnectionItem.OpportunityGuid) ?? 0; campaignConnectionRow.PendingConnections = entitySetItemQry.Where(a => a.EntitySetId == campaignConnectionItem.EntitySetId).Count(); campaignConnectionRow.IsActive = campaignConnectionItem.IsActive; campaignConnectionRows.Add(campaignConnectionRow); } var qry = campaignConnectionRows.AsQueryable(); SortProperty sortProperty = gCampaigns.SortProperty; if (sortProperty != null) { qry = qry.Sort(sortProperty); } else { qry = qry.OrderBy(g => g.Name); } gCampaigns.DataSource = qry.ToList(); gCampaigns.DataBind(); }
/// <summary> /// Gets the merge object list for the current EntitySet /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="fetchCount">The fetch count.</param> /// <returns></returns> private List <object> GetMergeObjectList(RockContext rockContext, int?fetchCount = null) { int entitySetId = hfEntitySetId.Value.AsInteger(); var entitySetService = new EntitySetService(rockContext); var entitySet = entitySetService.Get(entitySetId); Dictionary <int, object> mergeObjectsDictionary = new Dictionary <int, object>(); // If this EntitySet contains IEntity Items, add those first if (entitySet.EntityTypeId.HasValue) { var qryEntity = entitySetService.GetEntityQuery(entitySetId); if (fetchCount.HasValue) { qryEntity = qryEntity.Take(fetchCount.Value); } var entityTypeCache = EntityTypeCache.Read(entitySet.EntityTypeId.Value); bool isPersonEntityType = entityTypeCache != null && entityTypeCache.Guid == Rock.SystemGuid.EntityType.PERSON.AsGuid(); bool isGroupMemberEntityType = entityTypeCache != null && entityTypeCache.Guid == Rock.SystemGuid.EntityType.GROUP_MEMBER.AsGuid(); bool combineFamilyMembers = cbCombineFamilyMembers.Visible && cbCombineFamilyMembers.Checked; if (isPersonEntityType && combineFamilyMembers) { var qryPersons = qryEntity; Guid familyGroupType = Rock.SystemGuid.GroupType.GROUPTYPE_FAMILY.AsGuid(); var qryFamilyGroupMembers = new GroupMemberService(rockContext).Queryable() .Where(a => a.Group.GroupType.Guid == familyGroupType) .Where(a => qryPersons.Any(aa => aa.Id == a.PersonId)); var qryCombined = qryFamilyGroupMembers.Join( qryPersons, m => m.PersonId, p => p.Id, (m, p) => new { GroupMember = m, Person = p }) .GroupBy(a => a.GroupMember.GroupId) .Select(x => new { GroupId = x.Key, Persons = x.Select(xx => xx.Person).Distinct() }); foreach (var combinedFamilyItem in qryCombined) { object mergeObject; string commaPersonIds = combinedFamilyItem.Persons.Select(a => a.Id).Distinct().ToList().AsDelimited(","); var primaryGroupPerson = combinedFamilyItem.Persons.FirstOrDefault() as Person; if (mergeObjectsDictionary.ContainsKey(primaryGroupPerson.Id)) { foreach (var person in combinedFamilyItem.Persons) { if (!mergeObjectsDictionary.ContainsKey(person.Id)) { primaryGroupPerson = person as Person; break; } } } if (combinedFamilyItem.Persons.Count() > 1) { var combinedPerson = primaryGroupPerson.ToJson().FromJsonOrNull <MergeTemplateCombinedPerson>(); var familyTitle = RockUdfHelper.ufnCrm_GetFamilyTitle(rockContext, null, combinedFamilyItem.GroupId, commaPersonIds, true); combinedPerson.FullName = familyTitle; var firstNameList = combinedFamilyItem.Persons.Select(a => (a as Person).FirstName).ToList(); var nickNameList = combinedFamilyItem.Persons.Select(a => (a as Person).NickName).ToList(); combinedPerson.FirstName = firstNameList.AsDelimited(", ", " & "); combinedPerson.NickName = nickNameList.AsDelimited(", ", " & "); combinedPerson.LastName = primaryGroupPerson.LastName; combinedPerson.SuffixValueId = null; combinedPerson.SuffixValue = null; mergeObject = combinedPerson; } else { mergeObject = primaryGroupPerson; } mergeObjectsDictionary.AddOrIgnore(primaryGroupPerson.Id, mergeObject); } } else if (isGroupMemberEntityType) { foreach (var groupMember in qryEntity.AsNoTracking().OfType <GroupMember>()) { var person = groupMember.Person; person.AdditionalLavaFields = new Dictionary <string, object>(); foreach (var item in groupMember.ToDictionary()) { if (item.Key == "Id") { person.AdditionalLavaFields.AddOrIgnore("GroupMemberId", item.Value); } else if (item.Key == "Guid") { person.AdditionalLavaFields.AddOrIgnore("GroupMemberGuid", item.Value.ToStringSafe()); } else { person.AdditionalLavaFields.AddOrIgnore(item.Key, item.Value); } } mergeObjectsDictionary.AddOrIgnore(groupMember.PersonId, person); } } else { foreach (var item in qryEntity.AsNoTracking()) { mergeObjectsDictionary.AddOrIgnore(item.Id, item); } } } var entitySetItemService = new EntitySetItemService(rockContext); string[] emptyJson = new string[] { string.Empty, "{}" }; var entitySetItemMergeValuesQry = entitySetItemService.GetByEntitySetId(entitySetId, true).Where(a => !emptyJson.Contains(a.AdditionalMergeValuesJson)); if (fetchCount.HasValue) { entitySetItemMergeValuesQry = entitySetItemMergeValuesQry.Take(fetchCount.Value); } // the entityId to use for NonEntity objects int nonEntityId = 1; // now, add the additional MergeValues regardless of if the EntitySet contains IEntity items or just Non-IEntity items foreach (var additionalMergeValuesItem in entitySetItemMergeValuesQry.AsNoTracking()) { object mergeObject; int entityId; if (additionalMergeValuesItem.EntityId > 0) { entityId = additionalMergeValuesItem.EntityId; } else { // not pointing to an actual EntityId, so use the nonEntityId for ti entityId = nonEntityId++; } if (mergeObjectsDictionary.ContainsKey(entityId)) { mergeObject = mergeObjectsDictionary[entityId]; } else { if (entitySet.EntityTypeId.HasValue) { // if already have real entities in our list, don't add additional items to the mergeObjectsDictionary continue; } // non-Entity merge object, so just use Dictionary mergeObject = new Dictionary <string, object>(); mergeObjectsDictionary.AddOrIgnore(entityId, mergeObject); } foreach (var additionalMergeValue in additionalMergeValuesItem.AdditionalMergeValues) { if (mergeObject is IEntity) { // add the additional fields to AdditionalLavaFields IEntity mergeEntity = (mergeObject as IEntity); mergeEntity.AdditionalLavaFields = mergeEntity.AdditionalLavaFields ?? new Dictionary <string, object>(); object mergeValueObject = additionalMergeValue.Value; mergeEntity.AdditionalLavaFields.AddOrIgnore(additionalMergeValue.Key, mergeValueObject); } else if (mergeObject is IDictionary <string, object> ) { // anonymous object with no fields yet IDictionary <string, object> nonEntityObject = mergeObject as IDictionary <string, object>; nonEntityObject.AddOrIgnore(additionalMergeValue.Key, additionalMergeValue.Value); } else { throw new Exception(string.Format("Unexpected MergeObject Type: {0}", mergeObject)); } } } var result = mergeObjectsDictionary.Select(a => a.Value); if (fetchCount.HasValue) { // make sure the result is limited to fetchCount (even though the above queries are also limited to fetch count) result = result.Take(fetchCount.Value); } return(result.ToList()); }