/// <summary> /// Gets the queued communications. /// </summary> /// <param name="expirationDays">The expiration days.</param> /// <param name="delayMinutes">The delay minutes.</param> /// <param name="includeFuture">if set to <c>true</c> [include future].</param> /// <param name="includePendingApproval">if set to <c>true</c> communications that haven't been approved yet will be included.</param> /// <returns></returns> public IQueryable <Communication> GetQueued(int expirationDays, int delayMinutes, bool includeFuture, bool includePendingApproval) { var beginWindow = RockDateTime.Now.AddDays(0 - expirationDays); var endWindow = RockDateTime.Now.AddMinutes(0 - delayMinutes); var currentDateTime = RockDateTime.Now; // Conditions for communications that should be queued for Sending (indicated by includeFuture == false and includePending == false) // - communications that haven't been sent yet (SendDateTime is null) // - communication is approved (or includePendingApproval == false) // - FutureSendDateTime is not set (not scheduled), and communication was created within a reasonable window based on expiration days (for example, no older than 3 days ago) // - OR - FutureSendDateTime IS set (scheduled), and the FutureSendDateTime is Now (or within the expiration window) // Limit to communications that haven't been sent yet var queuedQry = Queryable().Where(c => !c.SendDateTime.HasValue); var qryPendingRecipients = new CommunicationRecipientService(( RockContext )Context) .Queryable() .Where(a => a.Status == CommunicationRecipientStatus.Pending); if (includePendingApproval) { // Also limit to communications that are Approved or Pending Approval queuedQry = queuedQry.Where(c => c.Status == CommunicationStatus.Approved || c.Status == CommunicationStatus.PendingApproval); } else { // Also limit to communications that are Approved queuedQry = queuedQry.Where(c => c.Status == CommunicationStatus.Approved); } if (includeFuture) { // Also limit to communications that have either been created within a reasonable timeframe (typically no older than 3 days ago) or are Scheduled to be sent at some point queuedQry = queuedQry.Where(c => (!c.FutureSendDateTime.HasValue && c.CreatedDateTime.HasValue && c.CreatedDateTime.Value >= beginWindow && c.CreatedDateTime.Value <= endWindow) || (c.FutureSendDateTime.HasValue && c.FutureSendDateTime.Value >= beginWindow)); } else { // Also limit to communications that have either been created within a reasonable timeframe (typically no older than 3 days ago) // or are Scheduled to be sent (also within that reasonable timeframe. In other words, if it was scheduled to be sent, but stil hasn't been sent 3 days after it was scheduled, don't include it) queuedQry = queuedQry.Where(c => (!c.FutureSendDateTime.HasValue && c.CreatedDateTime.HasValue && c.CreatedDateTime.Value >= beginWindow && c.CreatedDateTime.Value <= endWindow) || (c.FutureSendDateTime.HasValue && c.FutureSendDateTime.Value >= beginWindow && c.FutureSendDateTime.Value <= currentDateTime)); } // just in case SendDateTime is null (pre-v8 communication), also limit to communications that either have a ListGroupId or has PendingRecipients queuedQry = queuedQry.Where(c => c.ListGroupId.HasValue || qryPendingRecipients.Any(r => r.CommunicationId == c.Id)); return(queuedQry); }
/// <summary> /// Gets the queued communications /// </summary> /// <param name="expirationDays">The expiration days.</param> /// <param name="delayMinutes">The delay minutes.</param> /// <param name="includeFuture">if set to <c>true</c> [include future].</param> /// <param name="includePending">if set to <c>true</c> [include pending].</param> /// <returns></returns> public IQueryable <Communication> GetQueued(int expirationDays, int delayMinutes, bool includeFuture, bool includePending) { var beginWindow = RockDateTime.Now.AddDays(0 - expirationDays); var endWindow = RockDateTime.Now.AddMinutes(0 - delayMinutes); var nowDate = RockDateTime.Now; var qryPendingRecipients = new CommunicationRecipientService((RockContext)Context) .Queryable() .Where(a => a.Status == CommunicationRecipientStatus.Pending); return(Queryable() .Where(c => (c.Status == CommunicationStatus.Approved || (includePending && c.Status == CommunicationStatus.PendingApproval)) && qryPendingRecipients.Where(r => r.CommunicationId == c.Id).Any() && ( (!c.FutureSendDateTime.HasValue && c.CreatedDateTime.HasValue && c.CreatedDateTime.Value.CompareTo(beginWindow) >= 0 && c.CreatedDateTime.Value.CompareTo(endWindow) <= 0) || (c.FutureSendDateTime.HasValue && c.FutureSendDateTime.Value.CompareTo(beginWindow) >= 0 && (includeFuture || c.FutureSendDateTime.Value.CompareTo(nowDate) <= 0)) ))); }
/// <summary> /// Removes the non-primary person alias recipients. /// </summary> /// <param name="rockContext">The rock context.</param> private void RemoveNonPrimaryPersonAliasRecipients(RockContext rockContext) { /* * 4-MAY-2022 DMV * * In tracking down alleged duplicate communications we discovered * that duplicates could be sent to the same person if they are in the * recipient list more that once with mulitple Person Alias IDs. * This could have occured through a person merge or other data changes * in Rock. This method removes those duplicates from the list before * sending the communication. * */ var communicationRecipientService = new CommunicationRecipientService(rockContext); var recipientsQry = GetRecipientsQry(rockContext); int?smsMediumEntityTypeId = EntityTypeCache.GetId(Rock.SystemGuid.EntityType.COMMUNICATION_MEDIUM_SMS.AsGuid()); if (smsMediumEntityTypeId.HasValue) { IQueryable <CommunicationRecipient> duplicateSMSRecipientsQuery = recipientsQry.Where(a => a.MediumEntityTypeId == smsMediumEntityTypeId.Value) .Where(a => a.PersonAlias.PersonId != a.PersonAlias.AliasPersonId); // Only non-primary aliases. var duplicateSMSRecipients = duplicateSMSRecipientsQuery.ToList(); communicationRecipientService.DeleteRange(duplicateSMSRecipients); } int?emailMediumEntityTypeId = EntityTypeCache.GetId(Rock.SystemGuid.EntityType.COMMUNICATION_MEDIUM_EMAIL.AsGuid()); if (emailMediumEntityTypeId.HasValue) { IQueryable <CommunicationRecipient> duplicateEmailRecipientsQry = recipientsQry.Where(a => a.MediumEntityTypeId == emailMediumEntityTypeId.Value) .Where(a => a.PersonAlias.PersonId != a.PersonAlias.AliasPersonId); // Only non-primary aliases. var duplicateEmailRecipients = duplicateEmailRecipientsQry.ToList(); communicationRecipientService.DeleteRange(duplicateEmailRecipients); } rockContext.SaveChanges(); }
/// <summary> /// if <see cref="ExcludeDuplicateRecipientAddress" /> is set to true, removes <see cref="CommunicationRecipient"></see>s that have the same SMS/Email address as another recipient /// </summary> /// <param name="rockContext">The rock context.</param> public void RemoveRecipientsWithDuplicateAddress(RockContext rockContext) { if (!ExcludeDuplicateRecipientAddress) { return; } var communicationRecipientService = new CommunicationRecipientService(rockContext); var recipientsQry = GetRecipientsQry(rockContext); int?smsMediumEntityTypeId = EntityTypeCache.GetId(Rock.SystemGuid.EntityType.COMMUNICATION_MEDIUM_SMS.AsGuid()); if (smsMediumEntityTypeId.HasValue) { IQueryable <CommunicationRecipient> duplicateSMSRecipientsQuery = recipientsQry.Where(a => a.MediumEntityTypeId == smsMediumEntityTypeId.Value) .Where(a => a.PersonAlias.Person.PhoneNumbers.Where(pn => pn.IsMessagingEnabled).Any()) .GroupBy(a => a.PersonAlias.Person.PhoneNumbers.Where(pn => pn.IsMessagingEnabled).FirstOrDefault().Number) .Where(a => a.Count() > 1) .Select(a => a.OrderBy(x => x.Id).Skip(1).ToList()) .SelectMany(a => a); var duplicateSMSRecipients = duplicateSMSRecipientsQuery.ToList(); communicationRecipientService.DeleteRange(duplicateSMSRecipients); } int?emailMediumEntityTypeId = EntityTypeCache.GetId(Rock.SystemGuid.EntityType.COMMUNICATION_MEDIUM_EMAIL.AsGuid()); if (emailMediumEntityTypeId.HasValue) { IQueryable <CommunicationRecipient> duplicateEmailRecipientsQry = recipientsQry.Where(a => a.MediumEntityTypeId == emailMediumEntityTypeId.Value) .GroupBy(a => a.PersonAlias.Person.Email) .Where(a => a.Count() > 1) .Select(a => a.OrderBy(x => x.Id).Skip(1).ToList()) .SelectMany(a => a); var duplicateEmailRecipients = duplicateEmailRecipientsQry.ToList(); communicationRecipientService.DeleteRange(duplicateEmailRecipients); } rockContext.SaveChanges(); }
/// <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.Recipients.Where( r => r.Status == Model.CommunicationRecipientStatus.Pending ).Any() && ( !communication.FutureSendDateTime.HasValue || communication.FutureSendDateTime.Value.CompareTo( RockDateTime.Now ) <= 0 ) ) { string fromPhone = string.Empty; string fromValue = communication.GetChannelDataValue( "FromValue" ); int fromValueId = int.MinValue; if ( int.TryParse( fromValue, out fromValueId ) ) { fromPhone = DefinedValueCache.Read( fromValueId ).Value; } if ( !string.IsNullOrWhiteSpace( fromPhone ) ) { string accountSid = GetAttributeValue( "SID" ); string authToken = GetAttributeValue( "Token" ); var twilio = new TwilioRestClient( accountSid, authToken ); var recipientService = new CommunicationRecipientService( rockContext ); var globalConfigValues = GlobalAttributesCache.GetMergeFields( null ); bool recipientFound = true; while ( recipientFound ) { var recipient = recipientService.Get( communication.Id, CommunicationRecipientStatus.Pending ).FirstOrDefault(); if ( recipient != null ) { try { var phoneNumber = recipient.Person.PhoneNumbers .Where( p => p.IsMessagingEnabled ) .FirstOrDefault(); if ( phoneNumber != null ) { // Create merge field dictionary var mergeObjects = recipient.CommunicationMergeValues( globalConfigValues ); string message = communication.GetChannelDataValue( "Message" ); message = message.ResolveMergeFields( mergeObjects ); string twilioNumber = phoneNumber.Number; if ( !string.IsNullOrWhiteSpace( phoneNumber.CountryCode ) ) { twilioNumber = "+" + phoneNumber.CountryCode + phoneNumber.Number; } var response = twilio.SendMessage( fromPhone, twilioNumber, message ); recipient.Status = CommunicationRecipientStatus.Delivered; recipient.TransportEntityTypeName = this.GetType().FullName; recipient.UniqueMessageId = response.Sid; } 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> /// Copies the specified communication identifier. /// </summary> /// <param name="communicationId">The communication identifier.</param> /// <param name="currentPersonAliasId">The current person alias identifier.</param> /// <returns></returns> public Communication Copy(int communicationId, int?currentPersonAliasId) { var dataContext = ( RockContext )Context; var service = new CommunicationService(dataContext); var communicationRecipientService = new CommunicationRecipientService(dataContext); var communication = service.Get(communicationId); if (communication != null) { var newCommunication = communication.Clone(false); newCommunication.CreatedByPersonAlias = null; newCommunication.CreatedByPersonAliasId = null; newCommunication.CreatedDateTime = RockDateTime.Now; newCommunication.ModifiedByPersonAlias = null; newCommunication.ModifiedByPersonAliasId = null; newCommunication.ModifiedDateTime = RockDateTime.Now; newCommunication.Id = 0; newCommunication.Guid = Guid.Empty; newCommunication.SenderPersonAliasId = currentPersonAliasId; newCommunication.Status = CommunicationStatus.Draft; newCommunication.ReviewerPersonAliasId = null; newCommunication.ReviewedDateTime = null; newCommunication.ReviewerNote = string.Empty; newCommunication.SendDateTime = null; // Get the recipients from the original communication, // but only for recipients that are using the person's primary alias id. // This will avoid an issue where a copied communication will include the same person multiple times // if they have been merged since the original communication was created var primaryAliasRecipients = communicationRecipientService.Queryable() .Where(a => a.CommunicationId == communication.Id) .Select(a => new { a.PersonAlias.Person, a.AdditionalMergeValuesJson, a.PersonAliasId }).ToList() .GroupBy(a => a.Person.PrimaryAliasId) .Select(s => new { PersonAliasId = s.Key, AdditionalMergeValuesJson = s.Where(a => a.PersonAliasId == s.Key).Select(x => x.AdditionalMergeValuesJson).FirstOrDefault() }) .Where(s => s.PersonAliasId.HasValue) .ToList(); foreach (var primaryAliasRecipient in primaryAliasRecipients) { newCommunication.Recipients.Add(new CommunicationRecipient() { PersonAliasId = primaryAliasRecipient.PersonAliasId.Value, Status = CommunicationRecipientStatus.Pending, StatusNote = string.Empty, AdditionalMergeValuesJson = primaryAliasRecipient.AdditionalMergeValuesJson }); } foreach (var attachment in communication.Attachments.ToList()) { var newAttachment = new CommunicationAttachment(); newAttachment.BinaryFileId = attachment.BinaryFileId; newAttachment.CommunicationType = attachment.CommunicationType; newCommunication.Attachments.Add(newAttachment); } return(newCommunication); } return(null); }
private void BindGrid() { using ( new UnitOfWorkScope() ) { var communications = new CommunicationService() .Queryable() .Where( c => c.Status != CommunicationStatus.Transient ); string subject = rFilter.GetUserPreference( "Subject" ); if ( !string.IsNullOrWhiteSpace( subject ) ) { communications = communications.Where( c => c.Subject.StartsWith( subject ) ); } Guid entityTypeGuid = Guid.Empty; if ( Guid.TryParse( rFilter.GetUserPreference( "Channel" ), out entityTypeGuid ) ) { communications = communications.Where( c => c.ChannelEntityType != null && c.ChannelEntityType.Guid.Equals( entityTypeGuid ) ); } string status = rFilter.GetUserPreference( "Status" ); if ( !string.IsNullOrWhiteSpace( status ) ) { var communicationStatus = (CommunicationStatus)System.Enum.Parse( typeof( CommunicationStatus ), status ); communications = communications.Where( c => c.Status == communicationStatus ); } if ( canApprove ) { int personId = 0; if ( int.TryParse( rFilter.GetUserPreference( "Created By" ), out personId ) && personId != 0 ) { communications = communications.Where( c => c.SenderPersonId.HasValue && c.SenderPersonId.Value == personId ); } } else { communications = communications.Where( c => c.SenderPersonId.HasValue && c.SenderPersonId.Value == CurrentPersonId ); } string content = rFilter.GetUserPreference( "Content" ); if ( !string.IsNullOrWhiteSpace( content ) ) { communications = communications.Where( c => c.ChannelDataJson.Contains( content ) ); } var recipients = new CommunicationRecipientService().Queryable(); var sortProperty = gCommunication.SortProperty; var queryable = communications .Join( recipients, c => c.Id, r => r.CommunicationId, ( c, r ) => new { c, r } ) .GroupBy( cr => cr.c ) .Select( g => new CommunicationItem { Id = g.Key.Id, Communication = g.Key, Recipients = g.Count(), PendingRecipients = g.Count( s => s.r.Status == CommunicationRecipientStatus.Pending ), SuccessRecipients = g.Count( s => s.r.Status == CommunicationRecipientStatus.Success ), FailedRecipients = g.Count( s => s.r.Status == CommunicationRecipientStatus.Failed ), CancelledRecipients = g.Count( s => s.r.Status == CommunicationRecipientStatus.Cancelled ) } ); if ( sortProperty != null ) { queryable = queryable.Sort( sortProperty ); } else { queryable = queryable.OrderByDescending( c => c.Communication.Id ); } gCommunication.DataSource = queryable.ToList(); gCommunication.DataBind(); } }
/// <summary> /// Updates a communication model with the user-entered values /// </summary> /// <param name="communicationService">The service.</param> /// <returns></returns> private Rock.Model.Communication UpdateCommunication(RockContext rockContext) { var communicationService = new CommunicationService(rockContext); var recipientService = new CommunicationRecipientService(rockContext); Rock.Model.Communication communication = null; IQueryable<CommunicationRecipient> qryRecipients = null; if ( CommunicationId.HasValue ) { communication = communicationService.Get( CommunicationId.Value ); } if ( communication != null ) { // Remove any deleted recipients HashSet<int> personIdHash = new HashSet<int>( Recipients.Select( a => a.PersonId ) ); qryRecipients = communication.GetRecipientsQry( rockContext ); foreach ( var item in qryRecipients.Select( a => new { Id = a.Id, PersonId = a.PersonAlias.PersonId }) ) { if ( !personIdHash.Contains(item.PersonId) ) { var recipient = qryRecipients.Where( a => a.Id == item.Id ).FirstOrDefault(); recipientService.Delete( recipient ); communication.Recipients.Remove( recipient ); } } } if (communication == null) { communication = new Rock.Model.Communication(); communication.Status = CommunicationStatus.Transient; communication.SenderPersonAliasId = CurrentPersonAliasId; communicationService.Add( communication ); } if (qryRecipients == null) { qryRecipients = communication.GetRecipientsQry( rockContext ); } // Add any new recipients HashSet<int> communicationPersonIdHash = new HashSet<int>( qryRecipients.Select( a => a.PersonAlias.PersonId ) ); foreach(var recipient in Recipients ) { if ( !communicationPersonIdHash.Contains( recipient.PersonId ) ) { var person = new PersonService( rockContext ).Get( recipient.PersonId ); if ( person != null ) { var communicationRecipient = new CommunicationRecipient(); communicationRecipient.PersonAlias = person.PrimaryAlias; communication.Recipients.Add( communicationRecipient ); } } } communication.IsBulkCommunication = cbBulk.Checked; communication.MediumEntityTypeId = MediumEntityTypeId; communication.MediumData.Clear(); GetMediumData(); foreach ( var keyVal in MediumData ) { if ( !string.IsNullOrEmpty( keyVal.Value ) ) { communication.MediumData.Add( keyVal.Key, keyVal.Value ); } } if ( communication.MediumData.ContainsKey( "Subject" ) ) { communication.Subject = communication.MediumData["Subject"]; communication.MediumData.Remove( "Subject" ); } DateTime? futureSendDate = dtpFutureSend.SelectedDateTime; if ( futureSendDate.HasValue && futureSendDate.Value.CompareTo( RockDateTime.Now ) > 0 ) { communication.FutureSendDateTime = futureSendDate; } else { communication.FutureSendDateTime = null; } return communication; }
/// <summary> /// Gets the SMS conversation history for a person alias ID. Includes the communication sent by Rock that the person may be responding to. /// </summary> /// <param name="personId">The person identifier.</param> /// <param name="relatedSmsFromDefinedValueId">The related SMS from defined value identifier.</param> /// <returns>List<CommunicationRecipientResponse>.</returns> public List <CommunicationRecipientResponse> GetCommunicationConversationForPerson(int personId, int relatedSmsFromDefinedValueId) { List <CommunicationRecipientResponse> communicationRecipientResponseList = new List <CommunicationRecipientResponse>(); var smsMediumEntityTypeId = EntityTypeCache.GetId(SystemGuid.EntityType.COMMUNICATION_MEDIUM_SMS).Value; var personAliasIdQuery = new PersonAliasService(this.Context as RockContext).Queryable().Where(a => a.PersonId == personId).Select(a => a.Id); var communicationResponseQuery = this.Queryable() .Where(r => r.RelatedMediumEntityTypeId == smsMediumEntityTypeId && r.RelatedSmsFromDefinedValueId == relatedSmsFromDefinedValueId && r.FromPersonAliasId.HasValue && personAliasIdQuery.Contains(r.FromPersonAliasId.Value) ); var communicationResponseList = communicationResponseQuery.ToList(); foreach (var communicationResponse in communicationResponseList) { var communicationRecipientResponse = new CommunicationRecipientResponse { CreatedDateTime = communicationResponse.CreatedDateTime, PersonId = communicationResponse?.FromPersonAlias?.PersonId, FullName = communicationResponse?.FromPersonAlias?.Person.FullName, IsRead = communicationResponse.IsRead, MessageKey = communicationResponse.MessageKey, IsOutbound = false, RecipientPersonAliasId = communicationResponse.FromPersonAliasId, SMSMessage = communicationResponse.Response, MessageStatus = CommunicationRecipientStatus.Delivered, // We are just going to call these delivered because we have them. Setting this will tell the UI to not display the status. CommunicationResponseId = communicationResponse.Id, }; communicationRecipientResponseList.Add(communicationRecipientResponse); } var communicationRecipientQuery = new CommunicationRecipientService(this.Context as RockContext) .Queryable() .Where(r => r.MediumEntityTypeId == smsMediumEntityTypeId) .Where(r => r.Communication.SMSFromDefinedValueId == relatedSmsFromDefinedValueId) .Where(r => r.PersonAliasId.HasValue) .Where(r => personAliasIdQuery.Contains(r.PersonAliasId.Value)) .Where(r => r.Status == CommunicationRecipientStatus.Delivered || r.Status == CommunicationRecipientStatus.Pending); var communicationRecipientList = communicationRecipientQuery.Include(a => a.PersonAlias.Person.PhoneNumbers).Select(a => new { a.CreatedDateTime, a.Communication.SenderPersonAlias.Person, a.Communication, PersonAliasId = a.Communication.SenderPersonAliasId, a.SentMessage, a.Status }).ToList(); foreach (var communicationRecipient in communicationRecipientList) { var communicationRecipientResponse = new CommunicationRecipientResponse { CreatedDateTime = communicationRecipient.CreatedDateTime, PersonId = communicationRecipient.Person?.Id, FullName = communicationRecipient.Person?.FullName, IsRead = true, IsOutbound = true, RecipientPersonAliasId = communicationRecipient.PersonAliasId, SMSMessage = communicationRecipient.SentMessage, MessageStatus = communicationRecipient.Status, CommunicationId = communicationRecipient.Communication?.Id, }; if (communicationRecipient.Person?.IsNameless() == true) { // if the person is nameless, we'll need to know their number since we don't know their name communicationRecipientResponse.MessageKey = communicationRecipient.Person?.PhoneNumbers.FirstOrDefault()?.Number; } else { // If the Person is not nameless, we just need to show their name, not their number communicationRecipientResponse.MessageKey = null; } communicationRecipientResponseList.Add(communicationRecipientResponse); } return(communicationRecipientResponseList.OrderBy(a => a.CreatedDateTime).ToList()); }
/// <summary> /// Creates a recipient token to help track conversations. /// </summary> /// <param name="rockContext">A context to use for database calls.</param> /// <returns>String token</returns> private string GenerateResponseCode( Rock.Data.RockContext rockContext ) { bool isUnique = false; int randomNumber = -1; DateTime tokenStartDate = RockDateTime.Now.Subtract( new TimeSpan( TOKEN_REUSE_DURATION, 0, 0, 0 ) ); Random rnd = new Random(); while ( isUnique == false ) { randomNumber = rnd.Next( 100, 1000 ); if ( randomNumber != 666 ) // just because { // check if token has been used recently var communication = new CommunicationRecipientService( rockContext ).Queryable() .Where( c => c.ResponseCode == "@" + randomNumber.ToString() && c.CreatedDateTime > tokenStartDate ) .FirstOrDefault(); if ( communication == null ) { isUnique = true; } } } return "@" + randomNumber.ToString(); }
/// <summary> /// Sends the specified communication. /// </summary> /// <param name="communication">The communication.</param> public override void Send( Model.Communication communication ) { var rockContext = new RockContext(); var communicationService = new CommunicationService( rockContext ); communication = communicationService.Get( 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 ) ) { // Update any recipients that should not get sent the communication var recipientService = new CommunicationRecipientService( rockContext ); foreach ( var recipient in recipientService.Queryable( "Person" ) .Where( r => r.CommunicationId == communication.Id && r.Status == CommunicationRecipientStatus.Pending ) .ToList() ) { var person = recipient.Person; if ( person.IsDeceased ?? false ) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Person is deceased!"; } } rockContext.SaveChanges(); } base.Send( communication ); }
/// <summary> /// Sends the specified communication. /// </summary> /// <param name="communication">The communication.</param> public override void Send( Model.Communication communication ) { var rockContext = new RockContext(); var communicationService = new CommunicationService( rockContext ); communication = communicationService.Get( 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 ) ) { // Update any recipients that should not get sent the communication var recipientService = new CommunicationRecipientService( rockContext ); foreach ( var recipient in recipientService.Queryable( "Person" ) .Where( r => r.CommunicationId == communication.Id && r.Status == CommunicationRecipientStatus.Pending ) .ToList() ) { var person = recipient.Person; if ( person.IsDeceased ?? false ) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Person is deceased!"; } if ( person.EmailPreference == Model.EmailPreference.DoNotEmail ) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Email Preference of 'Do Not Email!'"; } else if ( person.EmailPreference == Model.EmailPreference.NoMassEmails && communication.IsBulkCommunication ) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Email Preference of 'No Mass Emails!'"; } } // If an unbsubcribe value has been entered, and this is a bulk email, add the text if ( communication.IsBulkCommunication ) { string unsubscribeHtml = GetAttributeValue( "UnsubscribeHTML" ); if ( !string.IsNullOrWhiteSpace( unsubscribeHtml ) ) { communication.SetChannelDataValue( "UnsubscribeHTML", unsubscribeHtml ); } } string defaultPlainText = GetAttributeValue( "DefaultPlainText" ); if ( !string.IsNullOrWhiteSpace( defaultPlainText ) ) { communication.SetChannelDataValue( "DefaultPlainText", defaultPlainText ); } rockContext.SaveChanges(); } base.Send( communication ); }
/// <summary> /// Gets the SMS conversation history for a person alias ID. Includes the communication sent by Rock that the person may be responding to. /// </summary> /// <param name="personAliasId">The person alias identifier.</param> /// <param name="relatedSmsFromDefinedValueId">The related SMS from defined value identifier.</param> /// <returns></returns> public List <CommunicationRecipientResponse> GetCommunicationConversation(int personAliasId, int relatedSmsFromDefinedValueId) { List <CommunicationRecipientResponse> communicationRecipientResponseList = new List <CommunicationRecipientResponse>(); var smsMediumEntityTypeId = EntityTypeCache.GetId(SystemGuid.EntityType.COMMUNICATION_MEDIUM_SMS).Value; /* * 5/4/2021 MSB * When we include conversations we need to make sure we include conversations from all aliases related to the * person so that conversations from merged records still appear here. * * Reason: Merge People Conversations */ var communicationResponseQuery = this.Queryable() .Where(r => r.RelatedMediumEntityTypeId == smsMediumEntityTypeId && r.RelatedSmsFromDefinedValueId == relatedSmsFromDefinedValueId && r.FromPersonAlias != null && r.FromPersonAlias.Person.Aliases.Any(fpa => fpa.Id == personAliasId)); var communicationResponseList = communicationResponseQuery.ToList(); foreach (var communicationResponse in communicationResponseList) { var communicationRecipientResponse = new CommunicationRecipientResponse { CreatedDateTime = communicationResponse.CreatedDateTime, PersonId = communicationResponse?.FromPersonAlias?.PersonId, FullName = communicationResponse?.FromPersonAlias?.Person.FullName, IsRead = communicationResponse.IsRead, MessageKey = communicationResponse.MessageKey, IsOutbound = false, RecipientPersonAliasId = communicationResponse.FromPersonAliasId, SMSMessage = communicationResponse.Response, MessageStatus = CommunicationRecipientStatus.Delivered, // We are just going to call these delivered because we have them. Setting this will tell the UI to not display the status. BinaryFileGuids = communicationResponse.Attachments?.Select(r => r.BinaryFile.Guid).ToList() }; communicationRecipientResponseList.Add(communicationRecipientResponse); } var communicationRecipientQuery = new CommunicationRecipientService(this.Context as RockContext) .Queryable() .Where(r => r.MediumEntityTypeId == smsMediumEntityTypeId) .Where(r => r.Communication.SMSFromDefinedValueId == relatedSmsFromDefinedValueId) .Where(r => r.PersonAlias != null) .Where(r => r.PersonAlias.Person.Aliases.Any(fpa => fpa.Id == personAliasId)) .Where(r => r.Status == CommunicationRecipientStatus.Delivered || r.Status == CommunicationRecipientStatus.Pending); var communicationRecipientList = communicationRecipientQuery.Include(a => a.PersonAlias.Person.PhoneNumbers).Select(a => new { a.CreatedDateTime, a.Communication.SenderPersonAlias.Person, a.Communication, PersonAliasId = a.Communication.SenderPersonAliasId, a.SentMessage, a.Status }).ToList(); foreach (var communicationRecipient in communicationRecipientList) { var communicationRecipientResponse = new CommunicationRecipientResponse { CreatedDateTime = communicationRecipient.CreatedDateTime, PersonId = communicationRecipient.Person?.Id, FullName = communicationRecipient.Person?.FullName, IsRead = true, IsOutbound = true, RecipientPersonAliasId = communicationRecipient.PersonAliasId, SMSMessage = communicationRecipient.SentMessage, MessageStatus = communicationRecipient.Status, BinaryFileGuids = communicationRecipient.Communication.Attachments?.Select(c => c.BinaryFile.Guid).ToList() }; if (communicationRecipient.Person?.IsNameless() == true) { // if the person is nameless, we'll need to know their number since we don't know their name communicationRecipientResponse.MessageKey = communicationRecipient.Person?.PhoneNumbers.FirstOrDefault()?.Number; } else { // If the Person is not nameless, we just need to show their name, not their number communicationRecipientResponse.MessageKey = null; } communicationRecipientResponseList.Add(communicationRecipientResponse); } return(communicationRecipientResponseList.OrderBy(a => a.CreatedDateTime).ToList()); }
/// <summary> /// Gets the HTML preview. /// </summary> /// <param name="communication">The communication.</param> /// <param name="person">The person.</param> /// <returns></returns> public override string GetHtmlPreview( Model.Communication communication, Person person ) { var rockContext = new RockContext(); // Requery the Communication object communication = new CommunicationService( rockContext ).Get( communication.Id ); var globalAttributes = Rock.Web.Cache.GlobalAttributesCache.Read(); var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields( null ); if ( person != null ) { mergeFields.Add( "Person", person ); var recipient = new CommunicationRecipientService( rockContext ).Queryable().Where(a => a.CommunicationId == communication.Id).Where( r => r.PersonAlias != null && r.PersonAlias.PersonId == person.Id ).FirstOrDefault(); if ( recipient != null ) { // Add any additional merge fields created through a report foreach ( var mergeField in recipient.AdditionalMergeValues ) { if ( !mergeFields.ContainsKey( mergeField.Key ) ) { mergeFields.Add( mergeField.Key, mergeField.Value ); } } } } string message = communication.GetMediumDataValue( "Message" ); return message.ResolveMergeFields( mergeFields, communication.EnabledLavaCommands ); }
/// <summary> /// Deletes the family's addresses, phone numbers, photos, viewed records, and people. /// TODO: delete attendance codes for attendance data that's about to be deleted when /// we delete the person record. /// </summary> /// <param name="families">The families.</param> /// <param name="rockContext">The rock context.</param> private void DeleteExistingFamilyData( XElement families, RockContext rockContext ) { PersonService personService = new PersonService( rockContext ); PhoneNumberService phoneNumberService = new PhoneNumberService( rockContext ); PersonViewedService personViewedService = new PersonViewedService( rockContext ); PageViewService pageViewService = new PageViewService( rockContext ); BinaryFileService binaryFileService = new BinaryFileService( rockContext ); PersonAliasService personAliasService = new PersonAliasService( rockContext ); NoteService noteService = new NoteService( rockContext ); AuthService authService = new AuthService( rockContext ); CommunicationService communicationService = new CommunicationService( rockContext ); CommunicationRecipientService communicationRecipientService = new CommunicationRecipientService( rockContext ); foreach ( var elemFamily in families.Elements( "family" ) ) { Guid guid = elemFamily.Attribute( "guid" ).Value.Trim().AsGuid(); GroupService groupService = new GroupService( rockContext ); Group family = groupService.Get( guid ); if ( family != null ) { var groupMemberService = new GroupMemberService( rockContext ); var members = groupMemberService.GetByGroupId( family.Id ); // delete the people records string errorMessage; List<int> photoIds = members.Select( m => m.Person ).Where( p => p.PhotoId != null ).Select( a => (int)a.PhotoId ).ToList(); foreach ( var person in members.Select( m => m.Person ) ) { person.GivingGroup = null; person.GivingGroupId = null; person.PhotoId = null; // delete phone numbers foreach ( var phone in phoneNumberService.GetByPersonId( person.Id ) ) { if ( phone != null ) { phoneNumberService.Delete( phone ); } } // delete communication recipient foreach ( var recipient in communicationRecipientService.Queryable().Where( r => r.PersonId == person.Id ) ) { communicationRecipientService.Delete( recipient ); } // delete communication foreach ( var communication in communicationService.Queryable().Where( c => c.SenderPersonId == person.Id ) ) { communicationService.Delete( communication ); } // delete person viewed records foreach ( var view in personViewedService.GetByTargetPersonId( person.Id ) ) { personViewedService.Delete( view ); } // delete page viewed records foreach ( var view in pageViewService.GetByPersonId( person.Id ) ) { pageViewService.Delete( view ); } // delete notes created by them or on their record. foreach ( var note in noteService.Queryable().Where ( n => n.CreatedByPersonAlias.PersonId == person.Id || (n.NoteType.EntityTypeId == _personEntityTypeId && n.EntityId == person.Id ) ) ) { noteService.Delete( note ); } //// delete any GroupMember records they have //foreach ( var groupMember in groupMemberService.Queryable().Where( gm => gm.PersonId == person.Id ) ) //{ // groupMemberService.Delete( groupMember ); //} //// delete any Authorization data //foreach ( var auth in authService.Queryable().Where( a => a.PersonId == person.Id ) ) //{ // authService.Delete( auth ); //} // delete their aliases foreach ( var alias in personAliasService.Queryable().Where( a => a.PersonId == person.Id ) ) { personAliasService.Delete( alias ); } //foreach ( var relationship in person.Gro) // Save these changes so the CanDelete passes the check... //rockContext.ChangeTracker.DetectChanges(); rockContext.SaveChanges( disablePrePostProcessing: true ); if ( personService.CanDelete( person, out errorMessage ) ) { personService.Delete( person ); //rockContext.ChangeTracker.DetectChanges(); //rockContext.SaveChanges( disablePrePostProcessing: true ); } else { throw new Exception( string.Format( "Trying to delete {0}, but: {1}", person.FullName, errorMessage ) ); } } //rockContext.ChangeTracker.DetectChanges(); rockContext.SaveChanges( disablePrePostProcessing: true ); // delete all member photos foreach ( var photo in binaryFileService.GetByIds( photoIds ) ) { binaryFileService.Delete( photo ); } DeleteGroupAndMemberData( family, rockContext ); } } }
/// <summary> /// Binds the grid. /// </summary> private void BindGrid() { var rockContext = new RockContext(); var communications = new CommunicationService( rockContext ) .Queryable().AsNoTracking() .Where( c => c.Status != CommunicationStatus.Transient ); string subject = tbSubject.Text; if ( !string.IsNullOrWhiteSpace( subject ) ) { communications = communications.Where( c => c.Subject.Contains( subject ) ); } Guid? entityTypeGuid = cpMedium.SelectedValue.AsGuidOrNull(); if ( entityTypeGuid.HasValue ) { communications = communications.Where( c => c.MediumEntityType != null && c.MediumEntityType.Guid.Equals( entityTypeGuid.Value ) ); } string status = ddlStatus.SelectedValue; if ( !string.IsNullOrWhiteSpace( status ) ) { var communicationStatus = (CommunicationStatus)System.Enum.Parse( typeof( CommunicationStatus ), status ); communications = communications.Where( c => c.Status == communicationStatus ); } if ( canApprove ) { if ( ppSender.PersonId.HasValue ) { communications = communications .Where( c => c.SenderPersonAlias != null && c.SenderPersonAlias.PersonId == ppSender.PersonId.Value ); } } else { // If can't approve, only show current person's communications communications = communications .Where( c => c.SenderPersonAlias != null && c.SenderPersonAlias.PersonId == CurrentPersonId ); } if ( drpDates.LowerValue.HasValue ) { communications = communications.Where( a => a.CreatedDateTime >= drpDates.LowerValue.Value ); } if ( drpDates.UpperValue.HasValue ) { DateTime upperDate = drpDates.UpperValue.Value.Date.AddDays( 1 ); communications = communications.Where( a => a.CreatedDateTime < upperDate ); } string content = tbContent.Text; if ( !string.IsNullOrWhiteSpace( content ) ) { communications = communications.Where( c => c.MediumDataJson.Contains( content ) ); } var recipients = new CommunicationRecipientService( rockContext ).Queryable(); var queryable = communications .Select( c => new CommunicationItem { Id = c.Id, MediumEntityTypeId = c.MediumEntityTypeId, MediumName = c.MediumEntityTypeId.HasValue ? c.MediumEntityType.FriendlyName : null, Subject = c.Subject, CreatedDateTime = c.CreatedDateTime, Sender = c.SenderPersonAlias != null ? c.SenderPersonAlias.Person : null, ReviewedDateTime = c.ReviewedDateTime, Reviewer = c.ReviewerPersonAlias != null ? c.ReviewerPersonAlias.Person : null, Status = c.Status, Recipients = recipients.Where( r => r.CommunicationId == c.Id ).Count(), PendingRecipients = recipients.Where( r => r.CommunicationId == c.Id && r.Status == CommunicationRecipientStatus.Pending ).Count(), CancelledRecipients = recipients.Where( r => r.CommunicationId == c.Id && r.Status == CommunicationRecipientStatus.Cancelled ).Count(), FailedRecipients = recipients.Where( r => r.CommunicationId == c.Id && r.Status == CommunicationRecipientStatus.Failed ).Count(), DeliveredRecipients = recipients.Where( r => r.CommunicationId == c.Id && ( r.Status == CommunicationRecipientStatus.Delivered || r.Status == CommunicationRecipientStatus.Opened ) ).Count(), OpenedRecipients = recipients.Where( r => r.CommunicationId == c.Id && r.Status == CommunicationRecipientStatus.Opened ).Count() }); var sortProperty = gCommunication.SortProperty; if ( sortProperty != null ) { queryable = queryable.Sort( sortProperty ); } else { queryable = queryable.OrderByDescending( c => c.CreatedDateTime ); } gCommunication.EntityTypeId = EntityTypeCache.Read<Rock.Model.Communication>().Id; gCommunication.SetLinqDataSource( queryable ); gCommunication.DataBind(); }
/// <summary> /// Executes the specified context. /// </summary> /// <param name="context">The context.</param> public virtual void Execute( IJobExecutionContext context ) { JobDataMap dataMap = context.JobDetail.JobDataMap; var beginWindow = RockDateTime.Now.AddDays( 0 - dataMap.GetInt( "ExpirationPeriod" ) ); var endWindow = RockDateTime.Now.AddMinutes( 0 - dataMap.GetInt( "DelayPeriod" ) ); var nowDate = RockDateTime.Now; var rockContext = new RockContext(); var qryPendingRecipients = new CommunicationRecipientService( rockContext ).Queryable().Where( a => a.Status == CommunicationRecipientStatus.Pending ); var qry = new CommunicationService( rockContext ).Queryable() .Where( c => c.Status == CommunicationStatus.Approved && qryPendingRecipients.Where( r => r.CommunicationId == c.Id ).Any() && ( ( !c.FutureSendDateTime.HasValue && c.CreatedDateTime.HasValue && c.CreatedDateTime.Value.CompareTo( beginWindow ) >= 0 && c.CreatedDateTime.Value.CompareTo( endWindow ) <= 0 ) || ( c.FutureSendDateTime.HasValue && c.FutureSendDateTime.Value.CompareTo( beginWindow ) >= 0 && c.FutureSendDateTime.Value.CompareTo( nowDate ) <= 0 ) ) ); var exceptionMsgs = new List<string>(); int communicationsSent = 0; foreach ( var comm in qry.AsNoTracking().ToList() ) { try { var medium = comm.Medium; if ( medium != null && medium.IsActive ) { medium.Send( comm ); communicationsSent++; } } catch ( Exception ex ) { exceptionMsgs.Add( string.Format( "Exception occurred sending communication ID:{0}:{1} {2}", comm.Id, Environment.NewLine, ex.Messages().AsDelimited( Environment.NewLine + " " ) ) ); ExceptionLogService.LogException( ex, System.Web.HttpContext.Current ); } } if ( communicationsSent > 0 ) { context.Result = string.Format( "Sent {0} {1}", communicationsSent, "communication".PluralizeIf( communicationsSent > 1 ) ); } else { context.Result = "No communications to send"; } if ( exceptionMsgs.Any() ) { throw new Exception( "One or more exceptions occurred sending communications..." + Environment.NewLine + exceptionMsgs.AsDelimited( Environment.NewLine ) ); } // check for communications that have not been sent but are past the expire date. Mark them as failed and set a warning. var qryExpired = new CommunicationService( rockContext ).Queryable() .Where( c => c.Status == CommunicationStatus.Approved && qryPendingRecipients.Where( r => r.CommunicationId == c.Id ).Any() && ( (!c.FutureSendDateTime.HasValue && c.CreatedDateTime.HasValue && c.CreatedDateTime.Value.CompareTo( beginWindow ) < 0 ) || (c.FutureSendDateTime.HasValue && c.FutureSendDateTime.Value.CompareTo( beginWindow ) < 0 ) ) ); foreach ( var comm in qryExpired.ToList() ) { foreach ( var recipient in comm.Recipients.Where( r => r.Status == CommunicationRecipientStatus.Pending ) ) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Communication was not sent before the expire window (possibly due to delayed approval)."; rockContext.SaveChanges(); } } }
/// <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 object communication = new CommunicationService( rockContext ).Get( 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 globalAttributes = Rock.Web.Cache.GlobalAttributesCache.Read(); string fromAddress = communication.GetChannelDataValue( "FromAddress" ); string replyTo = communication.GetChannelDataValue( "ReplyTo" ); // Check to make sure sending domain is a safe sender var safeDomains = globalAttributes.GetValue( "SafeSenderDomains" ).SplitDelimitedValues().ToList(); var emailParts = fromAddress.Split( new char[] { '@' }, StringSplitOptions.RemoveEmptyEntries ); if (emailParts.Length != 2 || !safeDomains.Contains(emailParts[1],StringComparer.OrdinalIgnoreCase)) { if (string.IsNullOrWhiteSpace(replyTo)) { replyTo = fromAddress; } fromAddress = globalAttributes.GetValue("OrganizationEmail"); } // From MailMessage message = new MailMessage(); message.From = new MailAddress( fromAddress, communication.GetChannelDataValue( "FromName" ) ); // Reply To if ( !string.IsNullOrWhiteSpace( replyTo ) ) { message.ReplyToList.Add( new MailAddress( replyTo ) ); } // CC string cc = communication.GetChannelDataValue( "CC" ); if ( !string.IsNullOrWhiteSpace( cc ) ) { foreach ( string ccRecipient in cc.SplitDelimitedValues() ) { message.CC.Add( new MailAddress( ccRecipient ) ); } } // BCC string bcc = communication.GetChannelDataValue( "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.GetChannelDataValue( "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 ) { Stream stream = new MemoryStream( binaryFile.Data.Content ); message.Attachments.Add( new Attachment( stream, binaryFile.FileName ) ); } } } } var recipientService = new CommunicationRecipientService( rockContext ); var globalConfigValues = Rock.Web.Cache.GlobalAttributesCache.GetMergeFields( null ); bool recipientFound = true; while ( recipientFound ) { var recipient = recipientService.Get( communication.Id, CommunicationRecipientStatus.Pending ).FirstOrDefault(); if ( recipient != null ) { if ( string.IsNullOrWhiteSpace( recipient.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.Person.Email, recipient.Person.FullName ) ); // Create merge field dictionary var mergeObjects = recipient.CommunicationMergeValues( globalConfigValues ); // Subject message.Subject = communication.Subject.ResolveMergeFields( mergeObjects ); // 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.Channel.Email.ProcessTextBody( communication, globalAttributes, mergeObjects ); if ( !string.IsNullOrWhiteSpace( plainTextBody ) ) { AlternateView plainTextView = AlternateView.CreateAlternateViewFromString( plainTextBody, new ContentType( MediaTypeNames.Text.Plain ) ); message.AlternateViews.Add( plainTextView ); } // Add Html view string htmlBody = Rock.Communication.Channel.Email.ProcessHtmlBody( communication, globalAttributes, mergeObjects ); if ( !string.IsNullOrWhiteSpace( htmlBody ) ) { AlternateView htmlView = AlternateView.CreateAlternateViewFromString( htmlBody, new 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; } catch ( Exception ex ) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "SMTP Exception: " + ex.Message; } } rockContext.SaveChanges(); } else { recipientFound = false; } } } }
/// <summary> /// Deletes the family's addresses, phone numbers, photos, viewed records, and people. /// TODO: delete attendance codes for attendance data that's about to be deleted when /// we delete the person record. /// </summary> /// <param name="families">The families.</param> /// <param name="rockContext">The rock context.</param> private void DeleteExistingFamilyData( XElement families, RockContext rockContext ) { PersonService personService = new PersonService( rockContext ); PhoneNumberService phoneNumberService = new PhoneNumberService( rockContext ); PersonViewedService personViewedService = new PersonViewedService( rockContext ); PageViewService pageViewService = new PageViewService( rockContext ); BinaryFileService binaryFileService = new BinaryFileService( rockContext ); PersonAliasService personAliasService = new PersonAliasService( rockContext ); PersonDuplicateService personDuplicateService = new PersonDuplicateService( rockContext ); NoteService noteService = new NoteService( rockContext ); AuthService authService = new AuthService( rockContext ); CommunicationService communicationService = new CommunicationService( rockContext ); CommunicationRecipientService communicationRecipientService = new CommunicationRecipientService( rockContext ); FinancialBatchService financialBatchService = new FinancialBatchService( rockContext ); FinancialTransactionService financialTransactionService = new FinancialTransactionService( rockContext ); PersonPreviousNameService personPreviousNameService = new PersonPreviousNameService( rockContext ); ConnectionRequestService connectionRequestService = new ConnectionRequestService( rockContext ); ConnectionRequestActivityService connectionRequestActivityService = new ConnectionRequestActivityService( rockContext ); // delete the batch data List<int> imageIds = new List<int>(); foreach ( var batch in financialBatchService.Queryable().Where( b => b.Name.StartsWith( "SampleData" ) ) ) { imageIds.AddRange( batch.Transactions.SelectMany( t => t.Images ).Select( i => i.BinaryFileId ).ToList() ); financialTransactionService.DeleteRange( batch.Transactions ); financialBatchService.Delete( batch ); } // delete all transaction images foreach ( var image in binaryFileService.GetByIds( imageIds ) ) { binaryFileService.Delete( image ); } foreach ( var elemFamily in families.Elements( "family" ) ) { Guid guid = elemFamily.Attribute( "guid" ).Value.Trim().AsGuid(); GroupService groupService = new GroupService( rockContext ); Group family = groupService.Get( guid ); if ( family != null ) { var groupMemberService = new GroupMemberService( rockContext ); var members = groupMemberService.GetByGroupId( family.Id, true ); // delete the people records string errorMessage; List<int> photoIds = members.Select( m => m.Person ).Where( p => p.PhotoId != null ).Select( a => (int)a.PhotoId ).ToList(); foreach ( var person in members.Select( m => m.Person ) ) { person.GivingGroup = null; person.GivingGroupId = null; person.PhotoId = null; // delete phone numbers foreach ( var phone in phoneNumberService.GetByPersonId( person.Id ) ) { if ( phone != null ) { phoneNumberService.Delete( phone ); } } // delete communication recipient foreach ( var recipient in communicationRecipientService.Queryable().Where( r => r.PersonAlias.PersonId == person.Id ) ) { communicationRecipientService.Delete( recipient ); } // delete communication foreach ( var communication in communicationService.Queryable().Where( c => c.SenderPersonAliasId == person.PrimaryAlias.Id ) ) { communicationService.Delete( communication ); } // delete person viewed records foreach ( var view in personViewedService.GetByTargetPersonId( person.Id ) ) { personViewedService.Delete( view ); } // delete page viewed records foreach ( var view in pageViewService.GetByPersonId( person.Id ) ) { pageViewService.Delete( view ); } // delete notes created by them or on their record. foreach ( var note in noteService.Queryable().Where ( n => n.CreatedByPersonAlias.PersonId == person.Id || (n.NoteType.EntityTypeId == _personEntityTypeId && n.EntityId == person.Id ) ) ) { noteService.Delete( note ); } // delete previous names on their records foreach ( var previousName in personPreviousNameService.Queryable().Where( r => r.PersonAlias.PersonId == person.Id ) ) { personPreviousNameService.Delete( previousName ); } // delete any GroupMember records they have foreach ( var groupMember in groupMemberService.Queryable().Where( gm => gm.PersonId == person.Id ) ) { groupMemberService.Delete( groupMember ); } //// delete any Authorization data //foreach ( var auth in authService.Queryable().Where( a => a.PersonId == person.Id ) ) //{ // authService.Delete( auth ); //} // delete their aliases foreach ( var alias in personAliasService.Queryable().Where( a => a.PersonId == person.Id ) ) { foreach ( var duplicate in personDuplicateService.Queryable().Where( d => d.DuplicatePersonAliasId == alias.Id ) ) { personDuplicateService.Delete( duplicate ); } personAliasService.Delete( alias ); } // delete any connection requests tied to them foreach ( var request in connectionRequestService.Queryable().Where( r => r.PersonAlias.PersonId == person.Id || r.ConnectorPersonAlias.PersonId == person.Id ) ) { connectionRequestActivityService.DeleteRange( request.ConnectionRequestActivities ); connectionRequestService.Delete( request ); } // Save these changes so the CanDelete passes the check... //rockContext.ChangeTracker.DetectChanges(); rockContext.SaveChanges( disablePrePostProcessing: true ); if ( personService.CanDelete( person, out errorMessage ) ) { personService.Delete( person ); //rockContext.ChangeTracker.DetectChanges(); //rockContext.SaveChanges( disablePrePostProcessing: true ); } else { throw new Exception( string.Format( "Trying to delete {0}, but: {1}", person.FullName, errorMessage ) ); } } //rockContext.ChangeTracker.DetectChanges(); rockContext.SaveChanges( disablePrePostProcessing: true ); // delete all member photos foreach ( var photo in binaryFileService.GetByIds( photoIds ) ) { binaryFileService.Delete( photo ); } DeleteGroupAndMemberData( family, rockContext ); } } }
/// <summary> /// Gets the communication. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="peopleIds">The people ids.</param> /// <returns></returns> private Rock.Model.Communication GetCommunication( RockContext rockContext, List<int> peopleIds ) { var communicationService = new CommunicationService(rockContext); var recipientService = new CommunicationRecipientService(rockContext); GetTemplateData(); Rock.Model.Communication communication = new Rock.Model.Communication(); communication.Status = CommunicationStatus.Transient; communication.SenderPersonAliasId = CurrentPersonAliasId; communicationService.Add( communication ); communication.IsBulkCommunication = true; communication.MediumEntityTypeId = EntityTypeCache.Read( "Rock.Communication.Medium.Email" ).Id; communication.FutureSendDateTime = null; // add each person as a recipient to the communication if ( peopleIds != null ) { foreach ( var personId in peopleIds ) { if ( !communication.Recipients.Any( r => r.PersonAlias.PersonId == personId ) ) { var communicationRecipient = new CommunicationRecipient(); communicationRecipient.PersonAlias = new PersonAliasService( rockContext ).GetPrimaryAlias( personId ); communication.Recipients.Add( communicationRecipient ); } } } // add the MediumData to the communication communication.MediumData.Clear(); foreach ( var keyVal in MediumData ) { if ( !string.IsNullOrEmpty( keyVal.Value ) ) { communication.MediumData.Add( keyVal.Key, keyVal.Value ); } } if ( communication.MediumData.ContainsKey( "Subject" ) ) { communication.Subject = communication.MediumData["Subject"]; communication.MediumData.Remove( "Subject" ); } return communication; }
/// <summary> /// Binds the grid. /// </summary> private void BindGrid() { var rockContext = new RockContext(); var communications = new CommunicationService( rockContext ) .Queryable( "ChannelEntityType,Sender,Reviewer" ) .Where( c => c.Status != CommunicationStatus.Transient ); string subject = rFilter.GetUserPreference( "Subject" ); if ( !string.IsNullOrWhiteSpace( subject ) ) { communications = communications.Where( c => c.Subject.Contains( subject ) ); } Guid entityTypeGuid = Guid.Empty; if ( Guid.TryParse( rFilter.GetUserPreference( "Channel" ), out entityTypeGuid ) ) { communications = communications.Where( c => c.ChannelEntityType != null && c.ChannelEntityType.Guid.Equals( entityTypeGuid ) ); } string status = rFilter.GetUserPreference( "Status" ); if ( !string.IsNullOrWhiteSpace( status ) ) { var communicationStatus = (CommunicationStatus)System.Enum.Parse( typeof( CommunicationStatus ), status ); communications = communications.Where( c => c.Status == communicationStatus ); } if ( canApprove ) { int personId = 0; if ( int.TryParse( rFilter.GetUserPreference( "Created By" ), out personId ) && personId != 0 ) { communications = communications.Where( c => c.SenderPersonId.HasValue && c.SenderPersonId.Value == personId ); } } else { communications = communications.Where( c => c.SenderPersonId.HasValue && c.SenderPersonId.Value == CurrentPersonId ); } string content = rFilter.GetUserPreference( "Content" ); if ( !string.IsNullOrWhiteSpace( content ) ) { communications = communications.Where( c => c.ChannelDataJson.Contains( content ) ); } var drp = new DateRangePicker(); drp.DelimitedValues = rFilter.GetUserPreference( "Date Range" ); if ( drp.LowerValue.HasValue ) { communications = communications.Where( a => a.ReviewedDateTime >= drp.LowerValue.Value ); } if ( drp.UpperValue.HasValue ) { DateTime upperDate = drp.UpperValue.Value.Date.AddDays( 1 ); communications = communications.Where( a => a.ReviewedDateTime < upperDate ); } var recipients = new CommunicationRecipientService( rockContext ).Queryable(); var queryable = communications .Select( c => new CommunicationItem { Id = c.Id, Communication = c, Recipients = recipients .Where( r => r.CommunicationId == c.Id) .Count(), PendingRecipients = recipients .Where( r => r.CommunicationId == c.Id && r.Status == CommunicationRecipientStatus.Pending) .Count(), CancelledRecipients = recipients .Where( r => r.CommunicationId == c.Id && r.Status == CommunicationRecipientStatus.Cancelled) .Count(), FailedRecipients = recipients .Where( r => r.CommunicationId == c.Id && r.Status == CommunicationRecipientStatus.Failed) .Count(), DeliveredRecipients = recipients .Where( r => r.CommunicationId == c.Id && (r.Status == CommunicationRecipientStatus.Delivered || r.Status == CommunicationRecipientStatus.Opened)) .Count(), OpenedRecipients = recipients .Where( r => r.CommunicationId == c.Id && r.Status == CommunicationRecipientStatus.Opened) .Count() } ); var sortProperty = gCommunication.SortProperty; if ( sortProperty != null ) { queryable = queryable.Sort( sortProperty ); } else { queryable = queryable.OrderByDescending( c => c.Communication.Id ); } // Get the channel names var channels = new Dictionary<int, string>(); foreach ( var item in Rock.Communication.ChannelContainer.Instance.Components.Values ) { var entityType = item.Value.EntityType; channels.Add( entityType.Id, item.Metadata.ComponentName ); } var communicationItems = queryable.ToList(); foreach( var c in communicationItems) { c.ChannelName = channels.ContainsKey( c.Communication.ChannelEntityTypeId ?? 0 ) ? channels[c.Communication.ChannelEntityTypeId ?? 0] : c.Communication.ChannelEntityType.FriendlyName; } gCommunication.DataSource = communicationItems; gCommunication.DataBind(); }
/// <summary> /// Process inbound messages that are sent to a SMS number. /// </summary> /// <param name="toPhone">The phone number a message is sent to.</param> /// <param name="fromPhone">The phone number a message is sent from.</param> /// <param name="message">The message that was sent.</param> /// <param name="errorMessage">The error message.</param> public void ProcessResponse( string toPhone, string fromPhone, string message, out string errorMessage ) { errorMessage = string.Empty; int toPersonId = -1; string transportPhone = string.Empty; Rock.Data.RockContext rockContext = new Rock.Data.RockContext(); // get from person var fromPerson = new PersonService( rockContext ).Queryable() .Where( p => p.PhoneNumbers.Any( n => (n.CountryCode + n.Number) == fromPhone.Replace( "+", "" ) ) ) .OrderBy( p => p.Id ).FirstOrDefault(); // order by person id to get the oldest person to help with duplicate records of the response recipient // get recipient from defined value var definedType = DefinedTypeCache.Read( Rock.SystemGuid.DefinedType.COMMUNICATION_SMS_FROM.AsGuid() ); if ( definedType != null ) { if ( definedType.DefinedValues != null && definedType.DefinedValues.Any() ) { var matchValue = definedType.DefinedValues.Where( v => v.Value == toPhone ).OrderBy( v => v.Order ).FirstOrDefault(); if ( matchValue != null ) { var toPersonGuid = matchValue.GetAttributeValue( "ResponseRecipient" ); transportPhone = matchValue.Id.ToString(); if ( toPersonGuid != null ) { var toPerson = new PersonAliasService( rockContext ).Get( new Guid( toPersonGuid ) ); toPersonId = toPerson.PersonId; } } } } if ( fromPerson != null && toPersonId != -1 ) { if ( toPersonId == fromPerson.Id ) // message from the channel recipient { // look for response code in the message Match match = Regex.Match( message, @"@\d{3}" ); if ( match.Success ) { string responseCode = match.ToString(); var recipient = new CommunicationRecipientService( rockContext ).Queryable("Communication") .Where( r => r.ResponseCode == responseCode ) .OrderByDescending(r => r.CreatedDateTime).FirstOrDefault(); if ( recipient != null ) { CreateCommunication( fromPerson.Id, fromPerson.FullName, recipient.Communication.SenderPersonId.Value, message.Replace(responseCode, ""), transportPhone, "", rockContext ); } else // send a warning message back to the channel recipient { string warningMessage = string.Format( "A conversation could not be found with the response token {0}.", responseCode ); CreateCommunication( fromPerson.Id, fromPerson.FullName, fromPerson.Id, warningMessage, transportPhone, "", rockContext ); } } } else // response from someone other than the channel recipient { string messageId = GenerateResponseCode( rockContext ); message = string.Format( "-{0}-\n{1}\n( {2} )", fromPerson.FullName, message, messageId ); CreateCommunication( fromPerson.Id, fromPerson.FullName, toPersonId, message, transportPhone, messageId, rockContext ); } } else { var globalAttributes = Rock.Web.Cache.GlobalAttributesCache.Read(); string organizationName = globalAttributes.GetValue( "OrganizationName" ); errorMessage = string.Format( "Could not deliver message. This phone number is not registered in the {0} database.", organizationName); } }
private void BindRecipients() { if ( CommunicationId.HasValue ) { var rockContext = new RockContext(); var recipients = new CommunicationRecipientService( rockContext ) .Queryable( "PersonAlias.Person,Activities" ) .Where( r => r.CommunicationId == CommunicationId.Value ); SetRecipients( pnlPending, aPending, lPending, gPending, recipients.Where( r => r.Status == CommunicationRecipientStatus.Pending ) ); SetRecipients( pnlDelivered, aDelivered, lDelivered, gDelivered, recipients.Where( r => r.Status == CommunicationRecipientStatus.Delivered || r.Status == CommunicationRecipientStatus.Opened ) ); SetRecipients( pnlFailed, aFailed, lFailed, gFailed, recipients.Where( r => r.Status == CommunicationRecipientStatus.Failed ) ); SetRecipients( pnlCancelled, aCancelled, lCancelled, gCancelled, recipients.Where( r => r.Status == CommunicationRecipientStatus.Cancelled ) ); if ( pnlOpened.Visible ) { SetRecipients( pnlOpened, aOpened, lOpened, gOpened, recipients.Where( r => r.Status == CommunicationRecipientStatus.Opened ) ); } } }
/// <summary> /// Updates a communication model with the user-entered values /// </summary> /// <param name="communicationService">The service.</param> /// <returns></returns> private Rock.Model.Communication UpdateCommunication(RockContext rockContext) { var communicationService = new CommunicationService(rockContext); var recipientService = new CommunicationRecipientService(rockContext); Rock.Model.Communication communication = null; if ( CommunicationId.HasValue ) { communication = communicationService.Get( CommunicationId.Value ); // Remove any deleted recipients foreach(var recipient in recipientService.GetByCommunicationId( CommunicationId.Value ) ) { if (!Recipients.Any( r => recipient.PersonAlias != null && r.PersonId == recipient.PersonAlias.PersonId)) { recipientService.Delete(recipient); communication.Recipients.Remove( recipient ); } } } if (communication == null) { communication = new Rock.Model.Communication(); communication.Status = CommunicationStatus.Transient; communication.SenderPersonAliasId = CurrentPersonAliasId; communicationService.Add( communication ); } // Add any new recipients foreach(var recipient in Recipients ) { if ( !communication.Recipients.Any( r => r.PersonAlias != null && r.PersonAlias.PersonId == recipient.PersonId ) ) { var person = new PersonService( rockContext ).Get( recipient.PersonId ); if ( person != null ) { var communicationRecipient = new CommunicationRecipient(); communicationRecipient.PersonAlias = person.PrimaryAlias; communication.Recipients.Add( communicationRecipient ); } } } communication.IsBulkCommunication = cbBulk.Checked; communication.MediumEntityTypeId = MediumEntityTypeId; communication.MediumData.Clear(); GetMediumData(); foreach ( var keyVal in MediumData ) { if ( !string.IsNullOrEmpty( keyVal.Value ) ) { communication.MediumData.Add( keyVal.Key, keyVal.Value ); } } if ( communication.MediumData.ContainsKey( "Subject" ) ) { communication.Subject = communication.MediumData["Subject"]; communication.MediumData.Remove( "Subject" ); } DateTime? futureSendDate = dtpFutureSend.SelectedDateTime; if ( futureSendDate.HasValue && futureSendDate.Value.CompareTo( RockDateTime.Now ) > 0 ) { communication.FutureSendDateTime = futureSendDate; } else { communication.FutureSendDateTime = null; } return communication; }
/// <summary> /// Shows the actions. /// </summary> /// <param name="communication">The communication.</param> private void ShowActions(Rock.Model.Communication communication) { bool canApprove = IsUserAuthorized( "Approve" ); // Set default visibility btnApprove.Visible = false; btnDeny.Visible = false; btnEdit.Visible = false; btnCancel.Visible = false; btnCopy.Visible = false; if ( communication != null ) { switch ( communication.Status ) { case CommunicationStatus.Transient: case CommunicationStatus.Draft: case CommunicationStatus.Denied: { // This block isn't used for transient, draft or denied communicaitons break; } case CommunicationStatus.PendingApproval: { if ( canApprove ) { btnApprove.Visible = true; btnDeny.Visible = true; btnEdit.Visible = true; } btnCancel.Visible = true; break; } case CommunicationStatus.Approved: { // If there are still any pending recipients, allow canceling of send var hasPendingRecipients = new CommunicationRecipientService( new RockContext() ).Queryable() .Where( r => r.CommunicationId == communication.Id ).Where( r => r.Status == CommunicationRecipientStatus.Pending ).Any(); btnCancel.Visible = hasPendingRecipients; btnCopy.Visible = true; break; } } } }
/// <summary> /// Gets the communications and response recipients. /// </summary> /// <param name="relatedSmsFromDefinedValueId">The related SMS from defined value identifier.</param> /// <param name="startDateTime">The start date time.</param> /// <param name="showReadMessages">if set to <c>true</c> [show read messages].</param> /// <param name="maxCount">The maximum count.</param> /// <param name="personId">The person identifier.</param> /// <returns></returns> public List <CommunicationRecipientResponse> GetCommunicationResponseRecipients(int relatedSmsFromDefinedValueId, DateTime startDateTime, bool showReadMessages, int maxCount, int?personId) { var smsMediumEntityTypeId = EntityTypeCache.GetId(SystemGuid.EntityType.COMMUNICATION_MEDIUM_SMS).Value; IQueryable <CommunicationResponse> communicationResponseQuery = this.Queryable() .Where(r => r.RelatedMediumEntityTypeId == smsMediumEntityTypeId && r.RelatedSmsFromDefinedValueId == relatedSmsFromDefinedValueId && r.CreatedDateTime >= startDateTime && r.FromPersonAliasId.HasValue); if (!showReadMessages) { communicationResponseQuery = communicationResponseQuery.Where(r => r.IsRead == false); } var personAliasQuery = personId == null ? new PersonAliasService(this.Context as RockContext).Queryable() : new PersonAliasService(this.Context as RockContext).Queryable().Where(p => p.PersonId == personId); // do an explicit LINQ inner join on PersonAlias to avoid performance issue where it would do an outer join instead var communicationResponseJoinQuery = from cr in communicationResponseQuery join pa in personAliasQuery on cr.FromPersonAliasId equals pa.Id select new { cr, pa }; IQueryable <CommunicationResponse> mostRecentCommunicationResponseQuery = communicationResponseJoinQuery .GroupBy(r => r.pa.PersonId) .Select(a => a.OrderByDescending(x => x.cr.CreatedDateTime).FirstOrDefault()) .OrderByDescending(a => a.cr.CreatedDateTime).Select(a => a.cr); IQueryable <CommunicationRecipient> communicationRecipientQuery = new CommunicationRecipientService(this.Context as RockContext).Queryable() .Where(r => r.MediumEntityTypeId == smsMediumEntityTypeId && r.Communication.SMSFromDefinedValueId == relatedSmsFromDefinedValueId && r.CreatedDateTime >= startDateTime && r.Status == CommunicationRecipientStatus.Delivered); // do an explicit LINQ inner join on PersonAlias to avoid performance issue where it would do an outer join instead var communicationRecipientJoinQuery = from cr in communicationRecipientQuery.Where(a => a.PersonAliasId.HasValue) join pa in personAliasQuery on cr.PersonAliasId.Value equals pa.Id select new { PersonId = pa.PersonId, Person = pa.Person, CreatedDateTime = cr.CreatedDateTime, cr.Communication, cr.CommunicationId, CommunicationSMSMessage = cr.Communication.SMSMessage, SentMessage = cr.SentMessage, PersonAliasId = cr.PersonAliasId }; var mostRecentCommunicationRecipientQuery = communicationRecipientJoinQuery .GroupBy(r => r.PersonId) .Select(a => a.Select(s => new { s.Person, s.CreatedDateTime, s.CommunicationSMSMessage, s.CommunicationId, s.Communication, s.SentMessage, s.PersonAliasId }).OrderByDescending(s => s.CreatedDateTime).FirstOrDefault() ); var mostRecentCommunicationResponseList = mostRecentCommunicationResponseQuery.Include(a => a.FromPersonAlias.Person).AsNoTracking().Take(maxCount).ToList(); List <CommunicationRecipientResponse> communicationRecipientResponseList = new List <CommunicationRecipientResponse>(); foreach (var mostRecentCommunicationResponse in mostRecentCommunicationResponseList) { var communicationRecipientResponse = new CommunicationRecipientResponse { CreatedDateTime = mostRecentCommunicationResponse.CreatedDateTime, PersonId = mostRecentCommunicationResponse?.FromPersonAlias.PersonId, RecordTypeValueId = mostRecentCommunicationResponse?.FromPersonAlias.Person.RecordTypeValueId, FullName = mostRecentCommunicationResponse?.FromPersonAlias.Person.FullName, IsRead = mostRecentCommunicationResponse.IsRead, MessageKey = mostRecentCommunicationResponse.MessageKey, IsOutbound = false, RecipientPersonAliasId = mostRecentCommunicationResponse.FromPersonAliasId, SMSMessage = mostRecentCommunicationResponse.Response, CommunicationResponseId = mostRecentCommunicationResponse.Id }; communicationRecipientResponseList.Add(communicationRecipientResponse); } var mostRecentCommunicationRecipientList = mostRecentCommunicationRecipientQuery.Take(maxCount).ToList(); foreach (var mostRecentCommunicationRecipient in mostRecentCommunicationRecipientList) { var communicationRecipientResponse = new CommunicationRecipientResponse { CreatedDateTime = mostRecentCommunicationRecipient.CreatedDateTime, PersonId = mostRecentCommunicationRecipient.Person.Id, RecordTypeValueId = mostRecentCommunicationRecipient.Person.RecordTypeValueId, FullName = mostRecentCommunicationRecipient.Person.FullName, IsOutbound = true, IsRead = true, MessageKey = null, // communication recipients just need to show their name, not their number RecipientPersonAliasId = mostRecentCommunicationRecipient.PersonAliasId, SMSMessage = mostRecentCommunicationRecipient.SentMessage.IsNullOrWhiteSpace() ? mostRecentCommunicationRecipient.CommunicationSMSMessage : mostRecentCommunicationRecipient.SentMessage, CommunicationId = mostRecentCommunicationRecipient.CommunicationId }; if (mostRecentCommunicationRecipient?.Person.IsNameless() == true) { // if the person is nameless, we'll need to know their number since we don't know their name communicationRecipientResponse.MessageKey = mostRecentCommunicationRecipient.Person.PhoneNumbers.FirstOrDefault()?.Number; } else { // If the Person is not nameless, we just need to show their name, not their number communicationRecipientResponse.MessageKey = null; } communicationRecipientResponseList.Add(communicationRecipientResponse); } // NOTE: We actually have up to twice the max count at this point, because we are combining results from // CommunicationRecipient and CommunicationResponse, and we took the maxCount of each of those. // Now, we see what that combination ends up looking like when we sort it by CreatedDateTime communicationRecipientResponseList = communicationRecipientResponseList .GroupBy(r => r.PersonId) .Select(a => a.OrderByDescending(x => x.CreatedDateTime).FirstOrDefault()) .OrderByDescending(a => a.CreatedDateTime).Take(maxCount).ToList(); return(communicationRecipientResponseList); }
/// <summary> /// Refresh the recipients list. /// </summary> /// <param name="rockContext">The rock context.</param> /// <returns></returns> public void RefreshCommunicationRecipientList(RockContext rockContext) { if (!ListGroupId.HasValue) { return; } var emailMediumEntityType = EntityTypeCache.Get(SystemGuid.EntityType.COMMUNICATION_MEDIUM_EMAIL.AsGuid()); var smsMediumEntityType = EntityTypeCache.Get(SystemGuid.EntityType.COMMUNICATION_MEDIUM_SMS.AsGuid()); var preferredCommunicationTypeAttribute = AttributeCache.Get(SystemGuid.Attribute.GROUPMEMBER_COMMUNICATION_LIST_PREFERRED_COMMUNICATION_MEDIUM.AsGuid()); var segmentDataViewGuids = this.Segments.SplitDelimitedValues().AsGuidList(); var segmentDataViewIds = new DataViewService(rockContext).GetByGuids(segmentDataViewGuids).Select(a => a.Id).ToList(); var qryCommunicationListMembers = GetCommunicationListMembers(rockContext, ListGroupId, this.SegmentCriteria, segmentDataViewIds); // NOTE: If this is scheduled communication, don't include Members that were added after the scheduled FutureSendDateTime if (this.FutureSendDateTime.HasValue) { var memberAddedCutoffDate = this.FutureSendDateTime; qryCommunicationListMembers = qryCommunicationListMembers.Where(a => (a.DateTimeAdded.HasValue && a.DateTimeAdded.Value < memberAddedCutoffDate) || (a.CreatedDateTime.HasValue && a.CreatedDateTime.Value < memberAddedCutoffDate)); } var communicationRecipientService = new CommunicationRecipientService(rockContext); var recipientsQry = GetRecipientsQry(rockContext); // Get all the List member which is not part of communication recipients yet var newMemberInList = qryCommunicationListMembers .Where(a => !recipientsQry.Any(r => r.PersonAlias.PersonId == a.PersonId)) .AsNoTracking() .ToList(); foreach (var newMember in newMemberInList) { var communicationRecipient = new CommunicationRecipient { PersonAliasId = newMember.Person.PrimaryAliasId.Value, Status = CommunicationRecipientStatus.Pending, CommunicationId = Id }; switch (CommunicationType) { case CommunicationType.Email: communicationRecipient.MediumEntityTypeId = emailMediumEntityType.Id; break; case CommunicationType.SMS: communicationRecipient.MediumEntityTypeId = smsMediumEntityType.Id; break; case CommunicationType.RecipientPreference: newMember.LoadAttributes(); if (preferredCommunicationTypeAttribute != null) { var recipientPreference = ( CommunicationType? )newMember .GetAttributeValue(preferredCommunicationTypeAttribute.Key).AsIntegerOrNull(); switch (recipientPreference) { case CommunicationType.SMS: communicationRecipient.MediumEntityTypeId = smsMediumEntityType.Id; break; case CommunicationType.Email: communicationRecipient.MediumEntityTypeId = emailMediumEntityType.Id; break; default: communicationRecipient.MediumEntityTypeId = emailMediumEntityType.Id; break; } } break; default: throw new Exception("Unexpected CommunicationType: " + CommunicationType.ConvertToString()); } communicationRecipientService.Add(communicationRecipient); } // Get all pending communication recipents that are no longer part of the group list member, then delete them from the Recipients var missingMemberInList = recipientsQry.Where(a => a.Status == CommunicationRecipientStatus.Pending) .Where(a => !qryCommunicationListMembers.Any(r => r.PersonId == a.PersonAlias.PersonId)) .ToList(); foreach (var missingMember in missingMemberInList) { communicationRecipientService.Delete(missingMember); } rockContext.SaveChanges(); }
/// <summary> /// Shows the detail. /// </summary> /// <param name="communication">The communication.</param> private void ShowDetail(Rock.Model.Communication communication) { Recipients.Clear(); if ( communication != null ) { this.AdditionalMergeFields = communication.AdditionalMergeFields.ToList(); lTitle.Text = ( communication.Subject ?? "New Communication" ).FormatAsHtmlTitle(); var recipientList = new CommunicationRecipientService( new RockContext() ) .Queryable() //.Include() .Where( r => r.CommunicationId == communication.Id ) .Select(a => new { a.PersonAlias.Person, PersonHasSMS = a.PersonAlias.Person.PhoneNumbers.Any( p => p.IsMessagingEnabled ), a.Status, a.StatusNote, a.OpenedClient, a.OpenedDateTime }).ToList(); Recipients = recipientList.Select( recipient => new Recipient( recipient.Person, recipient.PersonHasSMS, recipient.Status, recipient.StatusNote, recipient.OpenedClient, recipient.OpenedDateTime ) ).ToList(); } else { communication = new Rock.Model.Communication() { Status = CommunicationStatus.Transient }; communication.SenderPersonAliasId = CurrentPersonAliasId; lTitle.Text = "New Communication".FormatAsHtmlTitle(); int? personId = PageParameter( "Person" ).AsIntegerOrNull(); if ( personId.HasValue ) { communication.IsBulkCommunication = false; var person = new PersonService( new RockContext() ).Get( personId.Value ); if ( person != null ) { Recipients.Add( new Recipient( person, person.PhoneNumbers.Any(p => p.IsMessagingEnabled), CommunicationRecipientStatus.Pending, string.Empty, string.Empty, null ) ); } } } CommunicationId = communication.Id; MediumEntityTypeId = communication.MediumEntityTypeId; BindMediums(); MediumData = communication.MediumData; MediumData.AddOrReplace( "Subject", communication.Subject ); if (communication.Status == CommunicationStatus.Transient && !string.IsNullOrWhiteSpace(GetAttributeValue("DefaultTemplate"))) { var template = new CommunicationTemplateService( new RockContext() ).Get( GetAttributeValue( "DefaultTemplate" ).AsGuid() ); // If a template guid was passed in, it overrides any default template. string templateGuid = PageParameter( "templateGuid" ); if ( !string.IsNullOrEmpty( templateGuid ) ) { var guid = new Guid( templateGuid ); template = new CommunicationTemplateService( new RockContext() ).Queryable().Where( t => t.Guid == guid ).FirstOrDefault(); } if (template != null && template.MediumEntityTypeId == MediumEntityTypeId) { foreach(ListItem item in ddlTemplate.Items) { if (item.Value == template.Id.ToString()) { item.Selected = true; GetTemplateData( template.Id, false ); } else { item.Selected = false; } } } } cbBulk.Checked = communication.IsBulkCommunication; MediumControl control = LoadMediumControl( true ); InitializeControl( control ); dtpFutureSend.SelectedDateTime = communication.FutureSendDateTime; ShowStatus( communication ); ShowActions( communication ); }
/// <summary> /// Sends the specified communication. /// </summary> /// <param name="communication">The communication.</param> /// <param name="CurrentPersonId">The current person id.</param> /// <exception cref="System.NotImplementedException"></exception> public override void Send( Rock.Model.Communication communication, int? CurrentPersonId ) { 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( DateTime.Now ) > 0 ) ) { string fromPhone = string.Empty; string fromValue = communication.GetChannelDataValue( "FromValue" ); int fromValueId = int.MinValue; if ( int.TryParse( fromValue, out fromValueId ) ) { fromPhone = DefinedValueCache.Read( fromValueId ).Description; } if ( !string.IsNullOrWhiteSpace( fromPhone ) ) { string accountSid = GetAttributeValue( "SID" ); string authToken = GetAttributeValue( "Token" ); var twilio = new TwilioRestClient( accountSid, authToken ); var recipientService = new CommunicationRecipientService(); var globalConfigValues = GlobalAttributesCache.GetMergeFields( null ); bool recipientFound = true; while ( recipientFound ) { RockTransactionScope.WrapTransaction( () => { var recipient = recipientService.Get( communication.Id, CommunicationRecipientStatus.Pending ).FirstOrDefault(); if ( recipient != null ) { string phoneNumber = recipient.Person.PhoneNumbers .Where( p => p.IsMessagingEnabled ) .Select( p => p.Number ) .FirstOrDefault(); if ( string.IsNullOrWhiteSpace( phoneNumber ) ) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "No Phone Number with Messaging Enabled"; } else { // Create merge field dictionary var mergeObjects = MergeValues( globalConfigValues, recipient ); string subject = communication.Subject.ResolveMergeFields( mergeObjects ); try { twilio.SendMessage( fromPhone, phoneNumber, subject ); recipient.Status = CommunicationRecipientStatus.Success; } catch ( Exception ex ) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Twilio Exception: " + ex.Message; } } recipientService.Save( recipient, CurrentPersonId ); } else { recipientFound = false; } } ); } } } }
/// <summary> /// Sends the specified communication. /// </summary> /// <param name="communication">The communication.</param> /// <param name="CurrentPersonId">The current person id.</param> /// <exception cref="System.NotImplementedException"></exception> public override void Send( Rock.Model.Communication communication, int? CurrentPersonId ) { 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(DateTime.Now) > 0)) { // From MailMessage message = new MailMessage(); message.From = new MailAddress( communication.GetChannelDataValue( "FromAddress" ), communication.GetChannelDataValue( "FromName" ) ); // Reply To string replyTo = communication.GetChannelDataValue( "ReplyTo" ); if ( !string.IsNullOrWhiteSpace( replyTo ) ) { message.ReplyToList.Add( new MailAddress( replyTo ) ); } // CC string cc = communication.GetChannelDataValue( "CC" ); if ( !string.IsNullOrWhiteSpace( cc ) ) { foreach ( string ccRecipient in cc.SplitDelimitedValues() ) { message.CC.Add( new MailAddress( ccRecipient ) ); } } // BCC string bcc = communication.GetChannelDataValue( "BCC" ); if ( !string.IsNullOrWhiteSpace( bcc ) ) { foreach ( string bccRecipient in bcc.SplitDelimitedValues() ) { message.Bcc.Add( new MailAddress( bccRecipient ) ); } } message.IsBodyHtml = true; message.Priority = MailPriority.Normal; // Create SMTP Client SmtpClient smtpClient = new SmtpClient( GetAttributeValue( "Server" ) ); int port = int.MinValue; if ( int.TryParse( GetAttributeValue( "Port" ), out port ) ) { smtpClient.Port = port; } smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network; bool useSSL = false; smtpClient.EnableSsl = bool.TryParse( GetAttributeValue( "UseSSL" ), out useSSL ) && useSSL; string userName = GetAttributeValue( "UserName" ); string password = GetAttributeValue( "Password" ); if ( !string.IsNullOrEmpty( userName ) ) { smtpClient.UseDefaultCredentials = false; smtpClient.Credentials = new System.Net.NetworkCredential( userName, password ); } // Add Attachments string attachmentIds = communication.GetChannelDataValue( "Attachments" ); if ( !string.IsNullOrWhiteSpace( attachmentIds ) ) { var binaryFileService = new BinaryFileService(); foreach(string idVal in attachmentIds.SplitDelimitedValues()) { int binaryFileId = int.MinValue; if (int.TryParse(idVal, out binaryFileId)) { var binaryFile = binaryFileService.Get(binaryFileId); if ( binaryFile != null ) { Stream stream = new MemoryStream( binaryFile.Data.Content ); message.Attachments.Add( new Attachment( stream, binaryFile.FileName ) ); } } } } var recipientService = new CommunicationRecipientService(); var globalConfigValues = Rock.Web.Cache.GlobalAttributesCache.GetMergeFields( null ); bool recipientFound = true; while ( recipientFound ) { RockTransactionScope.WrapTransaction( () => { var recipient = recipientService.Get( communication.Id, CommunicationRecipientStatus.Pending ).FirstOrDefault(); if ( recipient != null ) { if ( string.IsNullOrWhiteSpace( recipient.Person.Email ) ) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "No Email Address"; } else { message.To.Clear(); message.To.Add( new MailAddress( recipient.Person.Email, recipient.Person.FullName ) ); // Create merge field dictionary var mergeObjects = MergeValues( globalConfigValues, recipient ); message.Subject = communication.Subject.ResolveMergeFields( mergeObjects ); message.Body = communication.GetChannelDataValue( "HtmlMessage" ).ResolveMergeFields( mergeObjects ); try { smtpClient.Send( message ); recipient.Status = CommunicationRecipientStatus.Success; } catch ( Exception ex ) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "SMTP Exception: " + ex.Message; } } recipientService.Save( recipient, CurrentPersonId ); } 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.Recipients.Where( r => r.Status == Model.CommunicationRecipientStatus.Pending ).Any() && ( !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 = recipientService.Get( communication.Id, CommunicationRecipientStatus.Pending ).FirstOrDefault(); 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" ); 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; } } } } }
public int GetRecipientCount(RockContext rockContext) { var count = new CommunicationRecipientService(rockContext).Queryable().Where(a => a.CommunicationId == this.Id).Count(); return(count); }
/// <summary> /// Gets the HTML preview. /// </summary> /// <param name="communication">The communication.</param> /// <param name="person">The person.</param> /// <returns></returns> public override string GetHtmlPreview( Model.Communication communication, Person person ) { var rockContext = new RockContext(); StringBuilder sbContent = new StringBuilder(); var globalAttributes = Rock.Web.Cache.GlobalAttributesCache.Read(); var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields( null ); // Requery the Communication object communication = new CommunicationService( rockContext ).Get( communication.Id ); mergeFields.Add( "Communication", communication ); if ( person != null ) { mergeFields.Add( "Person", person ); var recipient = new CommunicationRecipientService( rockContext ).Queryable().Where( a => a.CommunicationId == communication.Id ).Where( r => r.PersonAlias != null && r.PersonAlias.PersonId == person.Id ).FirstOrDefault(); if ( recipient != null ) { // Add any additional merge fields created through a report foreach ( var mergeField in recipient.AdditionalMergeValues ) { if ( !mergeFields.ContainsKey( mergeField.Key ) ) { mergeFields.Add( mergeField.Key, mergeField.Value ); } } } } // Body string htmlContent = communication.GetMediumDataValue( "HtmlMessage" ); sbContent.Append( Email.ProcessHtmlBody( communication, globalAttributes, mergeFields ) ); // Attachments StringBuilder sbAttachments = new StringBuilder(); string attachmentIds = communication.GetMediumDataValue( "Attachments" ); if ( !string.IsNullOrWhiteSpace( attachmentIds ) ) { sbContent.Append( "<br/><br/>" ); 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 ) { sbContent.AppendFormat( "<a target='_blank' href='{0}GetFile.ashx?id={1}'>{2}</a><br/>", System.Web.VirtualPathUtility.ToAbsolute( "~" ), binaryFile.Id, binaryFile.FileName ); } } } } return sbContent.ToString(); }
/// <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; using ( 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 { 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( globalConfigValues ); // Subject message.Subject = communication.Subject.ResolveMergeFields( mergeObjects, currentPerson ); // 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 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 ); // 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 ); } 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 = "Exception: " + ex.Message; } } rockContext.SaveChanges(); } else { recipientFound = false; } } } } } }
/// <summary> /// Refresh the recipients list. /// </summary> /// <param name="rockContext">The rock context.</param> /// <returns></returns> public void RefreshCommunicationRecipientList(RockContext rockContext) { if (!ListGroupId.HasValue) { return; } var segmentDataViewGuids = this.Segments.SplitDelimitedValues().AsGuidList(); var segmentDataViewIds = new DataViewService(rockContext).GetByGuids(segmentDataViewGuids).Select(a => a.Id).ToList(); var qryCommunicationListMembers = GetCommunicationListMembers(rockContext, ListGroupId, this.SegmentCriteria, segmentDataViewIds); // NOTE: If this is scheduled communication, don't include Members that were added after the scheduled FutureSendDateTime. // However, don't exclude if the date added can't be determined or they will never be sent a scheduled communication. if (this.FutureSendDateTime.HasValue) { var memberAddedCutoffDate = this.FutureSendDateTime; qryCommunicationListMembers = qryCommunicationListMembers.Where(a => (a.DateTimeAdded.HasValue && a.DateTimeAdded.Value < memberAddedCutoffDate) || (a.CreatedDateTime.HasValue && a.CreatedDateTime.Value < memberAddedCutoffDate) || (!a.DateTimeAdded.HasValue && !a.CreatedDateTime.HasValue)); } var communicationRecipientService = new CommunicationRecipientService(rockContext); var recipientsQry = GetRecipientsQry(rockContext); // Get all the List member which is not part of communication recipients yet var newMemberInList = qryCommunicationListMembers .Include(c => c.Person) .Where(a => !recipientsQry.Any(r => r.PersonAlias.PersonId == a.PersonId)) .AsNoTracking() .ToList(); var emailMediumEntityType = EntityTypeCache.Get(SystemGuid.EntityType.COMMUNICATION_MEDIUM_EMAIL.AsGuid()); var smsMediumEntityType = EntityTypeCache.Get(SystemGuid.EntityType.COMMUNICATION_MEDIUM_SMS.AsGuid()); var pushMediumEntityType = EntityTypeCache.Get(SystemGuid.EntityType.COMMUNICATION_MEDIUM_PUSH_NOTIFICATION.AsGuid()); var recipientsToAdd = newMemberInList.Select(m => new CommunicationRecipient { PersonAliasId = m.Person.PrimaryAliasId.Value, Status = CommunicationRecipientStatus.Pending, CommunicationId = Id, MediumEntityTypeId = DetermineMediumEntityTypeId( emailMediumEntityType.Id, smsMediumEntityType.Id, pushMediumEntityType.Id, CommunicationType, m.CommunicationPreference, m.Person.CommunicationPreference) }); rockContext.BulkInsert <CommunicationRecipient>(recipientsToAdd); // Get all pending communication recipients that are no longer part of the group list member, then delete them from the Recipients var missingMemberInList = recipientsQry.Where(a => a.Status == CommunicationRecipientStatus.Pending) .Where(a => !qryCommunicationListMembers.Any(r => r.PersonId == a.PersonAlias.PersonId)); rockContext.BulkDelete <CommunicationRecipient>(missingMemberInList); rockContext.SaveChanges(); }