/// <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> /// 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); }
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> /// 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> /// 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 ) { using ( var communicationRockContext = new RockContext() ) { // Requery the Communication object in case we need to load any properties from the database communication = new CommunicationService( communicationRockContext ) .Queryable( "CreatedByPersonAlias.Person" ) .FirstOrDefault( c => c.Id == communication.Id ); bool hasPendingRecipients; if ( communication != null && communication.Status == Model.CommunicationStatus.Approved && ( !communication.FutureSendDateTime.HasValue || communication.FutureSendDateTime.Value.CompareTo( RockDateTime.Now ) <= 0 )) { var qryRecipients = new CommunicationRecipientService( communicationRockContext ).Queryable(); hasPendingRecipients = qryRecipients.Where( a => a.CommunicationId == communication.Id ).Where( r => r.Status == Model.CommunicationRecipientStatus.Pending ).Any(); } else { hasPendingRecipients = false; } if ( hasPendingRecipients ) { var currentPerson = communication.CreatedByPersonAlias.Person; var globalAttributes = Rock.Web.Cache.GlobalAttributesCache.Read(); var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields( null, currentPerson ); // From - if none is set, use the one in the Organization's GlobalAttributes. string fromAddress = communication.GetMediumDataValue( "FromAddress" ); if ( string.IsNullOrWhiteSpace( fromAddress ) ) { fromAddress = globalAttributes.GetValue( "OrganizationEmail" ); } string fromName = communication.GetMediumDataValue( "FromName" ); if ( string.IsNullOrWhiteSpace( fromName ) ) { fromName = globalAttributes.GetValue( "OrganizationName" ); } // Resolve any possible merge fields in the from address fromAddress = fromAddress.ResolveMergeFields( mergeFields, currentPerson, communication.EnabledLavaCommands ); fromName = fromName.ResolveMergeFields( mergeFields, currentPerson, communication.EnabledLavaCommands ); MailMessage message = new MailMessage(); message.From = new MailAddress( fromAddress, fromName ); // Reply To try { string replyTo = communication.GetMediumDataValue( "ReplyTo" ); if ( !string.IsNullOrWhiteSpace( replyTo ) ) { message.ReplyToList.Add( new MailAddress( replyTo ) ); } } catch { } CheckSafeSender( message, globalAttributes ); // CC string cc = communication.GetMediumDataValue( "CC" ); if ( !string.IsNullOrWhiteSpace( cc ) ) { foreach ( string ccRecipient in cc.SplitDelimitedValues() ) { message.CC.Add( new MailAddress( ccRecipient ) ); } } // BCC string bcc = communication.GetMediumDataValue( "BCC" ); if ( !string.IsNullOrWhiteSpace( bcc ) ) { foreach ( string bccRecipient in bcc.SplitDelimitedValues() ) { message.Bcc.Add( new MailAddress( bccRecipient ) ); } } message.IsBodyHtml = true; message.Priority = MailPriority.Normal; using ( var smtpClient = GetSmtpClient() ) { // Add Attachments var attachments = new List<BinaryFile>(); string attachmentIds = communication.GetMediumDataValue( "Attachments" ); if ( !string.IsNullOrWhiteSpace( attachmentIds ) ) { var binaryFileService = new BinaryFileService( communicationRockContext ); foreach ( int binaryFileId in attachmentIds.SplitDelimitedValues().AsIntegerList() ) { var binaryFile = binaryFileService.Get( binaryFileId ); if ( binaryFile != null ) { attachments.Add( binaryFile ); } } } var personEntityTypeId = EntityTypeCache.Read( "Rock.Model.Person" ).Id; var communicationEntityTypeId = EntityTypeCache.Read( "Rock.Model.Communication" ).Id; var communicationCategoryId = CategoryCache.Read( Rock.SystemGuid.Category.HISTORY_PERSON_COMMUNICATIONS.AsGuid(), communicationRockContext ).Id; bool recipientFound = true; while ( recipientFound ) { // make a new rockContext per recipient so that DbChangeTracker doesn't get gummed up on large communications var recipientRockContext = new RockContext(); var recipient = Rock.Model.Communication.GetNextPending( communication.Id, recipientRockContext ); if ( recipient != null ) { if ( string.IsNullOrWhiteSpace( recipient.PersonAlias.Person.Email ) ) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "No Email Address"; } else { try { message.To.Clear(); message.Headers.Clear(); message.AlternateViews.Clear(); message.To.Add( new MailAddress( recipient.PersonAlias.Person.Email, recipient.PersonAlias.Person.FullName ) ); // Create merge field dictionary var mergeObjects = recipient.CommunicationMergeValues( mergeFields ); // Subject message.Subject = communication.Subject.ResolveMergeFields( mergeObjects, currentPerson, communication.EnabledLavaCommands ); // convert any special microsoft word characters to normal chars so they don't look funny (for example "Hey “double-quotes†from ‘single quote’") message.Subject = message.Subject.ReplaceWordChars(); // Add text view first as last view is usually treated as the preferred view by email readers (gmail) string plainTextBody = Rock.Communication.Medium.Email.ProcessTextBody( communication, globalAttributes, mergeObjects, currentPerson ); // convert any special microsoft word characters to normal chars so they don't look funny plainTextBody = plainTextBody.ReplaceWordChars(); if ( !string.IsNullOrWhiteSpace( plainTextBody ) ) { AlternateView plainTextView = AlternateView.CreateAlternateViewFromString( plainTextBody, new System.Net.Mime.ContentType( MediaTypeNames.Text.Plain ) ); message.AlternateViews.Add( plainTextView ); } // Add Html view string htmlBody = Rock.Communication.Medium.Email.ProcessHtmlBody( communication, globalAttributes, mergeObjects, currentPerson ); // convert any special microsoft word characters to normal chars so they don't look funny htmlBody = htmlBody.ReplaceWordChars(); if ( !string.IsNullOrWhiteSpace( htmlBody ) ) { AlternateView htmlView = AlternateView.CreateAlternateViewFromString( htmlBody, new System.Net.Mime.ContentType( MediaTypeNames.Text.Html ) ); message.AlternateViews.Add( htmlView ); } // Add any additional headers that specific SMTP provider needs var metaData = new Dictionary<string, string>(); metaData.Add( "communication_recipient_guid", recipient.Guid.ToString() ); AddAdditionalHeaders( message, metaData ); // Recreate the attachments message.Attachments.Clear(); if ( attachments.Any() ) { foreach( var attachment in attachments ) { message.Attachments.Add( new Attachment( attachment.ContentStream, attachment.FileName ) ); } } smtpClient.Send( message ); recipient.Status = CommunicationRecipientStatus.Delivered; string statusNote = StatusNote; if ( !string.IsNullOrWhiteSpace( statusNote ) ) { recipient.StatusNote = statusNote; } recipient.TransportEntityTypeName = this.GetType().FullName; var historyService = new HistoryService( recipientRockContext ); historyService.Add( new History { CreatedByPersonAliasId = communication.SenderPersonAliasId, EntityTypeId = personEntityTypeId, CategoryId = communicationCategoryId, EntityId = recipient.PersonAlias.PersonId, Summary = string.Format( "Sent communication from <span class='field-value'>{0}</span>.", message.From.DisplayName ), Caption = message.Subject, RelatedEntityTypeId = communicationEntityTypeId, RelatedEntityId = communication.Id } ); } catch ( Exception ex ) { recipient.Status = CommunicationRecipientStatus.Failed; recipient.StatusNote = "Exception: " + ex.Message; } } recipientRockContext.SaveChanges(); } else { recipientFound = false; } } } } } }