public void GetBookmarks() { IHistoryService service = new HistoryService(); const int userId = 290; const int postId1 = 19; const int postId2 = 709; const int postId3 = 1760; const int postId4 = 1711; bool addResult1 = service.Add(userId, postId1, false); bool addResult2 = service.Add(userId, postId2, true); bool addResult3 = service.Add(userId, postId3, true); bool addResult4 = service.Add(userId, postId4, false); var history = service.GetBookmarkList(userId); //clean up todo delete when mock is working service.DeleteHistory(service.Get(userId, postId1).Id); service.DeleteHistory(service.Get(userId, postId2).Id); service.DeleteHistory(service.Get(userId, postId3).Id); service.DeleteHistory(service.Get(userId, postId4).Id); //end of clean up Assert.True(addResult1); Assert.True(addResult2); Assert.True(addResult3); Assert.True(addResult4); Assert.Equal(2, history.Count); Assert.Equal(postId3, history[1].Postid); Assert.Equal(postId2, history[0].Postid); }
public void HistoryDeleteUserHistory() { IHistoryService service = new HistoryService(); const int userid = 290; const int postId1 = 19; const int postId2 = 709; const int postId3 = 1760; const int postId4 = 1711; bool addResult1 = service.Add(userid, postId1, false); bool addResult2 = service.Add(userid, postId2, false); bool addResult3 = service.Add(userid, postId3, true); bool addResult4 = service.Add(userid, postId4, false); var historyPre = service.GetHistoryList(userid); bool historyDeletion = service.DeleteUserHistory(userid); var historyPost = service.GetHistoryList(userid); //clean up todo delete when mock is working service.DeleteHistory(service.Get(userid, postId3).Id); //end of clean up Assert.True(addResult1); Assert.True(addResult2); Assert.True(addResult3); Assert.True(addResult4); Assert.Equal(3, historyPre.Count); Assert.True(historyDeletion); Assert.Empty(historyPost); }
public async Task Add_Should_Pass() { var repositoryMock = new Mock <IHistoryRepository>(); var mockDbSet = new Mock <DbSet <HistoryRecord> >(); repositoryMock .Setup(c => c.Histories) .Returns(mockDbSet.Object); var value = new HistoryViewModel { Operation = "add", NewValue = "5" }; var key = "Calculator[1]"; var service = new HistoryService(repositoryMock.Object); await service.Add(key, value, CancellationToken.None); var result = await service.Add(key, value, CancellationToken.None); Assert.AreEqual("add", result.Operation); Assert.AreEqual(null, result.OldValue); Assert.AreEqual("5", result.NewValue); }
public async Task Play() { TrackCollection.IsRunning = true; Stop(); var trackItem = TrackCollection.TrackCollection[TrackCollection.CurrentTrack]; var file = await StorageFile.GetFileFromPathAsync(trackItem.Path); string token = await _historyService.Add(file); Debug.WriteLine("Opening file: " + file.Path); SetActiveMusicInfo(token, trackItem); // Setting the info for windows 8 controls var resourceLoader = new ResourceLoader(); MediaControl.IsPlaying = true; MediaControl.ArtistName = trackItem.ArtistName ?? resourceLoader.GetString("UnknownArtist"); MediaControl.TrackName = trackItem.Name ?? resourceLoader.GetString("UnknownTrack"); _timeTotal = TimeSpan.Zero; _elapsedTime = TimeSpan.Zero; try { MediaControl.AlbumArt = new Uri(Locator.MusicPlayerVM.Artist.CurrentAlbumItem.Picture); } catch { // If album cover is from the internet then it's impossible to pass it to the MediaControl } TrackCollection.IsNextPossible(); TrackCollection.IsPreviousPossible(); if (TrackCollection.CanGoNext) { MediaControl.NextTrackPressed += MediaControl_NextTrackPressed; } else { MediaControl.NextTrackPressed -= MediaControl_NextTrackPressed; } if (TrackCollection.CanGoPrevious) { MediaControl.PreviousTrackPressed += MediaControl_PreviousTrackPressed; } else { MediaControl.PreviousTrackPressed -= MediaControl_PreviousTrackPressed; } }
public static void AddExitHistory(RockContext rockContext, Location location, Attendance attendeeAttendance, bool isSubroom) { HistoryService historyService = new HistoryService(rockContext); var summary = string.Format("Exited <span class=\"field-name\">{0}</span> at <span class=\"field-name\">{1}</span>", location.Name, Rock.RockDateTime.Now); if (isSubroom) { summary += string.Format(" (a subroom of <span class=\"field-name\">{0}</span>)", location.ParentLocation.Name); } History history = new History() { EntityTypeId = personEntityTypeId, EntityId = attendeeAttendance.PersonAlias.PersonId, RelatedEntityTypeId = locationEntityTypeId, RelatedEntityId = location.Id, Verb = "Exit", Summary = summary, Caption = "Exited Location", RelatedData = GetHostInfo(), CategoryId = 4 }; historyService.Add(history); InMemoryPersonStatus.RemoveFromWorship(attendeeAttendance.PersonAlias.PersonId); }
public static void AddMoveHistory(RockContext rockContext, Location location, Attendance attendeeAttendance, Person authorizedPerson, bool isSubroom) { HistoryService historyService = new HistoryService(rockContext); var moveSummary = string.Format("Moved to and Entered <span class=\"field-name\">{0}</span> at <span class=\"field-name\">{1}</span> under the authority of {2}", location.Name, Rock.RockDateTime.Now, authorizedPerson.FullName); if (isSubroom) { moveSummary += string.Format(" (a subroom of <span class=\"field-name\">{0}</span>)", location.ParentLocation.Name); } History moveHistory = new History() { EntityTypeId = personEntityTypeId, EntityId = attendeeAttendance.PersonAlias.PersonId, RelatedEntityTypeId = locationEntityTypeId, RelatedEntityId = location.Id, Verb = "Entry", Summary = moveSummary, Caption = "Moved To Location", RelatedData = GetHostInfo(), CategoryId = 4 }; historyService.Add(moveHistory); }
protected void Distribute_Click(object sender, Rock.Web.UI.Controls.RowEventArgs e) { RockContext rockContext = new RockContext(); HistoryService historyService = new HistoryService(rockContext); var keys = (( string )e.RowKeyValue).SplitDelimitedValues(); var personId = keys[0].AsInteger(); var matrixId = keys[1].AsInteger(); var scheduleGuid = keys[2].AsGuid(); AttributeMatrixItemService attributeMatrixItemService = new AttributeMatrixItemService(rockContext); var matrix = attributeMatrixItemService.Get(matrixId); matrix.LoadAttributes(); var category = new CategoryService(rockContext).Get(GetAttributeValue("HistoryCategory").AsGuid()); History history = new History() { CategoryId = category.Id, EntityTypeId = EntityTypeCache.GetId <Person>().Value, EntityId = personId, RelatedEntityTypeId = EntityTypeCache.GetId <AttributeMatrixItem>().Value, RelatedEntityId = matrixId, Verb = "Distributed", Caption = "Medication Distributed", Summary = string.Format("<span class=\"field-name\">{0}</span> was distributed at <span class=\"field-name\">{1}</span>", matrix.GetAttributeValue("Medication"), Rock.RockDateTime.Now), RelatedData = scheduleGuid.ToString() }; historyService.Add(history); rockContext.SaveChanges(); BindGrid(); }
/// <summary> /// Executes the specified workflow. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="action">The action.</param> /// <param name="entity">The entity.</param> /// <param name="errorMessages">The error messages.</param> /// <returns></returns> public override bool Execute(RockContext rockContext, WorkflowAction action, Object entity, out List <string> errorMessages) { errorMessages = new List <string>(); var personGuid = GetAttributeValue(action, "Person", true).AsGuidOrNull(); if (personGuid == null) { errorMessages.Add("Person Add History requires a valid person"); return(false); } var categoryGuid = GetAttributeValue(action, "Category").AsGuid(); var category = new CategoryService(rockContext).Get(categoryGuid); if (category == null) { errorMessages.Add("Person Add History requires a valid category"); return(false); } PersonAliasService personAliasService = new PersonAliasService(rockContext); var personAlias = personAliasService.Get(personGuid.Value); if (personAlias != null) { var person = personAlias.Person; var entityTypeId = EntityTypeCache.GetId(typeof(Rock.Model.Person)); var workflowEntityTypeId = EntityTypeCache.GetId(typeof(Rock.Model.Workflow)); var mergeFields = GetMergeFields(action); var caption = GetAttributeValue(action, "Caption").ResolveMergeFields(mergeFields); var summary = GetAttributeValue(action, "Summary").ResolveMergeFields(mergeFields); var verb = GetAttributeValue(action, "Verb").ResolveMergeFields(mergeFields); HistoryService historyService = new HistoryService(rockContext); History history = new History { Caption = caption, Summary = summary, Verb = verb, EntityId = person.Id, EntityTypeId = entityTypeId.Value, CategoryId = category.Id }; if (action?.Activity?.Workflow != null && action.Activity.WorkflowId != 0) { history.RelatedEntityTypeId = workflowEntityTypeId; history.RelatedEntityId = action.Activity.WorkflowId; } historyService.Add(history); rockContext.SaveChanges(); return(true); } else { errorMessages.Add("Person Add History requires a valid person"); return(false); } }
private async Task SendToCommunicationRecipient(Model.Communication communication, string fromPhone, Dictionary <string, object> mergeFields, Person currentPerson, List <Uri> attachmentMediaUrls, int personEntityTypeId, int communicationCategoryId, int communicationEntityTypeId, string publicAppRoot, string callbackUrl, CommunicationRecipient recipient) { using (var rockContext = new RockContext()) { try { recipient = new CommunicationRecipientService(rockContext).Get(recipient.Id); var twilioNumber = recipient.PersonAlias.Person.PhoneNumbers.GetFirstSmsNumber(); if (!string.IsNullOrWhiteSpace(twilioNumber)) { // Create merge field dictionary var mergeObjects = recipient.CommunicationMergeValues(mergeFields); string message = ResolveText(communication.SMSMessage, currentPerson, recipient, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); var response = await SendToTwilioAsync(fromPhone, callbackUrl, attachmentMediaUrls, message, twilioNumber).ConfigureAwait(false); recipient.Status = CommunicationRecipientStatus.Delivered; recipient.SendDateTime = RockDateTime.Now; recipient.TransportEntityTypeName = this.GetType().FullName; recipient.UniqueMessageId = response.Sid; try { var historyService = new HistoryService(rockContext); historyService.Add(new History { CreatedByPersonAliasId = communication.SenderPersonAliasId, EntityTypeId = personEntityTypeId, CategoryId = communicationCategoryId, EntityId = recipient.PersonAlias.PersonId, Verb = History.HistoryVerb.Sent.ConvertToString().ToUpper(), ChangeType = History.HistoryChangeType.Record.ToString(), ValueName = "SMS message", Caption = message.Truncate(200), RelatedEntityTypeId = communicationEntityTypeId, RelatedEntityId = communication.Id }); } catch (Exception ex) { ExceptionLogService.LogException(ex, null); } } else { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "No Phone Number with Messaging Enabled"; } } catch (Exception ex) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Twilio Exception: " + ex.Message; } rockContext.SaveChanges(); } }
public void HistoryAddInvalid() { IHistoryService service = new HistoryService(); var history = new History { Userid = 15, Postid = 110, isBookmark = false }; Assert.False(service.Add(history)); }
public void HistoryDeleteBookmarkValid() { IHistoryService service = new HistoryService(); const int userid = 290; const int postId = 1760; bool resultAdd = service.Add(userid, postId, true); Assert.True(resultAdd); Assert.True(service.DeleteBookmark(userid, postId)); //clean up todo delete when mock is working service.DeleteHistory(service.Get(userid, postId).Id); }
public void HistoryDeleteBookmarkInvalidUserAndPost() { IHistoryService service = new HistoryService(); const int invalidModifier = -1; const int userid = 290; const int postId = 1711; bool resultAdd = service.Add(userid, postId, true); Assert.True(resultAdd); Assert.False(service.DeleteBookmark(userid * invalidModifier, postId * invalidModifier)); //clean up todo delete when mock is working service.DeleteHistory(service.Get(userid, postId).Id); }
public void HistoryAddValid() { IHistoryService service = new HistoryService(); var history = new History { Userid = 0, Postid = 1760, isBookmark = false }; bool result = service.Add(history); Assert.True(result); //clean up todo delete when mock is working service.DeleteHistory(service.Get(0, 1760).Id); }
public void HistoryDeleteValid() { IHistoryService service = new HistoryService(); const int userId = 0; const int postId = 709; const bool isBookmark = true; var historyToAdd = new History { Userid = userId, Postid = postId, isBookmark = isBookmark }; bool resultAdd = service.Add(historyToAdd); History history = service.Get(userId, postId); Assert.True(resultAdd); Assert.True(service.HistoryExist(history.Id)); Assert.True(service.DeleteHistory(history.Id)); Assert.False(service.HistoryExist(history.Id)); }
public static void AddWithParentHistory(RockContext rockContext, Person person) { HistoryService historyService = new HistoryService(rockContext); var summary = string.Format("Moved to be with Parent at <span class=\"field-name\">{0}</span>", Rock.RockDateTime.Now); History history = new History() { EntityTypeId = personEntityTypeId, EntityId = person.Id, Verb = "Moved", Summary = summary, Caption = "Moved be with Parent", RelatedData = GetHostInfo(), CategoryId = 4 }; historyService.Add(history); AttendanceCache.SetWithParent(person.Id); }
public IActionResult CashedOut(int?WithdrawnCash, string UserName) { if (UserName != null && WithdrawnCash != null) { string resultMessage = "Insufficient funds on the card"; HistoryModel history = new HistoryModel { UsedUserName = UserName, WithdrawnCash = WithdrawnCash.Value }; if (WithdrawnCash <= CustomerService.GetBalance(UserName)) { resultMessage = "Please take your money"; history.OperationState = true; CustomerService.BalanceUpdate(WithdrawnCash.Value, UserName); } HistoryService.Add(history); return(Ok(new { history = HistoryService.GetHistory(UserName), resultMessage })); } return(BadRequest()); }
public static void AddMoveTwoWorshipHistory(RockContext rockContext, Person person) { HistoryService historyService = new HistoryService(rockContext); var summary = string.Format("Moved to Worship at <span class=\"field-name\">{0}</span>", Rock.RockDateTime.Now); History history = new History() { EntityTypeId = personEntityTypeId, EntityId = person.Id, Verb = "Moved", Summary = summary, Caption = "Moved To Worship", RelatedData = GetHostInfo(), CategoryId = 4 }; historyService.Add(history); InMemoryPersonStatus.AddToWorship(person.Id); }
public static void AddReturnToRoomHistory(RockContext rockContext, Person person) { var summary = ""; var caption = ""; HistoryService historyService = new HistoryService(rockContext); if (InMemoryPersonStatus.IsInWorship(person.Id) && InMemoryPersonStatus.IsWithParent(person.Id)) { InMemoryPersonStatus.RemoveFromWorship(person.Id); InMemoryPersonStatus.RemoveFromWithParent(person.Id); summary = string.Format("Returned from Worship and Parent at <span class=\"field-name\">{0}</span>", Rock.RockDateTime.Now); caption = "Returned from Worship and Parent"; } else if (InMemoryPersonStatus.IsInWorship(person.Id)) { InMemoryPersonStatus.RemoveFromWorship(person.Id); summary = string.Format("Returned from Worship at <span class=\"field-name\">{0}</span>", Rock.RockDateTime.Now); caption = "Returned from Worship"; } else if (InMemoryPersonStatus.IsWithParent(person.Id)) { InMemoryPersonStatus.RemoveFromWithParent(person.Id); summary = string.Format("Returned from Parent at <span class=\"field-name\">{0}</span>", Rock.RockDateTime.Now); caption = "Returned from Parent"; } if (!string.IsNullOrWhiteSpace(caption)) { History history = new History() { EntityTypeId = personEntityTypeId, EntityId = person.Id, Verb = "Returned", Summary = summary, Caption = "Returned from Worship", RelatedData = GetHostInfo(), CategoryId = 4 }; historyService.Add(history); } }
public static void AddReturnToRoomHistory(RockContext rockContext, Person person) { HistoryService historyService = new HistoryService(rockContext); if (AttendanceCache.IsWithParent(person.Id)) { AttendanceCache.RemoveWithParent(person.Id); var summary = string.Format("Returned from Parent at <span class=\"field-name\">{0}</span>", Rock.RockDateTime.Now); History history = new History() { EntityTypeId = personEntityTypeId, EntityId = person.Id, Verb = "Returned", Summary = summary, Caption = "Returned from Parent", RelatedData = GetHostInfo(), CategoryId = 4 }; historyService.Add(history); } }
public void HistoryGetValid() { IHistoryService service = new HistoryService(); const int userId = 0; const int postId = 709; const bool isBookmark = true; var history = new History { Userid = userId, Postid = postId, isBookmark = isBookmark }; bool historyAdd = service.Add(history); History historyGet = service.Get(userId, postId); //todo fix this Assert.True(historyAdd); Assert.Equal(userId, historyGet.Userid); Assert.Equal(postId, historyGet.Postid); Assert.Equal(isBookmark, historyGet.isBookmark); //clean up todo delete when mock is working service.DeleteHistory(history.Id); }
/// <summary> /// Sends the specified communication. /// </summary> /// <param name="communication">The communication.</param> /// <param name="mediumEntityTypeId">The medium entity type identifier.</param> /// <param name="mediumAttributes">The medium attributes.</param> /// <exception cref="System.NotImplementedException"></exception> public override void Send(Model.Communication communication, int mediumEntityTypeId, Dictionary <string, string> mediumAttributes) { var pushData = communication.PushData.FromJsonOrNull <PushData>(); using (var communicationRockContext = new RockContext()) { // Requery the Communication communication = new CommunicationService(communicationRockContext) .Queryable("CreatedByPersonAlias.Person") .FirstOrDefault(c => c.Id == communication.Id); bool hasPendingRecipients; if (communication != null && communication.Status == Model.CommunicationStatus.Approved && (!communication.FutureSendDateTime.HasValue || communication.FutureSendDateTime.Value.CompareTo(RockDateTime.Now) <= 0)) { var qryRecipients = new CommunicationRecipientService(communicationRockContext).Queryable(); hasPendingRecipients = qryRecipients .Where(r => r.CommunicationId == communication.Id && r.Status == Model.CommunicationRecipientStatus.Pending && r.MediumEntityTypeId.HasValue && r.MediumEntityTypeId.Value == mediumEntityTypeId) .Any(); } else { hasPendingRecipients = false; } if (hasPendingRecipients) { var currentPerson = communication.CreatedByPersonAlias?.Person; var globalAttributes = GlobalAttributesCache.Get(); string publicAppRoot = globalAttributes.GetValue("PublicApplicationRoot"); var mergeFields = Lava.LavaHelper.GetCommonMergeFields(null, currentPerson); string serverKey = GetAttributeValue("ServerKey"); var sender = new Sender(serverKey); var personEntityTypeId = EntityTypeCache.Get("Rock.Model.Person").Id; var communicationEntityTypeId = EntityTypeCache.Get("Rock.Model.Communication").Id; var communicationCategoryId = CategoryCache.Get(Rock.SystemGuid.Category.HISTORY_PERSON_COMMUNICATIONS.AsGuid(), communicationRockContext).Id; bool recipientFound = true; while (recipientFound) { // make a new rockContext per recipient var recipientRockContext = new RockContext(); var recipient = Model.Communication.GetNextPending(communication.Id, mediumEntityTypeId, recipientRockContext); if (recipient != null) { if (ValidRecipient(recipient, communication.IsBulkCommunication)) { try { var siteId = pushData?.MobileApplicationId; List <string> devices = null; if (recipient.PersonAliasId.HasValue) { int personAliasId = recipient.PersonAliasId.Value; var service = new PersonalDeviceService(recipientRockContext); devices = service.Queryable() .Where(p => p.PersonAliasId.HasValue && p.PersonAliasId.Value == personAliasId && p.NotificationsEnabled && !string.IsNullOrEmpty(p.DeviceRegistrationId)) .Where(p => !siteId.HasValue || siteId.Value == p.SiteId) .Select(p => p.DeviceRegistrationId) .ToList(); } else if (!string.IsNullOrEmpty(recipient.PersonalDevice?.DeviceRegistrationId)) { devices = new List <string> { recipient.PersonalDevice?.DeviceRegistrationId }; } if (devices != null && devices.Any()) { // Create merge field dictionary var mergeObjects = recipient.CommunicationMergeValues(mergeFields); var message = ResolveText(communication.PushMessage, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); var title = ResolveText(communication.PushTitle, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); var sound = ResolveText(communication.PushSound, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); var notification = new Message { RegistrationIds = devices.Distinct().ToList(), Notification = new FCM.Net.Notification { Title = title, Body = message, Sound = sound, }, Data = GetPushNotificationData(communication.PushOpenAction, pushData, recipient) }; ResponseContent response = Utility.AsyncHelpers.RunSync(() => sender.SendAsync(notification)); bool failed = response.MessageResponse.Failure == devices.Count || response.MessageResponse.Success == 0; var status = failed ? CommunicationRecipientStatus.Failed : CommunicationRecipientStatus.Delivered; if (failed) { recipient.StatusNote = "Firebase failed to notify devices"; } else { recipient.SendDateTime = RockDateTime.Now; } recipient.Status = status; recipient.TransportEntityTypeName = this.GetType().FullName; recipient.UniqueMessageId = response.MessageResponse.MulticastId; if (recipient.PersonAlias != null) { try { var historyService = new HistoryService(recipientRockContext); historyService.Add(new History { CreatedByPersonAliasId = communication.SenderPersonAliasId, EntityTypeId = personEntityTypeId, CategoryId = communicationCategoryId, EntityId = recipient.PersonAlias.PersonId, Verb = History.HistoryVerb.Sent.ConvertToString().ToUpper(), ChangeType = History.HistoryChangeType.Record.ToString(), ValueName = "Push Notification", Caption = message.Truncate(200), RelatedEntityTypeId = communicationEntityTypeId, RelatedEntityId = communication.Id }); } catch (Exception ex) { ExceptionLogService.LogException(ex, null); } } } else { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "No Personal Devices with Messaging Enabled"; } } catch (Exception ex) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Firebase Exception: " + ex.Message; } } recipientRockContext.SaveChanges(); } else { recipientFound = false; } } } } }
/// <summary> /// Sends the specified communication. /// </summary> /// <param name="communication">The communication.</param> /// <exception cref="System.NotImplementedException"></exception> public override void Send(Rock.Model.Communication communication) { using (var rockContext = new RockContext()) { // Requery the Communication object communication = new CommunicationService(rockContext) .Queryable("CreatedByPersonAlias.Person") .FirstOrDefault(c => c.Id == communication.Id); if (communication != null && communication.Status == Model.CommunicationStatus.Approved && communication.Recipients.Where(r => r.Status == Model.CommunicationRecipientStatus.Pending).Any() && (!communication.FutureSendDateTime.HasValue || communication.FutureSendDateTime.Value.CompareTo(RockDateTime.Now) <= 0)) { var currentPerson = communication.CreatedByPersonAlias.Person; var globalAttributes = Rock.Web.Cache.GlobalAttributesCache.Read(); var globalConfigValues = Rock.Web.Cache.GlobalAttributesCache.GetMergeFields(currentPerson); // From - if none is set, use the one in the Organization's GlobalAttributes. string fromAddress = communication.GetMediumDataValue("FromAddress"); if (string.IsNullOrWhiteSpace(fromAddress)) { fromAddress = globalAttributes.GetValue("OrganizationEmail"); } string fromName = communication.GetMediumDataValue("FromName"); if (string.IsNullOrWhiteSpace(fromName)) { fromName = globalAttributes.GetValue("OrganizationName"); } // Resolve any possible merge fields in the from address fromAddress = fromAddress.ResolveMergeFields(globalConfigValues, currentPerson); fromName = fromName.ResolveMergeFields(globalConfigValues, currentPerson); MailMessage message = new MailMessage(); message.From = new MailAddress(fromAddress, fromName); // Reply To string replyTo = communication.GetMediumDataValue("ReplyTo"); if (!string.IsNullOrWhiteSpace(replyTo)) { message.ReplyToList.Add(new MailAddress(replyTo)); } CheckSafeSender(message, globalAttributes); // CC string cc = communication.GetMediumDataValue("CC"); if (!string.IsNullOrWhiteSpace(cc)) { foreach (string ccRecipient in cc.SplitDelimitedValues()) { message.CC.Add(new MailAddress(ccRecipient)); } } // BCC string bcc = communication.GetMediumDataValue("BCC"); if (!string.IsNullOrWhiteSpace(bcc)) { foreach (string bccRecipient in bcc.SplitDelimitedValues()) { message.Bcc.Add(new MailAddress(bccRecipient)); } } message.IsBodyHtml = true; message.Priority = MailPriority.Normal; var smtpClient = GetSmtpClient(); // Add Attachments string attachmentIds = communication.GetMediumDataValue("Attachments"); if (!string.IsNullOrWhiteSpace(attachmentIds)) { var binaryFileService = new BinaryFileService(rockContext); foreach (string idVal in attachmentIds.SplitDelimitedValues()) { int binaryFileId = int.MinValue; if (int.TryParse(idVal, out binaryFileId)) { var binaryFile = binaryFileService.Get(binaryFileId); if (binaryFile != null) { message.Attachments.Add(new Attachment(binaryFile.ContentStream, binaryFile.FileName)); } } } } var historyService = new HistoryService(rockContext); var recipientService = new CommunicationRecipientService(rockContext); var personEntityTypeId = EntityTypeCache.Read("Rock.Model.Person").Id; var communicationEntityTypeId = EntityTypeCache.Read("Rock.Model.Communication").Id; var communicationCategoryId = CategoryCache.Read(Rock.SystemGuid.Category.HISTORY_PERSON_COMMUNICATIONS.AsGuid(), rockContext).Id; bool recipientFound = true; while (recipientFound) { var recipient = Rock.Model.Communication.GetNextPending(communication.Id, rockContext); if (recipient != null) { if (string.IsNullOrWhiteSpace(recipient.PersonAlias.Person.Email)) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "No Email Address"; } else { message.To.Clear(); message.Headers.Clear(); message.AlternateViews.Clear(); message.To.Add(new MailAddress(recipient.PersonAlias.Person.Email, recipient.PersonAlias.Person.FullName)); // Create merge field dictionary var mergeObjects = recipient.CommunicationMergeValues(globalConfigValues); // Subject message.Subject = communication.Subject.ResolveMergeFields(mergeObjects, currentPerson); // Add any additional headers that specific SMTP provider needs AddAdditionalHeaders(message, recipient); // Add text view first as last view is usually treated as the preferred view by email readers (gmail) string plainTextBody = Rock.Communication.Medium.Email.ProcessTextBody(communication, globalAttributes, mergeObjects, currentPerson); if (!string.IsNullOrWhiteSpace(plainTextBody)) { AlternateView plainTextView = AlternateView.CreateAlternateViewFromString(plainTextBody, new System.Net.Mime.ContentType(MediaTypeNames.Text.Plain)); message.AlternateViews.Add(plainTextView); } // Add Html view string htmlBody = Rock.Communication.Medium.Email.ProcessHtmlBody(communication, globalAttributes, mergeObjects, currentPerson); if (!string.IsNullOrWhiteSpace(htmlBody)) { AlternateView htmlView = AlternateView.CreateAlternateViewFromString(htmlBody, new System.Net.Mime.ContentType(MediaTypeNames.Text.Html)); message.AlternateViews.Add(htmlView); } try { smtpClient.Send(message); recipient.Status = CommunicationRecipientStatus.Delivered; string statusNote = StatusNote; if (!string.IsNullOrWhiteSpace(statusNote)) { recipient.StatusNote = statusNote; } recipient.TransportEntityTypeName = this.GetType().FullName; historyService.Add(new History { CreatedByPersonAliasId = communication.SenderPersonAliasId, EntityTypeId = personEntityTypeId, CategoryId = communicationCategoryId, EntityId = recipient.PersonAlias.PersonId, Summary = string.Format("Sent communication from <span class='field-value'>{0}</span>.", message.From.DisplayName), Caption = message.Subject, RelatedEntityTypeId = communicationEntityTypeId, RelatedEntityId = communication.Id }); } catch (Exception ex) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "SMTP Exception: " + ex.Message; } } rockContext.SaveChanges(); } else { recipientFound = false; } } } } }
/// <summary> /// Sends the specified communication. /// </summary> /// <param name="communication">The communication.</param> /// <param name="mediumEntityTypeId">The medium entity type identifier.</param> /// <param name="mediumAttributes">The medium attributes.</param> public override void Send(Model.Communication communication, int mediumEntityTypeId, Dictionary <string, string> mediumAttributes) { using (var communicationRockContext = new RockContext()) { // Requery the Communication communication = new CommunicationService(communicationRockContext).Get(communication.Id); bool hasPendingRecipients; if (communication != null && communication.Status == Model.CommunicationStatus.Approved && (!communication.FutureSendDateTime.HasValue || communication.FutureSendDateTime.Value.CompareTo(RockDateTime.Now) <= 0)) { var qryRecipients = new CommunicationRecipientService(communicationRockContext).Queryable(); hasPendingRecipients = qryRecipients .Where(r => r.CommunicationId == communication.Id && r.Status == Model.CommunicationRecipientStatus.Pending && r.MediumEntityTypeId.HasValue && r.MediumEntityTypeId.Value == mediumEntityTypeId) .Any(); } else { hasPendingRecipients = false; } if (hasPendingRecipients) { var currentPerson = communication.CreatedByPersonAlias?.Person; var globalAttributes = GlobalAttributesCache.Get(); string publicAppRoot = globalAttributes.GetValue("PublicApplicationRoot").EnsureTrailingForwardslash(); var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(null, currentPerson); string fromPhone = communication.SMSFromDefinedValue?.Value; if (string.IsNullOrWhiteSpace(fromPhone)) { // just in case we got this far without a From Number, throw an exception throw new Exception("A From Number was not provided for communication: " + communication.Id.ToString()); } if (!string.IsNullOrWhiteSpace(fromPhone)) { int?throttlingWaitTimeMS = null; if (this.IsLongCodePhoneNumber(fromPhone)) { throttlingWaitTimeMS = this.GetAttributeValue("Long-CodeThrottling").AsIntegerOrNull(); } string accountSid = GetAttributeValue("SID"); string authToken = GetAttributeValue("Token"); TwilioClient.Init(accountSid, authToken); var personEntityTypeId = EntityTypeCache.Get("Rock.Model.Person").Id; var communicationEntityTypeId = EntityTypeCache.Get("Rock.Model.Communication").Id; var communicationCategoryId = CategoryCache.Get(Rock.SystemGuid.Category.HISTORY_PERSON_COMMUNICATIONS.AsGuid(), communicationRockContext).Id; string callbackUrl = publicAppRoot + "Webhooks/Twilio.ashx"; var smsAttachmentsBinaryFileIdList = communication.GetAttachmentBinaryFileIds(CommunicationType.SMS); List <Uri> attachmentMediaUrls = new List <Uri>(); if (smsAttachmentsBinaryFileIdList.Any()) { attachmentMediaUrls = this.GetAttachmentMediaUrls(new BinaryFileService(communicationRockContext).GetByIds(smsAttachmentsBinaryFileIdList)); } bool recipientFound = true; while (recipientFound) { // make a new rockContext per recipient var recipientRockContext = new RockContext(); var recipient = Model.Communication.GetNextPending(communication.Id, mediumEntityTypeId, recipientRockContext); if (recipient != null) { if (ValidRecipient(recipient, communication.IsBulkCommunication)) { try { var phoneNumber = recipient.PersonAlias.Person.PhoneNumbers .Where(p => p.IsMessagingEnabled) .FirstOrDefault(); if (phoneNumber != null) { // Create merge field dictionary var mergeObjects = recipient.CommunicationMergeValues(mergeFields); string message = ResolveText(communication.SMSMessage, currentPerson, recipient, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); string twilioNumber = phoneNumber.Number; if (!string.IsNullOrWhiteSpace(phoneNumber.CountryCode)) { twilioNumber = "+" + phoneNumber.CountryCode + phoneNumber.Number; } MessageResource response = SendToTwilio(fromPhone, callbackUrl, attachmentMediaUrls, message, twilioNumber); recipient.Status = CommunicationRecipientStatus.Delivered; recipient.SendDateTime = RockDateTime.Now; recipient.TransportEntityTypeName = this.GetType().FullName; recipient.UniqueMessageId = response.Sid; try { var historyService = new HistoryService(recipientRockContext); historyService.Add(new History { CreatedByPersonAliasId = communication.SenderPersonAliasId, EntityTypeId = personEntityTypeId, CategoryId = communicationCategoryId, EntityId = recipient.PersonAlias.PersonId, Verb = History.HistoryVerb.Sent.ConvertToString().ToUpper(), ChangeType = History.HistoryChangeType.Record.ToString(), ValueName = "SMS message", Caption = message.Truncate(200), RelatedEntityTypeId = communicationEntityTypeId, RelatedEntityId = communication.Id }); } catch (Exception ex) { ExceptionLogService.LogException(ex, null); } } else { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "No Phone Number with Messaging Enabled"; } } catch (Exception ex) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Twilio Exception: " + ex.Message; } } recipientRockContext.SaveChanges(); if (throttlingWaitTimeMS.HasValue) { System.Threading.Tasks.Task.Delay(throttlingWaitTimeMS.Value).Wait(); } } else { recipientFound = false; } } } } } }
/// <summary> /// Sends the specified communication. /// </summary> /// <param name="communication">The communication.</param> /// <exception cref="System.NotImplementedException"></exception> public override void Send(Rock.Model.Communication communication) { var rockContext = new RockContext(); // Requery the Communication communication = new CommunicationService(rockContext).Get(communication.Id); if (communication != null && communication.Status == Model.CommunicationStatus.Approved && communication.HasPendingRecipients(rockContext) && (!communication.FutureSendDateTime.HasValue || communication.FutureSendDateTime.Value.CompareTo(RockDateTime.Now) <= 0)) { string fromPhone = string.Empty; string fromValue = communication.GetMediumDataValue("FromValue"); int fromValueId = int.MinValue; if (int.TryParse(fromValue, out fromValueId)) { fromPhone = DefinedValueCache.Read(fromValueId, rockContext).Value; } if (!string.IsNullOrWhiteSpace(fromPhone)) { string accountSid = GetAttributeValue("SID"); string authToken = GetAttributeValue("Token"); var twilio = new TwilioRestClient(accountSid, authToken); var historyService = new HistoryService(rockContext); var recipientService = new CommunicationRecipientService(rockContext); var personEntityTypeId = EntityTypeCache.Read("Rock.Model.Person").Id; var communicationEntityTypeId = EntityTypeCache.Read("Rock.Model.Communication").Id; var communicationCategoryId = CategoryCache.Read(Rock.SystemGuid.Category.HISTORY_PERSON_COMMUNICATIONS.AsGuid(), rockContext).Id; var globalConfigValues = GlobalAttributesCache.GetMergeFields(null); bool recipientFound = true; while (recipientFound) { var recipient = Rock.Model.Communication.GetNextPending(communication.Id, rockContext); if (recipient != null) { try { var phoneNumber = recipient.PersonAlias.Person.PhoneNumbers .Where(p => p.IsMessagingEnabled) .FirstOrDefault(); if (phoneNumber != null) { // Create merge field dictionary var mergeObjects = recipient.CommunicationMergeValues(globalConfigValues); string message = communication.GetMediumDataValue("Message"); // convert any special microsoft word characters to normal chars so they don't look funny (for example "Hey “double-quotes†from ‘single quote’") message = message.ReplaceWordChars(); message = message.ResolveMergeFields(mergeObjects); string twilioNumber = phoneNumber.Number; if (!string.IsNullOrWhiteSpace(phoneNumber.CountryCode)) { twilioNumber = "+" + phoneNumber.CountryCode + phoneNumber.Number; } var globalAttributes = Rock.Web.Cache.GlobalAttributesCache.Read(); string callbackUrl = globalAttributes.GetValue("PublicApplicationRoot") + "Webhooks/Twilio.ashx"; var response = twilio.SendMessage(fromPhone, twilioNumber, message, callbackUrl); recipient.Status = CommunicationRecipientStatus.Delivered; recipient.TransportEntityTypeName = this.GetType().FullName; recipient.UniqueMessageId = response.Sid; try { historyService.Add(new History { CreatedByPersonAliasId = communication.SenderPersonAliasId, EntityTypeId = personEntityTypeId, CategoryId = communicationCategoryId, EntityId = recipient.PersonAlias.PersonId, Summary = "Sent SMS message.", Caption = message.Truncate(200), RelatedEntityTypeId = communicationEntityTypeId, RelatedEntityId = communication.Id }); } catch (Exception ex) { ExceptionLogService.LogException(ex, null); } } else { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "No Phone Number with Messaging Enabled"; } } catch (Exception ex) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Twilio Exception: " + ex.Message; } rockContext.SaveChanges(); } else { recipientFound = false; } } } } }
/// <summary> /// Sends the specified communication from the Communication Wizard in Rock. /// </summary> /// <param name="communication">The communication.</param> /// <param name="mediumEntityTypeId">The medium entity type identifier.</param> /// <param name="mediumAttributes">The medium attributes.</param> /// <exception cref="System.NotImplementedException"></exception> public override void Send(Model.Communication communication, int mediumEntityTypeId, Dictionary <string, string> mediumAttributes) { using (var communicationRockContext = new RockContext()) { // Requery the Communication communication = new CommunicationService(communicationRockContext) .Queryable("CreatedByPersonAlias.Person") .FirstOrDefault(c => c.Id == communication.Id); bool hasPendingRecipients; if (communication != null && communication.Status == Model.CommunicationStatus.Approved && (!communication.FutureSendDateTime.HasValue || communication.FutureSendDateTime.Value.CompareTo(RockDateTime.Now) <= 0)) { var qryRecipients = new CommunicationRecipientService(communicationRockContext).Queryable(); hasPendingRecipients = qryRecipients .Where(r => r.CommunicationId == communication.Id && r.Status == Model.CommunicationRecipientStatus.Pending && r.MediumEntityTypeId.HasValue && r.MediumEntityTypeId.Value == mediumEntityTypeId) .Any(); } else { hasPendingRecipients = false; } if (hasPendingRecipients) { var currentPerson = communication.CreatedByPersonAlias?.Person; var globalAttributes = GlobalAttributesCache.Get(); string publicAppRoot = globalAttributes.GetValue("PublicApplicationRoot").EnsureTrailingForwardslash(); var mergeFields = Lava.LavaHelper.GetCommonMergeFields(null, currentPerson); var personEntityTypeId = EntityTypeCache.Get("Rock.Model.Person").Id; var communicationEntityTypeId = EntityTypeCache.Get("Rock.Model.Communication").Id; var communicationCategoryId = CategoryCache.Get(Rock.SystemGuid.Category.HISTORY_PERSON_COMMUNICATIONS.AsGuid(), communicationRockContext).Id; bool recipientFound = true; while (recipientFound) { // make a new rockContext per recipient var recipientRockContext = new RockContext(); var recipient = Model.Communication.GetNextPending(communication.Id, mediumEntityTypeId, recipientRockContext); if (recipient != null) { if (ValidRecipient(recipient, communication.IsBulkCommunication)) { if (recipient.PersonAliasId.HasValue) { try { var mergeObjects = recipient.CommunicationMergeValues(mergeFields); var message = ResolveText(communication.PushMessage, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); var title = ResolveText(communication.PushTitle, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); var sound = ResolveText(communication.PushSound, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); var data = ResolveText(communication.PushData, currentPerson, communication.EnabledLavaCommands, mergeFields, publicAppRoot); var jsonData = Newtonsoft.Json.JsonConvert.DeserializeObject <PushData>(data); var url = jsonData.Url; string appId = GetAttributeValue("AppId"); string restApiKey = GetAttributeValue("RestAPIKey"); OneSignalClient client = new OneSignalClient(restApiKey); var options = new NotificationCreateOptions { AppId = new Guid(appId), IncludeExternalUserIds = new List <string> { recipient.PersonAliasId.ToString() } }; options.Headings.Add(LanguageCodes.English, title); options.Contents.Add(LanguageCodes.English, message); options.Url = url; NotificationCreateResult response = client.Notifications.Create(options); bool failed = !string.IsNullOrWhiteSpace(response.Error); var status = failed ? CommunicationRecipientStatus.Failed : CommunicationRecipientStatus.Delivered; if (failed) { recipient.StatusNote = "OneSignal failed to notify devices"; } else { recipient.SendDateTime = RockDateTime.Now; } recipient.Status = status; recipient.TransportEntityTypeName = this.GetType().FullName; recipient.UniqueMessageId = response.Id; try { var historyService = new HistoryService(recipientRockContext); historyService.Add(new History { CreatedByPersonAliasId = communication.SenderPersonAliasId, EntityTypeId = personEntityTypeId, CategoryId = communicationCategoryId, EntityId = recipient.PersonAlias.PersonId, Verb = History.HistoryVerb.Sent.ConvertToString().ToUpper(), ChangeType = History.HistoryChangeType.Record.ToString(), ValueName = "Push Notification", Caption = message.Truncate(200), RelatedEntityTypeId = communicationEntityTypeId, RelatedEntityId = communication.Id }); } catch (Exception ex) { ExceptionLogService.LogException(ex, null); } } catch (Exception ex) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "OneSignal Exception: " + ex.Message; } } } recipientRockContext.SaveChanges(); } else { recipientFound = false; } } } } }
/// <summary> /// Job that will run quick SQL queries on a schedule. /// /// 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) { JobDataMap dataMap = context.JobDetail.JobDataMap; Guid?entryWorkflowType = dataMap.GetString("EraEntryWorkflow").AsGuidOrNull(); Guid?exitWorkflowType = dataMap.GetString("EraExitWorkflow").AsGuidOrNull(); bool updateVisitDates = dataMap.GetBooleanValue("SetVisitDates"); int commandTimeout = dataMap.GetString("CommandTimeout").AsIntegerOrNull() ?? 3600; // configuration // // giving int exitGivingCount = 1; // attendance int exitAttendanceCountShort = 1; int exitAttendanceCountLong = 8; // get era dataset from stored proc var resultContext = new RockContext(); var eraAttribute = AttributeCache.Get(SystemGuid.Attribute.PERSON_ERA_CURRENTLY_AN_ERA.AsGuid()); var eraStartAttribute = AttributeCache.Get(SystemGuid.Attribute.PERSON_ERA_START_DATE.AsGuid()); var eraEndAttribute = AttributeCache.Get(SystemGuid.Attribute.PERSON_ERA_END_DATE.AsGuid()); if (eraAttribute == null || eraStartAttribute == null || eraEndAttribute == null) { throw new Exception("Family analytic attributes could not be found"); } resultContext.Database.CommandTimeout = commandTimeout; context.UpdateLastStatusMessage("Getting Family Analytics Era Dataset..."); var results = resultContext.Database.SqlQuery <EraResult>("spCrm_FamilyAnalyticsEraDataset").ToList(); int personEntityTypeId = EntityTypeCache.Get("Rock.Model.Person").Id; int attributeEntityTypeId = EntityTypeCache.Get("Rock.Model.Attribute").Id; int eraAttributeId = eraAttribute.Id; int personAnalyticsCategoryId = CategoryCache.Get(SystemGuid.Category.HISTORY_PERSON_ANALYTICS.AsGuid()).Id; int progressPosition = 0; int progressTotal = results.Count; foreach (var result in results) { progressPosition++; // create new rock context for each family (https://weblog.west-wind.com/posts/2014/Dec/21/Gotcha-Entity-Framework-gets-slow-in-long-Iteration-Loops) RockContext updateContext = new RockContext(); updateContext.SourceOfChange = SOURCE_OF_CHANGE; updateContext.Database.CommandTimeout = commandTimeout; var attributeValueService = new AttributeValueService(updateContext); var historyService = new HistoryService(updateContext); // if era ensure it still meets requirements if (result.IsEra) { // This process will not remove eRA status from a single inactive family member if the family is considered eRA, even if the person record status is inactive. // It removes eRA status from all family members if the family no longer meets the eRA requirements. if (result.ExitGiftCountDuration < exitGivingCount && result.ExitAttendanceCountDurationShort < exitAttendanceCountShort && result.ExitAttendanceCountDurationLong < exitAttendanceCountLong) { // exit era (delete attribute value from each person in family) var family = new GroupService(updateContext).Queryable("Members, Members.Person").AsNoTracking().Where(m => m.Id == result.FamilyId).FirstOrDefault(); if (family != null) { foreach (var person in family.Members.Select(m => m.Person)) { // remove the era flag var eraAttributeValue = attributeValueService.Queryable().Where(v => v.AttributeId == eraAttribute.Id && v.EntityId == person.Id).FirstOrDefault(); if (eraAttributeValue != null) { attributeValueService.Delete(eraAttributeValue); } // set end date var eraEndAttributeValue = attributeValueService.Queryable().Where(v => v.AttributeId == eraEndAttribute.Id && v.EntityId == person.Id).FirstOrDefault(); if (eraEndAttributeValue == null) { eraEndAttributeValue = new AttributeValue(); eraEndAttributeValue.EntityId = person.Id; eraEndAttributeValue.AttributeId = eraEndAttribute.Id; attributeValueService.Add(eraEndAttributeValue); } eraEndAttributeValue.Value = RockDateTime.Now.ToString(); // add a history record if (personAnalyticsCategoryId != 0 && personEntityTypeId != 0 && attributeEntityTypeId != 0 && eraAttributeId != 0) { History historyRecord = new History(); historyService.Add(historyRecord); historyRecord.EntityTypeId = personEntityTypeId; historyRecord.EntityId = person.Id; historyRecord.CreatedDateTime = RockDateTime.Now; historyRecord.CreatedByPersonAliasId = person.PrimaryAliasId; historyRecord.Caption = "eRA"; historyRecord.Verb = "EXITED"; historyRecord.ChangeType = History.HistoryChangeType.Attribute.ConvertToString(); historyRecord.ValueName = "eRA"; historyRecord.NewValue = "Exited"; historyRecord.RelatedEntityTypeId = attributeEntityTypeId; historyRecord.RelatedEntityId = eraAttributeId; historyRecord.CategoryId = personAnalyticsCategoryId; historyRecord.SourceOfChange = SOURCE_OF_CHANGE; } updateContext.SaveChanges(); } // launch exit workflow if (exitWorkflowType.HasValue) { LaunchWorkflow(exitWorkflowType.Value, family); } } } } else { // entered era var family = new GroupService(updateContext).Queryable("Members").AsNoTracking().Where(m => m.Id == result.FamilyId).FirstOrDefault(); if (family != null) { // The stored procedure does not filter out inactive users because we want to exit the family from eRA if they are not active. // So check the status for each person here and do not add the person if they are inactive. If the system defined value for // an inactive person is not available then use "-1" as every record should pass != -1. int inactiveStatusId = DefinedValueCache.GetId(Rock.SystemGuid.DefinedValue.PERSON_RECORD_STATUS_INACTIVE.AsGuid()) ?? -1; var familyMembers = family.Members .Where(m => !m.Person.IsDeceased) .Where(m => m.Person.RecordStatusValueId != inactiveStatusId) .Select(m => m.Person); foreach (var person in familyMembers) { // set era attribute to true var eraAttributeValue = attributeValueService.Queryable().Where(v => v.AttributeId == eraAttribute.Id && v.EntityId == person.Id).FirstOrDefault(); if (eraAttributeValue == null) { eraAttributeValue = new AttributeValue(); eraAttributeValue.EntityId = person.Id; eraAttributeValue.AttributeId = eraAttribute.Id; attributeValueService.Add(eraAttributeValue); } eraAttributeValue.Value = bool.TrueString; // add start date var eraStartAttributeValue = attributeValueService.Queryable().Where(v => v.AttributeId == eraStartAttribute.Id && v.EntityId == person.Id).FirstOrDefault(); if (eraStartAttributeValue == null) { eraStartAttributeValue = new AttributeValue(); eraStartAttributeValue.EntityId = person.Id; eraStartAttributeValue.AttributeId = eraStartAttribute.Id; attributeValueService.Add(eraStartAttributeValue); } eraStartAttributeValue.Value = RockDateTime.Now.ToString(); // delete end date if it exists var eraEndAttributeValue = attributeValueService.Queryable().Where(v => v.AttributeId == eraEndAttribute.Id && v.EntityId == person.Id).FirstOrDefault(); if (eraEndAttributeValue != null) { attributeValueService.Delete(eraEndAttributeValue); } // add a history record if (personAnalyticsCategoryId != 0 && personEntityTypeId != 0 && attributeEntityTypeId != 0 && eraAttributeId != 0) { History historyRecord = new History(); historyService.Add(historyRecord); historyRecord.EntityTypeId = personEntityTypeId; historyRecord.EntityId = person.Id; historyRecord.CreatedDateTime = RockDateTime.Now; historyRecord.CreatedByPersonAliasId = person.PrimaryAliasId; historyRecord.Caption = "eRA"; historyRecord.Verb = "ENTERED"; historyRecord.ChangeType = History.HistoryChangeType.Attribute.ConvertToString(); historyRecord.RelatedEntityTypeId = attributeEntityTypeId; historyRecord.RelatedEntityId = eraAttributeId; historyRecord.CategoryId = personAnalyticsCategoryId; historyRecord.SourceOfChange = SOURCE_OF_CHANGE; } updateContext.SaveChanges(); } // launch entry workflow if (entryWorkflowType.HasValue) { LaunchWorkflow(entryWorkflowType.Value, family); } } } // update stats context.UpdateLastStatusMessage($"Updating eRA {progressPosition} of {progressTotal}"); } // load giving attributes context.UpdateLastStatusMessage("Updating Giving..."); resultContext.Database.ExecuteSqlCommand("spCrm_FamilyAnalyticsGiving"); // load attendance attributes context.UpdateLastStatusMessage("Updating Attendance..."); resultContext.Database.ExecuteSqlCommand("spCrm_FamilyAnalyticsAttendance"); // process visit dates if (updateVisitDates) { context.UpdateLastStatusMessage("Updating Visit Dates..."); resultContext.Database.ExecuteSqlCommand("spCrm_FamilyAnalyticsUpdateVisitDates"); } context.UpdateLastStatusMessage(""); }
/// <summary> /// Sends the specified communication. /// </summary> /// <param name="communication">The communication.</param> /// <exception cref="System.NotImplementedException"></exception> public override void Send(Rock.Model.Communication communication) { using (var communicationRockContext = new RockContext()) { // Requery the Communication object in case we need to load any properties from the database communication = new CommunicationService(communicationRockContext) .Queryable("CreatedByPersonAlias.Person") .FirstOrDefault(c => c.Id == communication.Id); bool hasPendingRecipients; if (communication != null && communication.Status == Model.CommunicationStatus.Approved && (!communication.FutureSendDateTime.HasValue || communication.FutureSendDateTime.Value.CompareTo(RockDateTime.Now) <= 0)) { var qryRecipients = new CommunicationRecipientService(communicationRockContext).Queryable(); hasPendingRecipients = qryRecipients.Where(a => a.CommunicationId == communication.Id).Where(r => r.Status == Model.CommunicationRecipientStatus.Pending).Any(); } else { hasPendingRecipients = false; } if (hasPendingRecipients) { var currentPerson = communication.CreatedByPersonAlias.Person; var globalAttributes = Rock.Web.Cache.GlobalAttributesCache.Read(); var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(null, currentPerson); // From - if none is set, use the one in the Organization's GlobalAttributes. string fromAddress = communication.GetMediumDataValue("FromAddress"); if (string.IsNullOrWhiteSpace(fromAddress)) { fromAddress = globalAttributes.GetValue("OrganizationEmail"); } string fromName = communication.GetMediumDataValue("FromName"); if (string.IsNullOrWhiteSpace(fromName)) { fromName = globalAttributes.GetValue("OrganizationName"); } // Resolve any possible merge fields in the from address fromAddress = fromAddress.ResolveMergeFields(mergeFields, currentPerson, communication.EnabledLavaCommands); fromName = fromName.ResolveMergeFields(mergeFields, currentPerson, communication.EnabledLavaCommands); MailMessage message = new MailMessage(); message.From = new MailAddress(fromAddress, fromName); // Reply To try { string replyTo = communication.GetMediumDataValue("ReplyTo"); if (!string.IsNullOrWhiteSpace(replyTo)) { message.ReplyToList.Add(new MailAddress(replyTo)); } } catch { } CheckSafeSender(message, globalAttributes); // CC string cc = communication.GetMediumDataValue("CC"); if (!string.IsNullOrWhiteSpace(cc)) { foreach (string ccRecipient in cc.SplitDelimitedValues()) { message.CC.Add(new MailAddress(ccRecipient)); } } // BCC string bcc = communication.GetMediumDataValue("BCC"); if (!string.IsNullOrWhiteSpace(bcc)) { foreach (string bccRecipient in bcc.SplitDelimitedValues()) { message.Bcc.Add(new MailAddress(bccRecipient)); } } message.IsBodyHtml = true; message.Priority = MailPriority.Normal; using (var smtpClient = GetSmtpClient()) { // Add Attachments var attachments = new List <BinaryFile>(); string attachmentIds = communication.GetMediumDataValue("Attachments"); if (!string.IsNullOrWhiteSpace(attachmentIds)) { var binaryFileService = new BinaryFileService(communicationRockContext); foreach (int binaryFileId in attachmentIds.SplitDelimitedValues().AsIntegerList()) { var binaryFile = binaryFileService.Get(binaryFileId); if (binaryFile != null) { attachments.Add(binaryFile); } } } var personEntityTypeId = EntityTypeCache.Read("Rock.Model.Person").Id; var communicationEntityTypeId = EntityTypeCache.Read("Rock.Model.Communication").Id; var communicationCategoryId = CategoryCache.Read(Rock.SystemGuid.Category.HISTORY_PERSON_COMMUNICATIONS.AsGuid(), communicationRockContext).Id; bool recipientFound = true; while (recipientFound) { // make a new rockContext per recipient so that DbChangeTracker doesn't get gummed up on large communications var recipientRockContext = new RockContext(); var recipient = Rock.Model.Communication.GetNextPending(communication.Id, recipientRockContext); if (recipient != null) { if (string.IsNullOrWhiteSpace(recipient.PersonAlias.Person.Email)) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "No Email Address"; } else { try { message.To.Clear(); message.Headers.Clear(); message.AlternateViews.Clear(); message.To.Add(new MailAddress(recipient.PersonAlias.Person.Email, recipient.PersonAlias.Person.FullName)); // Create merge field dictionary var mergeObjects = recipient.CommunicationMergeValues(mergeFields); // Subject message.Subject = communication.Subject.ResolveMergeFields(mergeObjects, currentPerson, communication.EnabledLavaCommands); // convert any special microsoft word characters to normal chars so they don't look funny (for example "Hey “double-quotes†from ‘single quote’") message.Subject = message.Subject.ReplaceWordChars(); // Add text view first as last view is usually treated as the preferred view by email readers (gmail) string plainTextBody = Rock.Communication.Medium.Email.ProcessTextBody(communication, globalAttributes, mergeObjects, currentPerson); // convert any special microsoft word characters to normal chars so they don't look funny plainTextBody = plainTextBody.ReplaceWordChars(); if (!string.IsNullOrWhiteSpace(plainTextBody)) { AlternateView plainTextView = AlternateView.CreateAlternateViewFromString(plainTextBody, new System.Net.Mime.ContentType(MediaTypeNames.Text.Plain)); message.AlternateViews.Add(plainTextView); } // Add Html view string htmlBody = Rock.Communication.Medium.Email.ProcessHtmlBody(communication, globalAttributes, mergeObjects, currentPerson); // convert any special microsoft word characters to normal chars so they don't look funny htmlBody = htmlBody.ReplaceWordChars(); if (!string.IsNullOrWhiteSpace(htmlBody)) { AlternateView htmlView = AlternateView.CreateAlternateViewFromString(htmlBody, new System.Net.Mime.ContentType(MediaTypeNames.Text.Html)); message.AlternateViews.Add(htmlView); } // Add any additional headers that specific SMTP provider needs var metaData = new Dictionary <string, string>(); metaData.Add("communication_recipient_guid", recipient.Guid.ToString()); AddAdditionalHeaders(message, metaData); // Recreate the attachments message.Attachments.Clear(); if (attachments.Any()) { foreach (var attachment in attachments) { message.Attachments.Add(new Attachment(attachment.ContentStream, attachment.FileName)); } } smtpClient.Send(message); recipient.Status = CommunicationRecipientStatus.Delivered; string statusNote = StatusNote; if (!string.IsNullOrWhiteSpace(statusNote)) { recipient.StatusNote = statusNote; } recipient.TransportEntityTypeName = this.GetType().FullName; var historyService = new HistoryService(recipientRockContext); historyService.Add(new History { CreatedByPersonAliasId = communication.SenderPersonAliasId, EntityTypeId = personEntityTypeId, CategoryId = communicationCategoryId, EntityId = recipient.PersonAlias.PersonId, Summary = string.Format("Sent communication from <span class='field-value'>{0}</span>.", message.From.DisplayName), Caption = message.Subject, RelatedEntityTypeId = communicationEntityTypeId, RelatedEntityId = communication.Id }); } catch (Exception ex) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Exception: " + ex.Message; } } recipientRockContext.SaveChanges(); } else { recipientFound = false; } } } } } }
/// <summary> /// Sends the specified communication. /// </summary>Medi /// <param name="communication">The communication.</param> /// <param name="mediumEntityTypeId">The medium entity type identifier.</param> /// <param name="mediumAttributes">The medium attributes.</param> public override void Send(Rock.Model.Communication communication, int mediumEntityTypeId, Dictionary <string, string> mediumAttributes) { using (var communicationRockContext = new RockContext()) { // Requery the Communication communication = new CommunicationService(communicationRockContext) .Queryable().Include(a => a.CreatedByPersonAlias.Person).Include(a => a.CommunicationTemplate) .FirstOrDefault(c => c.Id == communication.Id); bool hasPendingRecipients; if (communication != null && communication.Status == Model.CommunicationStatus.Approved && (!communication.FutureSendDateTime.HasValue || communication.FutureSendDateTime.Value.CompareTo(RockDateTime.Now) <= 0)) { var qryRecipients = new CommunicationRecipientService(communicationRockContext).Queryable(); hasPendingRecipients = qryRecipients .Where(r => r.CommunicationId == communication.Id && r.Status == Model.CommunicationRecipientStatus.Pending && r.MediumEntityTypeId.HasValue && r.MediumEntityTypeId.Value == mediumEntityTypeId) .Any(); } else { hasPendingRecipients = false; } if (hasPendingRecipients) { var currentPerson = communication.CreatedByPersonAlias?.Person; var globalAttributes = Rock.Web.Cache.GlobalAttributesCache.Read(); string publicAppRoot = globalAttributes.GetValue("PublicApplicationRoot").EnsureTrailingForwardslash(); var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(null, currentPerson); var cssInliningEnabled = communication.CommunicationTemplate?.CssInliningEnabled ?? false; // From - if none is set, use the one in the Organization's GlobalAttributes. string fromAddress = communication.FromEmail; if (string.IsNullOrWhiteSpace(fromAddress)) { fromAddress = globalAttributes.GetValue("OrganizationEmail"); } string fromName = communication.FromName; if (string.IsNullOrWhiteSpace(fromName)) { fromName = globalAttributes.GetValue("OrganizationName"); } // Resolve any possible merge fields in the from address fromAddress = fromAddress.ResolveMergeFields(mergeFields, currentPerson, communication.EnabledLavaCommands); fromName = fromName.ResolveMergeFields(mergeFields, currentPerson, communication.EnabledLavaCommands); MailMessage message = new MailMessage(); // Reply To try { string replyTo = communication.ReplyToEmail; if (!string.IsNullOrWhiteSpace(replyTo)) { // Resolve any possible merge fields in the replyTo address message.ReplyToList.Add(new MailAddress(replyTo.ResolveMergeFields(mergeFields, currentPerson))); } } catch { } message.IsBodyHtml = true; message.Priority = MailPriority.Normal; using (var smtpClient = GetSmtpClient()) { var personEntityTypeId = EntityTypeCache.Read("Rock.Model.Person").Id; var communicationEntityTypeId = EntityTypeCache.Read("Rock.Model.Communication").Id; var communicationCategoryId = CategoryCache.Read(Rock.SystemGuid.Category.HISTORY_PERSON_COMMUNICATIONS.AsGuid(), communicationRockContext).Id; bool recipientFound = true; while (recipientFound) { // make a new rockContext per recipient var recipientRockContext = new RockContext(); var recipient = Rock.Model.Communication.GetNextPending(communication.Id, mediumEntityTypeId, recipientRockContext); if (recipient != null) { if (ValidRecipient(recipient, communication.IsBulkCommunication)) { try { message.To.Clear(); message.CC.Clear(); message.Bcc.Clear(); message.Headers.Clear(); message.AlternateViews.Clear(); // Set From/To and check safe sender message.From = new MailAddress(fromAddress, fromName); message.To.Add(new MailAddress(recipient.PersonAlias.Person.Email, recipient.PersonAlias.Person.FullName)); CheckSafeSender(message, globalAttributes); // Create merge field dictionary var mergeObjects = recipient.CommunicationMergeValues(mergeFields); // CC string cc = communication.CCEmails; if (!string.IsNullOrWhiteSpace(cc)) { // Resolve any possible merge fields in the cc address cc = cc.ResolveMergeFields(mergeObjects, currentPerson); foreach (string ccRecipient in cc.SplitDelimitedValues()) { message.CC.Add(new MailAddress(ccRecipient)); } } // BCC string bcc = communication.BCCEmails; if (!string.IsNullOrWhiteSpace(bcc)) { bcc = bcc.ResolveMergeFields(mergeObjects, currentPerson); foreach (string bccRecipient in bcc.SplitDelimitedValues()) { // Resolve any possible merge fields in the bcc address message.Bcc.Add(new MailAddress(bccRecipient)); } } // Subject message.Subject = ResolveText(communication.Subject, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); // Plain text if (mediumAttributes.ContainsKey("DefaultPlainText")) { string plainText = ResolveText(mediumAttributes["DefaultPlainText"], currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); if (!string.IsNullOrWhiteSpace(plainText)) { AlternateView plainTextView = AlternateView.CreateAlternateViewFromString(plainText, new System.Net.Mime.ContentType(MediaTypeNames.Text.Plain)); message.AlternateViews.Add(plainTextView); } } // Add Html view // Get the unsubscribe content and add a merge field for it string htmlBody = communication.Message; if (communication.IsBulkCommunication && mediumAttributes.ContainsKey("UnsubscribeHTML")) { string unsubscribeHtml = ResolveText(mediumAttributes["UnsubscribeHTML"], currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); mergeObjects.AddOrReplace("UnsubscribeOption", unsubscribeHtml); htmlBody = ResolveText(htmlBody, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); // Resolve special syntax needed if option was included in global attribute if (Regex.IsMatch(htmlBody, @"\[\[\s*UnsubscribeOption\s*\]\]")) { htmlBody = Regex.Replace(htmlBody, @"\[\[\s*UnsubscribeOption\s*\]\]", unsubscribeHtml); } // Add the unsubscribe option at end if it wasn't included in content if (!htmlBody.Contains(unsubscribeHtml)) { htmlBody += unsubscribeHtml; } } else { htmlBody = ResolveText(htmlBody, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); htmlBody = Regex.Replace(htmlBody, @"\[\[\s*UnsubscribeOption\s*\]\]", string.Empty); } if (!string.IsNullOrWhiteSpace(htmlBody)) { if (cssInliningEnabled) { // move styles inline to help it be compatible with more email clients htmlBody = htmlBody.ConvertHtmlStylesToInlineAttributes(); } // add the main Html content to the email AlternateView htmlView = AlternateView.CreateAlternateViewFromString(htmlBody, new System.Net.Mime.ContentType(MediaTypeNames.Text.Html)); message.AlternateViews.Add(htmlView); } // Add any additional headers that specific SMTP provider needs var metaData = new Dictionary <string, string>(); metaData.Add("communication_recipient_guid", recipient.Guid.ToString()); AddAdditionalHeaders(message, metaData); // Recreate the attachments message.Attachments.Clear(); foreach (var binaryFile in communication.GetAttachments(CommunicationType.Email).Select(a => a.BinaryFile)) { message.Attachments.Add(new Attachment(binaryFile.ContentStream, binaryFile.FileName)); } smtpClient.Send(message); recipient.Status = CommunicationRecipientStatus.Delivered; string statusNote = StatusNote; if (!string.IsNullOrWhiteSpace(statusNote)) { recipient.StatusNote = statusNote; } recipient.TransportEntityTypeName = this.GetType().FullName; try { var historyService = new HistoryService(recipientRockContext); historyService.Add(new History { CreatedByPersonAliasId = communication.SenderPersonAliasId, EntityTypeId = personEntityTypeId, CategoryId = communicationCategoryId, EntityId = recipient.PersonAlias.PersonId, Summary = string.Format("Sent communication from <span class='field-value'>{0}</span>.", message.From.DisplayName), Caption = message.Subject, RelatedEntityTypeId = communicationEntityTypeId, RelatedEntityId = communication.Id }); } catch (Exception ex) { ExceptionLogService.LogException(ex, null); } } catch (Exception ex) { ExceptionLogService.LogException(ex); recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Exception: " + ex.Messages().AsDelimited(" => "); } } recipientRockContext.SaveChanges(); } else { recipientFound = false; } } } } } }
/// <summary> /// Sends the specified communication. /// </summary> /// <param name="communication">The communication.</param> /// <param name="mediumEntityTypeId">The medium entity type identifier.</param> /// <param name="mediumAttributes">The medium attributes.</param> /// <exception cref="System.NotImplementedException"></exception> public override void Send(Communication communication, int mediumEntityTypeId, Dictionary <string, string> mediumAttributes) { using (var communicationRockContext = new RockContext()) { // Requery the Communication communication = new CommunicationService(communicationRockContext) .Queryable("CreatedByPersonAlias.Person") .FirstOrDefault(c => c.Id == communication.Id); bool hasPendingRecipients; if (communication != null && communication.Status == CommunicationStatus.Approved && (!communication.FutureSendDateTime.HasValue || communication.FutureSendDateTime.Value.CompareTo(RockDateTime.Now) <= 0)) { var qryRecipients = new CommunicationRecipientService(communicationRockContext).Queryable(); hasPendingRecipients = qryRecipients .Where(r => r.CommunicationId == communication.Id && r.Status == CommunicationRecipientStatus.Pending && r.MediumEntityTypeId.HasValue && r.MediumEntityTypeId.Value == mediumEntityTypeId) .Any(); } else { hasPendingRecipients = false; } if (hasPendingRecipients) { var currentPerson = communication.CreatedByPersonAlias?.Person; var globalAttributes = GlobalAttributesCache.Get(); string publicAppRoot = globalAttributes.GetValue("PublicApplicationRoot").EnsureTrailingForwardslash(); var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(null, currentPerson); var personEntityTypeId = EntityTypeCache.Get("Rock.Model.Person").Id; var communicationEntityTypeId = EntityTypeCache.Get("Rock.Model.Communication").Id; var communicationCategoryId = CategoryCache.Get(Rock.SystemGuid.Category.HISTORY_PERSON_COMMUNICATIONS.AsGuid(), communicationRockContext).Id; var client = new RestClient(GetAttributeValue("APIEndpoint")); JsonSerializerSettings serializerSettings = new JsonSerializerSettings { ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor, DateTimeZoneHandling = DateTimeZoneHandling.Local }; bool recipientFound = true; while (recipientFound) { // make a new rockContext per recipient var recipientRockContext = new RockContext(); var recipient = Communication.GetNextPending(communication.Id, mediumEntityTypeId, recipientRockContext); if (recipient != null) { if (ValidRecipient(recipient, communication.IsBulkCommunication)) { try { int personAlias = recipient.PersonAliasId; // Create merge field dictionary var mergeObjects = recipient.CommunicationMergeValues(mergeFields); var message = ResolveText(communication.PushMessage, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); var title = ResolveText(communication.PushTitle, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); var sound = ResolveText(communication.PushSound, currentPerson, communication.EnabledLavaCommands, mergeObjects, publicAppRoot); var notification = new subsplash.Model.Notification(); notification.AppKey = GetAttributeValue("AppKey"); notification.Body = message; notification.Title = title; notification.PublishedAt = RockDateTime.Now; notification.Embedded = new NotificationEmbedded(); notification.Embedded.ExternalUser = new ExternalUser(); notification.Embedded.ExternalUser.AuthProviderId = Encryption.DecryptString(GetAttributeValue("AuthProviderId")).AsGuidOrNull(); notification.Embedded.ExternalUser.UserIds = recipient.PersonAlias.Person.Aliases.Select(a => a.AliasPersonId.ToString()).ToList(); var sendPush = new RestRequest("notifications", Method.POST); sendPush.AddHeader("Content-Type", "application/json"); sendPush.AddHeader("Authorization", "Bearer " + Encryption.DecryptString(GetAttributeValue("JWTToken"))); sendPush.RequestFormat = DataFormat.Json; sendPush.AddParameter("application/json", JsonConvert.SerializeObject(notification, serializerSettings), ParameterType.RequestBody); var response = client.Execute(sendPush); ErrorResponse error = ( ErrorResponse )JsonConvert.DeserializeObject(response.Content, typeof(ErrorResponse), serializerSettings); if (error != null && error.Errors != null) { recipient.StatusNote = string.Join("\n", error.Errors.Select(e => e.Code + ": " + (e.Message ?? e.Detail)).ToList()); recipient.Status = CommunicationRecipientStatus.Failed; } else { recipient.SendDateTime = RockDateTime.Now; recipient.Status = CommunicationRecipientStatus.Delivered; subsplash.Model.Notification notificationResponse = (subsplash.Model.Notification)JsonConvert.DeserializeObject(response.Content, typeof(subsplash.Model.Notification), serializerSettings); recipient.UniqueMessageId = notificationResponse.Id.ToString(); } recipient.TransportEntityTypeName = this.GetType().FullName; try { var historyService = new HistoryService(recipientRockContext); historyService.Add(new History { CreatedByPersonAliasId = communication.SenderPersonAliasId, EntityTypeId = personEntityTypeId, CategoryId = communicationCategoryId, EntityId = recipient.PersonAlias.PersonId, Verb = History.HistoryVerb.Sent.ConvertToString().ToUpper(), ChangeType = History.HistoryChangeType.Record.ToString(), ValueName = "Push Notification", Caption = message.Truncate(200), RelatedEntityTypeId = communicationEntityTypeId, RelatedEntityId = communication.Id }); } catch (Exception ex) { ExceptionLogService.LogException(ex, null); } } catch (Exception ex) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Subsplash Push Notification Exception: " + ex.Message; } } recipientRockContext.SaveChanges(); } else { recipientFound = false; } } } } }
/// <summary> /// Job that will run quick SQL queries on a schedule. /// /// 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) { JobDataMap dataMap = context.JobDetail.JobDataMap; Guid?entryWorkflowType = dataMap.GetString("EraEntryWorkflow").AsGuidOrNull(); Guid?exitWorkflowType = dataMap.GetString("EraExitWorkflow").AsGuidOrNull(); bool updateVisitDates = dataMap.GetBooleanValue("SetVisitDates"); var groupTypeList = dataMap.GetString("GroupTypes"); // configuration // // giving int exitGivingCount = 1; // attendance int exitAttendanceCountShort = 1; int exitAttendanceCountLong = 8; // get era dataset from stored proc var resultContext = new RockContext(); var eraAttribute = AttributeCache.Read(SystemGuid.Attribute.PERSON_ERA_CURRENTLY_AN_ERA.AsGuid()); var eraStartAttribute = AttributeCache.Read(SystemGuid.Attribute.PERSON_ERA_START_DATE.AsGuid()); var eraEndAttribute = AttributeCache.Read(SystemGuid.Attribute.PERSON_ERA_END_DATE.AsGuid()); resultContext.Database.CommandTimeout = 3600; var results = resultContext.Database.SqlQuery <EraResult>("spCrm_FamilyAnalyticsEraDataset").ToList(); int personEntityTypeId = EntityTypeCache.Read("Rock.Model.Person").Id; int attributeEntityTypeId = EntityTypeCache.Read("Rock.Model.Attribute").Id; int eraAttributeId = AttributeCache.Read(SystemGuid.Attribute.PERSON_ERA_CURRENTLY_AN_ERA.AsGuid()).Id; int personAnalyticsCategoryId = CategoryCache.Read(SystemGuid.Category.HISTORY_PERSON_ANALYTICS.AsGuid()).Id; foreach (var result in results) { // create new rock context for each family (https://weblog.west-wind.com/posts/2014/Dec/21/Gotcha-Entity-Framework-gets-slow-in-long-Iteration-Loops) RockContext updateContext = new RockContext(); var attributeValueService = new AttributeValueService(updateContext); var historyService = new HistoryService(updateContext); // if era ensure it still meets requirements if (result.IsEra) { if (result.ExitGiftCountDuration < exitGivingCount && result.ExitAttendanceCountDurationShort < exitAttendanceCountShort && result.ExitAttendanceCountDurationLong < exitAttendanceCountLong) { // exit era (delete attribute value from each person in family) var family = new GroupService(updateContext).Queryable("Members, Members.Person").AsNoTracking().Where(m => m.Id == result.FamilyId).FirstOrDefault(); if (family != null) { foreach (var person in family.Members.Select(m => m.Person)) { // remove the era flag var eraAttributeValue = attributeValueService.Queryable().Where(v => v.AttributeId == eraAttribute.Id && v.EntityId == person.Id).FirstOrDefault(); if (eraAttributeValue != null) { attributeValueService.Delete(eraAttributeValue); } // set end date var eraEndAttributeValue = attributeValueService.Queryable().Where(v => v.AttributeId == eraEndAttribute.Id && v.EntityId == person.Id).FirstOrDefault(); if (eraEndAttributeValue == null) { eraEndAttributeValue = new AttributeValue(); eraEndAttributeValue.EntityId = person.Id; eraEndAttributeValue.AttributeId = eraEndAttribute.Id; attributeValueService.Add(eraEndAttributeValue); } eraEndAttributeValue.Value = RockDateTime.Now.ToString(); // add a history record if (personAnalyticsCategoryId != 0 && personEntityTypeId != 0 && attributeEntityTypeId != 0 && eraAttributeId != 0) { History historyRecord = new History(); historyService.Add(historyRecord); historyRecord.EntityTypeId = personEntityTypeId; historyRecord.EntityId = person.Id; historyRecord.CreatedDateTime = RockDateTime.Now; historyRecord.CreatedByPersonAliasId = person.PrimaryAliasId; historyRecord.Caption = "eRA"; historyRecord.Summary = "Exited eRA Status"; historyRecord.Verb = "EXITED"; historyRecord.RelatedEntityTypeId = attributeEntityTypeId; historyRecord.RelatedEntityId = eraAttributeId; historyRecord.CategoryId = personAnalyticsCategoryId; } updateContext.SaveChanges(); } // launch exit workflow if (exitWorkflowType.HasValue) { LaunchWorkflow(exitWorkflowType.Value, family); } } } } else { // entered era var family = new GroupService(updateContext).Queryable("Members").AsNoTracking().Where(m => m.Id == result.FamilyId).FirstOrDefault(); if (family != null) { foreach (var person in family.Members.Where(m => !m.Person.IsDeceased).Select(m => m.Person)) { // set era attribute to true var eraAttributeValue = attributeValueService.Queryable().Where(v => v.AttributeId == eraAttribute.Id && v.EntityId == person.Id).FirstOrDefault(); if (eraAttributeValue == null) { eraAttributeValue = new AttributeValue(); eraAttributeValue.EntityId = person.Id; eraAttributeValue.AttributeId = eraAttribute.Id; attributeValueService.Add(eraAttributeValue); } eraAttributeValue.Value = bool.TrueString; // add start date var eraStartAttributeValue = attributeValueService.Queryable().Where(v => v.AttributeId == eraStartAttribute.Id && v.EntityId == person.Id).FirstOrDefault(); if (eraStartAttributeValue == null) { eraStartAttributeValue = new AttributeValue(); eraStartAttributeValue.EntityId = person.Id; eraStartAttributeValue.AttributeId = eraStartAttribute.Id; attributeValueService.Add(eraStartAttributeValue); } eraStartAttributeValue.Value = RockDateTime.Now.ToString(); // delete end date if it exists var eraEndAttributeValue = attributeValueService.Queryable().Where(v => v.AttributeId == eraEndAttribute.Id && v.EntityId == person.Id).FirstOrDefault(); if (eraEndAttributeValue != null) { attributeValueService.Delete(eraEndAttributeValue); } // add a history record if (personAnalyticsCategoryId != 0 && personEntityTypeId != 0 && attributeEntityTypeId != 0 && eraAttributeId != 0) { History historyRecord = new History(); historyService.Add(historyRecord); historyRecord.EntityTypeId = personEntityTypeId; historyRecord.EntityId = person.Id; historyRecord.CreatedDateTime = RockDateTime.Now; historyRecord.CreatedByPersonAliasId = person.PrimaryAliasId; historyRecord.Caption = "eRA"; historyRecord.Summary = "Entered eRA Status"; historyRecord.Verb = "ENTERED"; historyRecord.RelatedEntityTypeId = attributeEntityTypeId; historyRecord.RelatedEntityId = eraAttributeId; historyRecord.CategoryId = personAnalyticsCategoryId; } updateContext.SaveChanges(); } // launch entry workflow if (entryWorkflowType.HasValue) { LaunchWorkflow(entryWorkflowType.Value, family); } } } // update stats } // load giving attributes resultContext.Database.ExecuteSqlCommand("spCrm_FamilyAnalyticsGiving"); // load attendance attributes resultContext.Database.ExecuteSqlCommand("spCrm_FamilyAnalyticsAttendance"); // process history for group types if (!string.IsNullOrWhiteSpace(groupTypeList)) { string[] groupTypeGuids = groupTypeList.Split(','); var inactiveRecordValue = DefinedValueCache.Read(SystemGuid.DefinedValue.PERSON_RECORD_STATUS_INACTIVE); var groupTypeEntityTypeId = EntityTypeCache.Read("Rock.Model.GroupType").Id; foreach (var groupTypeGuid in groupTypeGuids) { var groupType = GroupTypeCache.Read(groupTypeGuid.AsGuid()); if (groupType != null) { // if the person is in a group of that type and the last history record for that group type isn't START write a start RockContext rockContext = new RockContext(); // get history for this group type var historyRecords = new HistoryService(rockContext).Queryable() .Where(h => h.EntityTypeId == personEntityTypeId && h.RelatedEntityTypeId == groupTypeEntityTypeId && h.RelatedEntityId == groupType.Id ) .GroupBy(h => h.EntityId) .Select(g => g.OrderByDescending(h => h.CreatedDateTime).Select(h => new { h.EntityId, h.Verb }).FirstOrDefault()) .ToList(); // get group member information var groupMemberInfo = new GroupMemberService(rockContext).Queryable() .Where(m => m.Group.GroupTypeId == groupType.Id && m.GroupMemberStatus == GroupMemberStatus.Active && m.Group.IsActive //&& m.Person.RecordStatusValueId != inactiveRecordValue.Id ) .GroupBy(m => m.PersonId) .Select(g => g.OrderBy(m => m.CreatedDateTime).Select(m => new { m.PersonId, m.CreatedDateTime, PersonAliasId = m.Person.Aliases.Select(p => p.Id).FirstOrDefault() }).FirstOrDefault()) .ToList(); var needsStartDate = groupMemberInfo.Where(m => !historyRecords.Any(h => h.EntityId == m.PersonId && h.Verb == "STARTED")); foreach (var startItem in needsStartDate) { using (RockContext updateContext = new RockContext()) { var historyService = new HistoryService(updateContext); History history = new History(); historyService.Add(history); history.EntityTypeId = personEntityTypeId; history.EntityId = startItem.PersonId; history.RelatedEntityTypeId = groupTypeEntityTypeId; history.RelatedEntityId = groupType.Id; history.Caption = groupType.Name; history.Summary = "Started Membership in Group Of Type"; history.Verb = "STARTED"; history.CreatedDateTime = startItem.CreatedDateTime; history.CreatedByPersonAliasId = startItem.PersonAliasId; history.CategoryId = personAnalyticsCategoryId; updateContext.SaveChanges(); } } var needsStoppedDate = historyRecords.Where(h => h.Verb == "STARTED" && !groupMemberInfo.Any(m => m.PersonId == h.EntityId)); foreach (var stopItem in needsStoppedDate) { using (RockContext updateContext = new RockContext()) { var person = new PersonService(updateContext).Get(stopItem.EntityId); if (person != null) { var historyService = new HistoryService(updateContext); History history = new History(); historyService.Add(history); history.EntityTypeId = personEntityTypeId; history.EntityId = person.Id; history.RelatedEntityTypeId = groupTypeEntityTypeId; history.RelatedEntityId = groupType.Id; history.Caption = groupType.Name; history.Summary = "Stopped Membership in Group Of Type"; history.Verb = "STOPPED"; history.CreatedDateTime = RockDateTime.Now; history.CreatedByPersonAliasId = person.PrimaryAliasId; history.CategoryId = personAnalyticsCategoryId; updateContext.SaveChanges(); } } } } } } // process visit dates if (updateVisitDates) { resultContext.Database.ExecuteSqlCommand("spCrm_FamilyAnalyticsUpdateVisitDates"); } }