Communication Recipient POCO Service class
        /// <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();
        }
Exemple #5
0
        /// <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;
                        }
                    }
                }
            }
        }
Exemple #6
0
        /// <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&lt;CommunicationRecipientResponse&gt;.</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());
        }
Exemple #10
0
        /// <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();
        }
Exemple #11
0
        /// <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 );
        }
Exemple #12
0
        /// <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());
        }
Exemple #14
0
        /// <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 );
        }
Exemple #15
0
        /// <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();
                }
            }
        }
Exemple #18
0
        /// <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;
                    }
                }
            }
        }
Exemple #19
0
        /// <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();
        }
Exemple #22
0
        /// <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 );
        }
Exemple #29
0
        /// <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;
                            }
                        } );
                    }
                }
            }
        }
Exemple #30
0
        /// <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;
                        }
                    } );
                }
            }
        }
Exemple #31
0
        /// <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);
        }
Exemple #33
0
        /// <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();
        }
Exemple #34
0
        /// <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();
        }