/// <summary> /// Sends the message to a person via the specified medium type using the system communication. /// </summary> /// <param name="person">The person.</param> /// <param name="mediumType">Type of the medium.</param> /// <param name="systemCommunication">The system communication.</param> /// <param name="mergeObjects">The merge objects.</param> /// <returns></returns> internal static SendMessageResult SendMessage(Person person, int mediumType, SystemCommunication systemCommunication, Dictionary <string, object> mergeObjects) { var results = new SendMessageResult(); CreateMessageResult createMessageResults; switch (mediumType) { case ( int )CommunicationType.SMS: createMessageResults = CreateSmsMessage(person, mergeObjects, systemCommunication); break; default: createMessageResults = CreateEmailMessage(person, mergeObjects, systemCommunication); break; } if (createMessageResults.Message == null) { results.Warnings.AddRange(createMessageResults.Warnings); return(results); } if (createMessageResults.Message.Send(out var errors)) { results.MessagesSent = 1; } else { results.Errors.AddRange(errors); } return(results); }
/// <summary> /// Initializes a new instance of the <see cref="RockSMSMessage"/> class. /// </summary> /// <param name="systemCommunication">The system communication.</param> public RockSMSMessage(SystemCommunication systemCommunication) : this() { if (systemCommunication != null) { InitializeSmsMessage(systemCommunication); } }
/// <summary> /// Initializes the SMS message. /// </summary> /// <param name="systemCommunication">The system communication.</param> private void InitializeSmsMessage(SystemCommunication systemCommunication) { if (systemCommunication == null) { return; } this.FromNumber = DefinedValueCache.Get(systemCommunication.SMSFromDefinedValue); this.Message = systemCommunication.SMSMessage; }
/// <summary> /// Gets the <see cref="SystemCommunication"/> for a <see cref="GroupType"/>. /// </summary> /// <param name="groupType">The <see cref="GroupType"/>.</param> /// <param name="systemCommunicationService">The <see cref="SystemCommunicationService"/>.</param> /// <returns>A <see cref="SystemCommunication"/> if one is set on the <see cref="GroupType"/>, otherwise null.</returns> private SystemCommunication GetGroupTypeRsvpReminder(GroupType groupType, SystemCommunicationService systemCommunicationService) { SystemCommunication groupTypeReminder = null; if (groupType.RSVPReminderSystemCommunicationId.HasValue) { groupTypeReminder = systemCommunicationService.Get(groupType.RSVPReminderSystemCommunicationId.Value); } return(groupTypeReminder); }
/// <summary> /// Handles the Delete event of the gEmailTemplates control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="RowEventArgs" /> instance containing the event data.</param> protected void gEmailTemplates_Delete(object sender, RowEventArgs e) { var rockContext = new RockContext(); SystemCommunicationService emailTemplateService = new SystemCommunicationService(rockContext); SystemCommunication emailTemplate = emailTemplateService.Get(e.RowKeyId); if (emailTemplate != null) { emailTemplateService.Delete(emailTemplate); rockContext.SaveChanges(); } BindGrid(); }
private void InitializePushMessage(SystemCommunication systemCommunication) { if (systemCommunication == null) { return; } Message = systemCommunication.PushMessage; Title = systemCommunication.PushTitle; Sound = systemCommunication.PushSound; ImageBinaryFileId = systemCommunication.PushImageBinaryFileId; OpenAction = systemCommunication.PushOpenAction; OpenMessage = systemCommunication.PushOpenMessage; Data = Newtonsoft.Json.JsonConvert.DeserializeObject <PushData>(systemCommunication.PushData); }
/// <summary> /// Sends the message to a group using the system communication. Each person in the group receives the message in their preferred medium. /// A "Recipient" merge object will be added to the merge objects. /// </summary> /// <param name="groupId">The group identifier.</param> /// <param name="systemCommunication">The system communication.</param> /// <param name="mergeObjects">The merge objects.</param> /// <returns></returns> public static SendMessageResult SendMessage(int groupId, SystemCommunication systemCommunication, Dictionary <string, object> mergeObjects) { var results = new SendMessageResult(); using (var rockContext = new RockContext()) { // The group members are the message recipients var groupMemberViews = new GroupMemberService(rockContext) .Queryable() .AsNoTracking() .Where(m => m.GroupId == groupId && m.GroupMemberStatus == GroupMemberStatus.Active && m.Person != null ) .Select(m => new { Person = m.Person, GroupCommunicationPreference = m.CommunicationPreference }) .Distinct() .ToList(); // Send the communication to each recipient in the group foreach (var groupMemberView in groupMemberViews) { var recipient = groupMemberView.Person; mergeObjects["Recipient"] = recipient; var mediumType = Rock.Model.Communication.DetermineMediumEntityTypeId( ( int )CommunicationType.Email, ( int )CommunicationType.SMS, ( int )CommunicationType.PushNotification, groupMemberView.GroupCommunicationPreference, groupMemberView.Person.CommunicationPreference); var result = SendMessage(recipient, mediumType, systemCommunication, mergeObjects); // Add this result to the set of results to return results.Warnings.AddRange(result.Warnings); results.Errors.AddRange(result.Errors); results.MessagesSent += result.MessagesSent; } } return(results); }
/// <summary> /// Sends the digest emails for each regional group within the attendance summary. /// </summary> /// <param name="systemCommunication">The system communication.</param> /// <param name="attendanceSummary">The attendance summary.</param> private void SendDigestEmails(SystemCommunication systemCommunication, AttendanceSummary attendanceSummary) { foreach (var regionalGroupAttendance in attendanceSummary.RegionalGroupAttendances) { // Add the merge objects to support this email. var mergeObjects = Rock.Lava.LavaHelper.GetCommonMergeFields(null); mergeObjects.Add("StartDate", attendanceSummary.StartDate); mergeObjects.Add("EndDate", attendanceSummary.EndDate); mergeObjects.Add("GroupAttendances", regionalGroupAttendance.GroupAttendances); // Send a separate email to each leader within this regional group. foreach (Person leader in regionalGroupAttendance.Leaders) { SendDigestEmail(systemCommunication, mergeObjects, leader, regionalGroupAttendance.Group); } } }
/// <summary> /// Sends an RSVP reminder email to an individual attendee. /// </summary> /// <param name="person">The <see cref="Person"/>.</param> /// <param name="reminder">The <see cref="SystemCommunication"/> to be sent as a reminder.</param> /// <param name="lavaMergeFields">A dictionary containing Lava merge fields.</param> /// <param name="smsNumber">The correctly formatted SMS Number for SMS communications.</param> /// <returns>1 if the communication was successfully sent, otherwise 0.</returns> private int SendReminderSMS(Person person, SystemCommunication reminder, Dictionary <string, object> lavaMergeFields, string smsNumber) { var recipient = new RockSMSMessageRecipient(person, smsNumber, lavaMergeFields); var message = new RockSMSMessage(reminder); message.SetRecipients(new List <RockSMSMessageRecipient>() { recipient }); message.Send(out List <string> smsErrors); if (!smsErrors.Any()) { return(1); // No error, this should be counted as a sent reminder. } return(0); }
/// <summary> /// Processes a group for any RSVP communications that need to be issued. /// </summary> /// <param name="group">The <see cref="Group"/>.</param> /// <param name="offsetDays">The number of days prior to the RSVP occurrence that a reminder should be sent.</param> /// <param name="reminder">The <see cref="SystemCommunication"/> to be sent as a reminder.</param> /// <param name="rockContext">The <see cref="RockContext"/>.</param> /// <returns>The number of reminders sent.</returns> private int SendRsvpRemindersForGroup(Group group, int offsetDays, SystemCommunication reminder, RockContext rockContext) { int remindersSent = 0; // Get occurrences (within the correct date range) with positive RSVP responses for this group. var rsvpOccurrences = GetOccurrencesForGroup(group, offsetDays, rockContext); foreach (var rsvpOccurrence in rsvpOccurrences) { // Process each positive RSVP response and send their reminder. var rsvpResponders = rsvpOccurrence.Attendees.Where(a => a.RSVP == RSVP.Yes).ToList(); foreach (var rsvpResponder in rsvpResponders) { remindersSent += SendReminder(group, rsvpOccurrence, rsvpResponder.PersonAlias.Person, reminder); } } return(remindersSent); }
/// <summary> /// Initializes the email message. /// </summary> /// <param name="systemCommunication">The system email.</param> private void InitEmailMessage(SystemCommunication systemCommunication) { if (systemCommunication == null) { return; } this.FromEmail = systemCommunication.From; this.FromName = systemCommunication.FromName; var recipients = systemCommunication.To.SplitDelimitedValues().ToList().Select(a => RockEmailMessageRecipient.CreateAnonymous(a, null)).ToList(); this.SetRecipients(recipients); this.CCEmails = systemCommunication.Cc.SplitDelimitedValues().ToList(); this.BCCEmails = systemCommunication.Bcc.SplitDelimitedValues().ToList(); this.Subject = systemCommunication.Subject; this.Message = systemCommunication.Body; this.CssInliningEnabled = systemCommunication.CssInliningEnabled; }
/// <summary> /// Processes a list of groups, sending any RSVP reminders for those groups and returning a <see cref="StringBuilder"/> with the job result messages. /// </summary> /// <param name="groups">The list of <see cref="Group"/> objects to process.</param> /// <param name="groupTypeReminder">The <see cref="SystemCommunication"/> from the <see cref="GroupType"/>.</param> /// <param name="groupTypeOffset">The RSVPReminderOffsetDays property of the <see cref="GroupType"/>.</param> /// <param name="rockContext">The <see cref="RockContext"/>.</param> /// <returns>A <see cref="StringBuilder"/> with the job result messages.</returns> private StringBuilder ProcessGroups(List <Group> groups, SystemCommunication groupTypeReminder, int?groupTypeOffset, RockContext rockContext) { var sbResults = new StringBuilder(); foreach (var group in groups) { // Select the appropriate RSVP settings (from GroupType or Group) and process RSVP reminders for the group. SystemCommunication groupReminder = groupTypeReminder ?? group.RSVPReminderSystemCommunication; int rsvpOffset = (groupTypeOffset ?? group.RSVPReminderOffsetDays) ?? 0; int remindersSent = SendRsvpRemindersForGroup(group, rsvpOffset, groupReminder, rockContext); // If a reminder was sent, add a message about it so we can display it in the job status message. if (remindersSent > 0) { sbResults.AppendLine(string.Format("Sent {0} reminder(s) for group {1}.", remindersSent, group.Name)); } } return(sbResults); }
/// <summary> /// Sends and individual digest email to the specified person. /// </summary> /// <param name="systemCommunication">The system communication.</param> /// <param name="mergeObjects">The Lava merge objects.</param> /// <param name="person">The person who should receive the email.</param> /// <param name="regionalGroup">The regional group that this digest email represents.</param> private void SendDigestEmail(SystemCommunication systemCommunication, Dictionary <string, object> mergeObjects, Person person, Group regionalGroup) { mergeObjects.AddOrReplace("Person", person); var recipient = new RockEmailMessageRecipient(person, mergeObjects); var message = new RockEmailMessage(systemCommunication); message.Subject = $"'{regionalGroup.Name}' Group Attendance Digest"; message.SetRecipients(new List <RockEmailMessageRecipient> { recipient }); message.Send(out List <string> errorMessages); if (!errorMessages.Any()) { _digestsSentCount++; return; } _errors.Add($"Unable to send '{regionalGroup.Name}' digest to '{person.Email}'."); }
/// <summary> /// Sends an RSVP reminder SMS to an individual attendee. /// </summary> /// <param name="person">The <see cref="Person"/>.</param> /// <param name="reminder">The <see cref="SystemCommunication"/> to be sent as a reminder.</param> /// <param name="lavaMergeFields">A dictionary containing Lava merge fields.</param> /// <returns>1 if the communication was successfully sent, otherwise 0.</returns> private int SendReminderEmail(Person person, SystemCommunication reminder, Dictionary <string, object> lavaMergeFields) { if (!person.IsEmailActive) { return(0); } var recipient = new RockEmailMessageRecipient(person, lavaMergeFields); var message = new RockEmailMessage(reminder); message.SetRecipients(new List <RockEmailMessageRecipient>() { recipient }); message.Send(out List <string> emailErrors); if (!emailErrors.Any()) { return(1); // No error, this should be counted as a sent reminder. } return(0); }
/// <summary> /// Sends the attendance reminders. /// </summary> /// <param name="leaders">The leaders.</param> /// <param name="occurrences">The occurrences.</param> /// <param name="systemCommunication">The system communication.</param> /// <param name="jobPreferredCommunicationType">Type of the job preferred communication.</param> /// <param name="isSmsEnabled">if set to <c>true</c> [is SMS enabled].</param> /// <returns></returns> private SendMessageResult SendAttendanceReminders(List <GroupMember> leaders, Dictionary <int, List <DateTime> > occurrences, SystemCommunication systemCommunication, CommunicationType jobPreferredCommunicationType, bool isSmsEnabled) { var result = new SendMessageResult(); // Loop through the leaders foreach (var leader in leaders) { var mediumType = Rock.Model.Communication.DetermineMediumEntityTypeId( ( int )CommunicationType.Email, ( int )CommunicationType.SMS, ( int )CommunicationType.PushNotification, jobPreferredCommunicationType, leader.CommunicationPreference, leader.Person.CommunicationPreference); var leaderOccurrences = occurrences.Where(o => o.Key == leader.GroupId); foreach (var leaderOccurrence in leaderOccurrences) { var mergeObjects = Rock.Lava.LavaHelper.GetCommonMergeFields(null, leader.Person); mergeObjects.Add("Person", leader.Person); mergeObjects.Add("Group", leader.Group); mergeObjects.Add("Occurrence", leaderOccurrence.Value.Max()); var sendResult = CommunicationHelper.SendMessage(leader.Person, mediumType, systemCommunication, mergeObjects); result.MessagesSent += sendResult.MessagesSent; result.Errors.AddRange(sendResult.Errors); result.Warnings.AddRange(sendResult.Warnings); } } return(result); }
/// <summary> /// Job that will sync groups. /// /// Called by the <see cref="IScheduler" /> when a /// <see cref="ITrigger" /> fires that is associated with /// the <see cref="IJob" />. /// </summary> public virtual void Execute(IJobExecutionContext context) { JobDataMap dataMap = context.JobDetail.JobDataMap; try { int notificationsSent = 0; int errorsEncountered = 0; int pendingMembersCount = 0; // get groups set to sync RockContext rockContext = new RockContext(); Guid?groupTypeGuid = dataMap.GetString("GroupType").AsGuidOrNull(); Guid?systemEmailGuid = dataMap.GetString("NotificationEmail").AsGuidOrNull(); Guid?groupRoleFilterGuid = dataMap.GetString("GroupRoleFilter").AsGuidOrNull(); int? pendingAge = dataMap.GetString("PendingAge").AsIntegerOrNull(); bool includePreviouslyNotificed = dataMap.GetString("IncludePreviouslyNotified").AsBoolean(); // get system email var emailService = new SystemCommunicationService(rockContext); SystemCommunication systemEmail = null; if (!systemEmailGuid.HasValue || systemEmailGuid == Guid.Empty) { context.Result = "Job failed. Unable to find System Email"; throw new Exception("No system email found."); } systemEmail = emailService.Get(systemEmailGuid.Value); // get group members if (!groupTypeGuid.HasValue || groupTypeGuid == Guid.Empty) { context.Result = "Job failed. Unable to find group type"; throw new Exception("No group type found"); } var qry = new GroupMemberService(rockContext).Queryable("Person, Group, Group.Members.GroupRole") .Where(m => m.Group.GroupType.Guid == groupTypeGuid.Value && m.GroupMemberStatus == GroupMemberStatus.Pending); if (!includePreviouslyNotificed) { qry = qry.Where(m => m.IsNotified == false); } if (groupRoleFilterGuid.HasValue) { qry = qry.Where(m => m.GroupRole.Guid == groupRoleFilterGuid.Value); } if (pendingAge.HasValue && pendingAge.Value > 0) { var ageDate = RockDateTime.Now.AddDays(pendingAge.Value * -1); qry = qry.Where(m => m.ModifiedDateTime > ageDate); } var pendingGroupMembers = qry.ToList(); var groups = pendingGroupMembers.GroupBy(m => m.Group); var errorList = new List <string>(); foreach (var groupKey in groups) { var group = groupKey.Key; // get list of pending people var qryPendingIndividuals = group.Members.Where(m => m.GroupMemberStatus == GroupMemberStatus.Pending); if (!includePreviouslyNotificed) { qryPendingIndividuals = qryPendingIndividuals.Where(m => m.IsNotified == false); } if (groupRoleFilterGuid.HasValue) { qryPendingIndividuals = qryPendingIndividuals.Where(m => m.GroupRole.Guid == groupRoleFilterGuid.Value); } var pendingIndividuals = qryPendingIndividuals.Select(m => m.Person).ToList(); if (!pendingIndividuals.Any()) { continue; } // get list of leaders var groupLeaders = group.Members.Where(m => m.GroupRole.IsLeader == true && m.Person != null && m.Person.Email != null && m.Person.Email != string.Empty); if (!groupLeaders.Any()) { errorList.Add("Unable to send emails to members in group " + group.Name + " because there is no group leader"); continue; } var recipients = new List <RockEmailMessageRecipient>(); foreach (var leader in groupLeaders) { // create merge object var mergeFields = new Dictionary <string, object>(); mergeFields.Add("PendingIndividuals", pendingIndividuals); mergeFields.Add("Group", group); mergeFields.Add("ParentGroup", group.ParentGroup); mergeFields.Add("Person", leader.Person); recipients.Add(new RockEmailMessageRecipient(leader.Person, mergeFields)); } var errorMessages = new List <string>(); var emailMessage = new RockEmailMessage(systemEmail.Guid); emailMessage.SetRecipients(recipients); var sendSuccess = emailMessage.Send(out errorMessages); errorsEncountered += errorMessages.Count; errorList.AddRange(errorMessages); // be conservative: only mark as notified if we are sure the email didn't fail if (sendSuccess == false) { continue; } notificationsSent += recipients.Count(); // mark pending members as notified as we go in case the job fails var notifiedPersonIds = pendingIndividuals.Select(p => p.Id); foreach (var pendingGroupMember in pendingGroupMembers.Where(m => m.IsNotified == false && m.GroupId == group.Id && notifiedPersonIds.Contains(m.PersonId))) { pendingGroupMember.IsNotified = true; pendingMembersCount++; } rockContext.SaveChanges(); } context.Result = string.Format("Sent {0} emails to leaders for {1} pending individuals. {2} errors encountered.", notificationsSent, pendingMembersCount, errorsEncountered); if (errorList.Any()) { StringBuilder sb = new StringBuilder(); sb.AppendLine(); sb.Append("Errors in GroupLeaderPendingNotificationJob: "); errorList.ForEach(e => { sb.AppendLine(); sb.Append(e); }); string errors = sb.ToString(); context.Result += errors; throw new Exception(errors); } } catch (Exception ex) { HttpContext context2 = HttpContext.Current; ExceptionLogService.LogException(ex, context2); throw; } }
/// <summary> /// Starts the refund process. /// </summary> private void StartRefunds() { long totalMilliseconds = 0; var importTask = new Task(() => { // wait a little so the browser can render and start listening to events System.Threading.Thread.Sleep(1000); _hubContext.Clients.All.showButtons(this.SignalRNotificationKey, false); Stopwatch stopwatch = Stopwatch.StartNew(); List <int> registrationTemplateIds = rtpRegistrationTemplate.ItemIds.AsIntegerList(); registrationTemplateIds.RemoveAll(i => i.Equals(0)); RockContext rockContext = new RockContext(); SystemCommunicationService systemCommunicationService = new SystemCommunicationService(rockContext); if (pnlRegistration.Visible && registrationTemplateIds.Count > 0) { List <int> registrationInstanceIds = new List <int>(); if (ddlRegistrationInstance.SelectedValueAsId().HasValue&& ddlRegistrationInstance.SelectedValueAsId() > 0) { registrationInstanceIds.Add(ddlRegistrationInstance.SelectedValueAsId().Value); } else { RegistrationTemplateService registrationTemplateService = new RegistrationTemplateService(rockContext); var templates = registrationTemplateService.GetByIds(rtpRegistrationTemplate.ItemIds.AsIntegerList()); int registrationCount = templates.SelectMany(t => t.Instances).SelectMany(i => i.Registrations).Count(); registrationInstanceIds.AddRange(templates.SelectMany(t => t.Instances).OrderBy(i => i.Name).Select(i => i.Id)); } RegistrationInstanceService registrationInstanceService = new RegistrationInstanceService(rockContext); // Load the registration instance and then iterate through all registrations. var registrations = registrationInstanceService.Queryable().Where(ri => registrationInstanceIds.Contains(ri.Id)).SelectMany(ri => ri.Registrations); int j = 1; foreach (Registration registration in registrations) { bool issuedRefund = false; OnProgress("Processing registration refund " + j + " of " + registrations.Count()); foreach (var payment in registration.GetPayments(rockContext)) { issuedRefund = issueRefund(payment.Transaction, "Registration", registration.FirstName + " " + registration.LastName); } j++; // Send an email if applicable if (issuedRefund && !string.IsNullOrWhiteSpace(registration.ConfirmationEmail) && ddlSystemCommunication.SelectedValueAsInt().HasValue&& ddlSystemCommunication.SelectedValueAsInt() > 0) { var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(this.RockPage); mergeFields.Add("Registration", registration); SystemCommunication systemCommunication = systemCommunicationService.Get(ddlSystemCommunication.SelectedValueAsInt().Value); var emailMessage = new RockEmailMessage(systemCommunication); emailMessage.AdditionalMergeFields = mergeFields; emailMessage.AddRecipient(RockEmailMessageRecipient.CreateAnonymous(registration.ConfirmationEmail, mergeFields)); emailMessage.CreateCommunicationRecord = true; emailMessage.Send(); } } } if (pnlTransactionCodes.Visible && tbTransactionCodes.Text.Length > 0) { var codes = tbTransactionCodes.Text.SplitDelimitedValues(); FinancialTransactionService financialTransactionService = new FinancialTransactionService(rockContext); var transactions = financialTransactionService.Queryable().Where(ft => codes.Contains(ft.TransactionCode)); int j = 0; foreach (var transaction in transactions) { OnProgress("Processing transaction refund " + j + " of " + transactions.Count()); var issuedRefund = issueRefund(transaction, "Transaction", transaction.AuthorizedPersonAlias != null ? transaction.AuthorizedPersonAlias.Person.FullName : "Unknown"); // Send an email if applicable if (issuedRefund && transaction.AuthorizedPersonAlias != null && !string.IsNullOrWhiteSpace(transaction.AuthorizedPersonAlias.Person.Email) && ddlSystemCommunication.SelectedValueAsInt().HasValue&& ddlSystemCommunication.SelectedValueAsInt() > 0) { var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(this.RockPage); mergeFields.Add("Transaction", transaction); SystemCommunication systemCommunication = systemCommunicationService.Get(ddlSystemCommunication.SelectedValueAsInt().Value); var emailMessage = new RockEmailMessage(systemCommunication); emailMessage.AdditionalMergeFields = mergeFields; emailMessage.FromEmail = ebEmail.Text; emailMessage.AddRecipient(new RockEmailMessageRecipient(transaction.AuthorizedPersonAlias.Person, mergeFields)); emailMessage.CreateCommunicationRecord = true; emailMessage.Send(); } } } stopwatch.Stop(); totalMilliseconds = stopwatch.ElapsedMilliseconds; _hubContext.Clients.All.showButtons(this.SignalRNotificationKey, true); }); importTask.ContinueWith((t) => { if (t.IsFaulted) { foreach (var exception in t.Exception.InnerExceptions) { LogException(exception); } OnProgress("ERROR: " + t.Exception.Message); } else { OnProgress(string.Format("{0} Complete: [{1}ms]", "All refunds have been issued.", totalMilliseconds)); } }); importTask.Start(); }
/// <summary> /// Initializes a new instance of the <see cref="RockEmailMessage"/> class. /// </summary> /// <param name="systemCommunication">The system email.</param> public RockEmailMessage(SystemCommunication systemCommunication) : this() { InitEmailMessage(systemCommunication); }
/// <summary> /// Executes the specified context. /// </summary> /// <param name="context">The context.</param> public void Execute(IJobExecutionContext context) { var rockContext = new RockContext(); var personService = new PersonService(rockContext); JobDataMap dataMap = context.JobDetail.JobDataMap; Guid? systemEmailGuid = dataMap.GetString("BirthdayEmail").AsGuidOrNull(); var emailService = new SystemCommunicationService(rockContext); SystemCommunication systemEmail = null; if (systemEmailGuid.HasValue) { systemEmail = emailService.Get(systemEmailGuid.Value); } if (systemEmail == null) { // no email specified, so nothing to do return; } var activeStatusGuid = Rock.SystemGuid.DefinedValue.PERSON_RECORD_STATUS_ACTIVE.AsGuid(); // only include alive people that have record status of Active var personQry = personService.Queryable(false, false).Where(a => a.RecordStatusValue.Guid == activeStatusGuid && a.IsDeceased == false); var ageRange = (dataMap.GetString("AgeRange") ?? string.Empty).Split(','); if (ageRange.Length == 2) { int?minimumAge = ageRange[0].AsIntegerOrNull(); int?maximumAge = ageRange[1].AsIntegerOrNull(); personQry = personQry.WhereAgeRange(minimumAge, maximumAge, true); } // only include people whose birthday is today (which can be determined from the computed DaysUntilBirthday column) personQry = personQry.Where(a => a.DaysUntilBirthday.HasValue && a.DaysUntilBirthday == 0); var connectionStatusGuids = (dataMap.GetString("ConnectionStatuses") ?? string.Empty).Split(',').AsGuidList(); if (connectionStatusGuids.Any()) { personQry = personQry.Where(a => connectionStatusGuids.Contains(a.ConnectionStatusValue.Guid)); } // only include people that have an email address and want an email personQry = personQry.Where(a => (a.Email != null) && (a.Email != string.Empty) && (a.EmailPreference != EmailPreference.DoNotEmail) && (a.IsEmailActive)); var recipients = new List <RockEmailMessageRecipient>(); var personList = personQry.AsNoTracking().ToList(); foreach (var person in personList) { var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(null); mergeFields.Add("Person", person); recipients.Add(new RockEmailMessageRecipient(person, mergeFields)); } var emailMessage = new RockEmailMessage(systemEmail.Guid); emailMessage.SetRecipients(recipients); var errors = new List <string>(); emailMessage.Send(out errors); context.Result = string.Format("{0} birthday emails sent", recipients.Count()); if (errors.Any()) { StringBuilder sb = new StringBuilder(); sb.AppendLine(); sb.Append(string.Format("{0} Errors: ", errors.Count())); errors.ForEach(e => { sb.AppendLine(); sb.Append(e); }); string errorMessage = sb.ToString(); context.Result += errorMessage; var exception = new Exception(errorMessage); HttpContext context2 = HttpContext.Current; ExceptionLogService.LogException(exception, context2); throw exception; } }
/// <summary> /// Private method called by Execute() to process the job. /// </summary> private void ProcessJob(IJobExecutionContext context) { JobDataMap dataMap = context.JobDetail.JobDataMap; int notificationsSent = 0; int errorsEncountered = 0; int absentMembersCount = 0; int sendFailed = 0; // get groups set to sync RockContext rockContext = new RockContext(); Guid?groupTypeGuid = dataMap.GetString(AttributeKey.GroupType).AsGuidOrNull(); Guid?systemEmailGuid = dataMap.GetString(AttributeKey.NotificationEmail).AsGuidOrNull(); Guid?groupRoleFilterGuid = dataMap.GetString(AttributeKey.GroupRoleFilter).AsGuidOrNull(); int minimumAbsences = dataMap.GetString(AttributeKey.MinimumAbsences).AsInteger(); // get system email var emailService = new SystemCommunicationService(rockContext); SystemCommunication systemEmail = null; if (!systemEmailGuid.HasValue || systemEmailGuid == Guid.Empty) { context.Result = "Job failed. Unable to find System Email"; throw new Exception("No system email found."); } if (minimumAbsences == default(int)) { context.Result = "Job failed. The is no minimum absense count entered."; throw new Exception("No minimum absense count found."); } systemEmail = emailService.Get(systemEmailGuid.Value); // get group members if (!groupTypeGuid.HasValue || groupTypeGuid == Guid.Empty) { context.Result = "Job failed. Unable to find group type"; throw new Exception("No group type found."); } var groupMemberQry = new GroupMemberService(rockContext).Queryable("Group, Group.Members.GroupRole") .Where(m => m.Group.GroupType.Guid == groupTypeGuid.Value); if (groupRoleFilterGuid.HasValue) { groupMemberQry = groupMemberQry.Where(m => m.GroupRole.Guid == groupRoleFilterGuid.Value); } var groupMembers = groupMemberQry.GroupBy(m => m.Group); var errorList = new List <string>(); foreach (var groupGroupMember in groupMembers) { var group = groupGroupMember.Key; var filteredPersons = groupGroupMember.Select(a => a.PersonId); // get list of leaders var groupLeaders = group.Members.Where(m => m.GroupRole.IsLeader == true && m.Person != null && m.Person.Email != null && m.Person.Email != string.Empty); if (!groupLeaders.Any()) { errorList.Add("Unable to send emails to members in group " + group.Name + " because there is no group leader"); continue; } // Get all the occurrences for this group var occurrences = new AttendanceOccurrenceService(rockContext) .Queryable("Attendees.PersonAlias.Person") .Where(a => a.DidNotOccur.HasValue && !a.DidNotOccur.Value && a.GroupId == group.Id) .OrderByDescending(a => a.OccurrenceDate) .Take(minimumAbsences) .ToList(); if (occurrences.Count == minimumAbsences) { var absentPersons = occurrences .SelectMany(a => a.Attendees) .Where(a => a.DidAttend.HasValue && !a.DidAttend.Value && filteredPersons.Contains(a.PersonAlias.PersonId)) .GroupBy(a => a.PersonAlias.Person) .Where(a => a.Count() == minimumAbsences) .Select(a => a.Key) .ToList(); if (absentPersons.Count > 0) { var recipients = new List <RockEmailMessageRecipient>(); foreach (var leader in groupLeaders) { // create merge object var mergeFields = new Dictionary <string, object>(); mergeFields.Add("AbsentMembers", absentPersons); mergeFields.Add("Group", group); mergeFields.Add("Person", leader.Person); recipients.Add(new RockEmailMessageRecipient(leader.Person, mergeFields)); } var errorMessages = new List <string>(); var emailMessage = new RockEmailMessage(systemEmail.Guid); emailMessage.SetRecipients(recipients); var sendSuccess = emailMessage.Send(out errorMessages); if (!sendSuccess) { sendFailed++; } errorsEncountered += errorMessages.Count; errorList.AddRange(errorMessages); // be conservative: only mark as notified if we are sure the email didn't fail if (errorMessages.Any()) { continue; } absentMembersCount += absentPersons.Count; notificationsSent += recipients.Count(); } } } context.Result = string.Format("Sent {0} emails to leaders for {1} absent members. {2} errors encountered. {3} times Send reported a fail.", notificationsSent, absentMembersCount, errorsEncountered, sendFailed); if (errorList.Any()) { StringBuilder sb = new StringBuilder(); sb.AppendLine(); sb.Append("Errors in GroupLeaderAbsenceNotifications: "); errorList.ForEach(e => { sb.AppendLine(); sb.Append(e); }); string errors = sb.ToString(); context.Result += errors; throw new Exception(errors); } }
/// <summary> /// Private method called by Execute() to process the job. This method should be wrapped in a try/catch block to ensure than any exceptions are sent to the /// <see cref="ExceptionLogService"/>. /// </summary> /// <param name="context">The job's execution context.</param> private void ProcessJob(IJobExecutionContext context) { JobDataMap dataMap = context.JobDetail.JobDataMap; RockContext rockContext = new RockContext(); // Make sure GroupType job attribute was assigned. Guid? groupTypeGuid = dataMap.GetString(AttributeKey.GroupType).AsGuidOrNull(); List <GroupType> rsvpGroupTypes = new List <GroupType>(); if (groupTypeGuid != null) { // Verify GroupType exists. var groupType = new GroupTypeService(rockContext).Get(groupTypeGuid.Value); if (groupType == null) { context.Result = "Job Exited: The selected Group Type does not exist."; return; } // Verify GroupType has RSVP enabled. if (!groupType.EnableRSVP) { context.Result = "Job Exited: The selected Group Type does not have RSVP enabled."; return; } rsvpGroupTypes.Add(groupType); } else { rsvpGroupTypes = new GroupTypeService(rockContext).Queryable().AsNoTracking().Where(gt => gt.EnableRSVP).ToList(); } var sbResults = new StringBuilder(); foreach (var groupType in rsvpGroupTypes) { // Retrieve RSVP settings from GroupType. var systemCommunicationService = new SystemCommunicationService(rockContext); SystemCommunication groupTypeReminder = GetGroupTypeRsvpReminder(groupType, systemCommunicationService); int?groupTypeOffset = GetGroupTypeOffset(groupType); // Get RSVP enabled groups which have an RSVP Reminder set, and verify that there is at least one group to process. var groups = GetRsvpReminderEligibleGroups(groupType, rockContext, groupType.RSVPReminderSystemCommunicationId.HasValue); if (!groups.Any()) { sbResults.AppendLine($"The Group Type {groupType.Name} does not contain any groups with RSVP reminder communications."); continue; } // Process groups and get the response. This is where the actual work starts. sbResults.Append(ProcessGroups(groups, groupTypeReminder, groupTypeOffset, rockContext)); } // Job complete! Record results of the job to the context. var jobResult = sbResults.ToString(); if (string.IsNullOrEmpty(jobResult)) { jobResult = "Job completed successfully, but no reminders were sent."; } context.Result = jobResult; }
/// <summary> /// Sends the meeting reminders. /// </summary> /// <param name="context">The overall job context.</param> /// <param name="rockContext">The rockContext.</param> /// <param name="occurrenceData">The occurrenceData to process.</param> /// <param name="systemCommunication">The system communication.</param> /// <param name="jobPreferredCommunicationType">Type of the job preferred communication.</param> /// <returns></returns> private SendMessageResult SendMeetingReminders(IJobExecutionContext context, RockContext rockContext, Dictionary <RoomOccurrence, Group> occurrenceData, SystemCommunication systemCommunication, CommunicationType jobPreferredCommunicationType, bool isSmsEnabled, bool isPushEnabled) { var result = new SendMessageResult(); var errorsEmail = new List <string>(); var errorsSms = new List <string>(); var errorsPush = new List <string>(); // Loop through the room occurrence data foreach (var occurrence in occurrenceData) { var emailMessage = new RockEmailMessage(systemCommunication); RockSMSMessage smsMessage = isSmsEnabled ? new RockSMSMessage(systemCommunication) : null; RockPushMessage pushMessage = isPushEnabled ? new RockPushMessage(systemCommunication) : null; var group = occurrence.Value; foreach (var groupMember in group.ActiveMembers().ToList()) { groupMember.Person.LoadAttributes(); var smsNumber = groupMember.Person.PhoneNumbers.GetFirstSmsNumber(); var personAlias = new PersonAliasService(rockContext).Get(groupMember.Person.PrimaryAliasId.Value); List <string> pushDevices = new PersonalDeviceService(rockContext).Queryable() .Where(a => a.PersonAliasId.HasValue && a.PersonAliasId == personAlias.Id && a.NotificationsEnabled) .Select(a => a.DeviceRegistrationId) .ToList(); if (!groupMember.Person.CanReceiveEmail(false) && smsNumber.IsNullOrWhiteSpace() && pushDevices.Count == 0) { continue; } var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(null, groupMember.Person); mergeFields.Add("Group", group); mergeFields.Add("Occurrence", occurrence.Key); mergeFields.Add("Person", groupMember.Person); var notificationType = ( CommunicationType )Communication.DetermineMediumEntityTypeId( ( int )CommunicationType.Email, ( int )CommunicationType.SMS, ( int )CommunicationType.PushNotification, jobPreferredCommunicationType, groupMember.CommunicationPreference, groupMember.Person.CommunicationPreference); switch (notificationType) { case CommunicationType.Email: if (!groupMember.Person.CanReceiveEmail(false)) { errorCount += 1; errorMessages.Add(string.Format("{0} does not have a valid email address.", groupMember.Person.FullName)); } else { emailMessage.AddRecipient(new RockEmailMessageRecipient(groupMember.Person, mergeFields)); } break; case CommunicationType.SMS: if (string.IsNullOrWhiteSpace(smsNumber) || smsMessage == null) { errorCount += 1; errorMessages.Add(string.Format("No SMS number could be found for {0}.", groupMember.Person.FullName)); goto case CommunicationType.Email; } else { smsMessage.AddRecipient(new RockSMSMessageRecipient(groupMember.Person, smsNumber, mergeFields)); } break; case CommunicationType.PushNotification: if (pushDevices.Count == 0 || pushMessage == null) { errorCount += 1; errorMessages.Add(string.Format("No devices that support notifications could be found for {0}.", groupMember.Person.FullName)); goto case CommunicationType.Email; } else { string deviceIds = String.Join(",", pushDevices); pushMessage.AddRecipient(new RockPushMessageRecipient(groupMember.Person, deviceIds, mergeFields)); } break; default: break; } } if (emailMessage.GetRecipients().Count > 0) { emailMessage.Send(out errorsEmail); if (errorsEmail.Any()) { result.Errors.AddRange(errorsEmail); } else { notificationEmails++; } } if (smsMessage != null && smsMessage.GetRecipients().Count > 0) { smsMessage.Send(out errorsSms); if (errorsSms.Any()) { result.Errors.AddRange(errorsSms); } else { notificationSms++; } } if (pushMessage != null && pushMessage.GetRecipients().Count > 0) { pushMessage.Send(out errorsPush); if (errorsPush.Any()) { result.Errors.AddRange(errorsPush); } else { notificationPush++; } } } if (errorMessages.Any()) { StringBuilder sb = new StringBuilder(); sb.AppendLine(); sb.Append(string.Format("{0} Errors: ", errorCount)); errorMessages.ForEach(e => { sb.AppendLine(); sb.Append(e); }); string errors = sb.ToString(); context.Result += errors; var exception = new Exception(errors); HttpContext context2 = HttpContext.Current; ExceptionLogService.LogException(exception, context2); throw exception; } return(result); }
private static CreateMessageResult CreateEmailMessage(Person person, Dictionary <string, object> mergeObjects, SystemCommunication systemCommunication) { var createMessageResult = new CreateMessageResult(); if (string.IsNullOrWhiteSpace(person.Email)) { var warning = $"{person.FullName} does not have an email address entered."; createMessageResult.Warnings.Add(warning); RockLogger.Log.Warning(RockLogDomains.Jobs, warning); return(createMessageResult); } if (!person.IsEmailActive) { var warning = $"{person.FullName.ToPossessive()} email address is inactive."; createMessageResult.Warnings.Add(warning); RockLogger.Log.Warning(RockLogDomains.Jobs, warning); return(createMessageResult); } if (person.EmailPreference == EmailPreference.DoNotEmail) { var warning = $"{person.FullName} is marked as do not email."; createMessageResult.Warnings.Add(warning); RockLogger.Log.Warning(RockLogDomains.Jobs, warning); return(createMessageResult); } var recipients = new List <RockMessageRecipient> { new RockEmailMessageRecipient(person, mergeObjects) }; var message = new RockEmailMessage(systemCommunication); message.SetRecipients(recipients); createMessageResult.Message = message; return(createMessageResult); }
/// <summary> /// Handles the Click event of the btnSave control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param> protected void btnSave_Click(object sender, EventArgs e) { var rockContext = new RockContext(); SystemCommunicationService emailTemplateService = new SystemCommunicationService(rockContext); SystemCommunication emailTemplate; int emailTemplateId = int.Parse(hfEmailTemplateId.Value); if (emailTemplateId == 0) { emailTemplate = new SystemCommunication(); emailTemplateService.Add(emailTemplate); } else { emailTemplate = emailTemplateService.Get(emailTemplateId); } emailTemplate.IsActive = cbIsActive.Checked; emailTemplate.CategoryId = cpCategory.SelectedValueAsInt(); emailTemplate.Title = tbTitle.Text; emailTemplate.FromName = tbFromName.Text; emailTemplate.From = tbFrom.Text; emailTemplate.To = tbTo.Text; emailTemplate.Cc = tbCc.Text; emailTemplate.Bcc = tbBcc.Text; emailTemplate.Subject = tbSubject.Text; emailTemplate.Body = ceEmailTemplate.Text; emailTemplate.LavaFields = kvlMergeFields.Value.AsDictionaryOrNull(); emailTemplate.CssInliningEnabled = cbCssInliningEnabled.Checked; emailTemplate.SMSFromDefinedValueId = dvpSMSFrom.SelectedValue.AsIntegerOrNull(); emailTemplate.SMSMessage = tbSMSTextMessage.Text; var pushCommunication = new CommunicationDetails(); var pushNotificationControl = phPushNotification.Controls[0] as PushNotification; if (pushNotificationControl != null) { pushNotificationControl.UpdateCommunication(pushCommunication); } emailTemplate.PushData = pushCommunication.PushData; emailTemplate.PushImageBinaryFileId = pushCommunication.PushImageBinaryFileId; emailTemplate.PushMessage = pushCommunication.PushMessage; emailTemplate.PushOpenAction = pushCommunication.PushOpenAction; emailTemplate.PushOpenMessage = pushCommunication.PushOpenMessage; emailTemplate.PushTitle = pushCommunication.PushTitle; if (!emailTemplate.IsValid) { // If CodeEditor is hidden, we need to manually add the Required error message or it will not be shown. if (string.IsNullOrWhiteSpace(ceEmailTemplate.Text) && !ceEmailTemplate.Visible) { var customValidator = new CustomValidator(); customValidator.ValidationGroup = ceEmailTemplate.ValidationGroup; customValidator.ControlToValidate = ceEmailTemplate.ID; customValidator.ErrorMessage = "Email Message Body is required."; customValidator.IsValid = false; Page.Validators.Add(customValidator); } // Controls will render the error messages return; } rockContext.SaveChanges(); NavigateToParentPage(); }
/// <summary> /// Shows the edit. /// </summary> /// <param name="emailTemplateId">The email template id.</param> protected void ShowEdit(int emailTemplateId) { var globalAttributes = GlobalAttributesCache.Get(); string globalFromName = globalAttributes.GetValue("OrganizationName"); tbFromName.Help = string.Format("If a From Name value is not entered the 'Organization Name' Global Attribute value of '{0}' will be used when this template is sent. <small><span class='tip tip-lava'></span></small>", globalFromName); string globalFrom = globalAttributes.GetValue("OrganizationEmail"); tbFrom.Help = string.Format("If a From Address value is not entered the 'Organization Email' Global Attribute value of '{0}' will be used when this template is sent. <small><span class='tip tip-lava'></span></small>", globalFrom); tbTo.Help = "You can specify multiple email addresses by separating them with a comma."; SystemCommunicationService emailTemplateService = new SystemCommunicationService(new RockContext()); SystemCommunication emailTemplate = emailTemplateService.Get(emailTemplateId); bool showMessagePreview = false; var pushCommunication = new CommunicationDetails(); if (emailTemplate != null) { pdAuditDetails.Visible = true; pdAuditDetails.SetEntity(emailTemplate, ResolveRockUrl("~")); lActionTitle.Text = ActionTitle.Edit(SystemCommunication.FriendlyTypeName).FormatAsHtmlTitle(); hfEmailTemplateId.Value = emailTemplate.Id.ToString(); cbIsActive.Checked = emailTemplate.IsActive.GetValueOrDefault(); cpCategory.SetValue(emailTemplate.CategoryId); tbTitle.Text = emailTemplate.Title; tbFromName.Text = emailTemplate.FromName; tbFrom.Text = emailTemplate.From; tbTo.Text = emailTemplate.To; tbCc.Text = emailTemplate.Cc; tbBcc.Text = emailTemplate.Bcc; tbSubject.Text = emailTemplate.Subject; ceEmailTemplate.Text = emailTemplate.Body; pushCommunication = new CommunicationDetails { PushData = emailTemplate.PushData, PushImageBinaryFileId = emailTemplate.PushImageBinaryFileId, PushMessage = emailTemplate.PushMessage, PushTitle = emailTemplate.PushTitle, PushOpenMessage = emailTemplate.PushOpenMessage, PushOpenAction = emailTemplate.PushOpenAction }; nbTemplateHelp.InnerHtml = CommunicationTemplateHelper.GetTemplateHelp(false); kvlMergeFields.Value = emailTemplate.LavaFields.Select(a => string.Format("{0}^{1}", a.Key, a.Value)).ToList().AsDelimited("|"); hfShowAdditionalFields.Value = (!string.IsNullOrEmpty(emailTemplate.Cc) || !string.IsNullOrEmpty(emailTemplate.Bcc)).ToTrueFalse().ToLower(); cbCssInliningEnabled.Checked = emailTemplate.CssInliningEnabled; showMessagePreview = true; } else { pdAuditDetails.Visible = false; lActionTitle.Text = ActionTitle.Add(SystemCommunication.FriendlyTypeName).FormatAsHtmlTitle(); hfEmailTemplateId.Value = 0.ToString(); cbIsActive.Checked = true; cpCategory.SetValue(( int? )null); tbTitle.Text = string.Empty; tbFromName.Text = string.Empty; tbFrom.Text = string.Empty; tbTo.Text = string.Empty; tbCc.Text = string.Empty; tbBcc.Text = string.Empty; tbSubject.Text = string.Empty; ceEmailTemplate.Text = string.Empty; } var pushNotificationControl = phPushNotification.Controls[0] as PushNotification; if (pushNotificationControl != null) { pushNotificationControl.SetFromCommunication(pushCommunication); } SetEmailMessagePreviewModeEnabled(showMessagePreview); LoadDropDowns(); // SMS Fields mfpSMSMessage.MergeFields.Clear(); mfpSMSMessage.MergeFields.Add("GlobalAttribute"); mfpSMSMessage.MergeFields.Add("Rock.Model.Person"); if (emailTemplate != null) { dvpSMSFrom.SetValue(emailTemplate.SMSFromDefinedValueId); tbSMSTextMessage.Text = emailTemplate.SMSMessage; } }
private static CreateMessageResult CreateSmsMessage(Person person, Dictionary <string, object> mergeObjects, SystemCommunication systemCommunication) { var isSmsEnabled = MediumContainer.HasActiveSmsTransport() && !string.IsNullOrWhiteSpace(systemCommunication.SMSMessage); var createMessageResult = new CreateMessageResult(); var smsNumber = person.PhoneNumbers.GetFirstSmsNumber(); var recipients = new List <RockMessageRecipient>(); if (string.IsNullOrWhiteSpace(smsNumber) || !isSmsEnabled) { var smsWarningMessage = $"No SMS number could be found for {person.FullName}."; if (!isSmsEnabled) { smsWarningMessage = $"SMS is not enabled. {person.FullName} did not receive a notification."; } RockLogger.Log.Warning(RockLogDomains.Jobs, smsWarningMessage); createMessageResult.Warnings.Add(smsWarningMessage); return(createMessageResult); } recipients.Add(new RockSMSMessageRecipient(person, smsNumber, mergeObjects)); var message = new RockSMSMessage(systemCommunication); message.SetRecipients(recipients); createMessageResult.Message = message; return(createMessageResult); }
/// <summary> /// Sends an RSVP reminder <see cref="SystemCommunication"/> to an individual attendee. /// </summary> /// <param name="group">The <see cref="Group"/>.</param> /// <param name="occurrence">The <see cref="AttendanceOccurrence"/>.</param> /// <param name="person">The <see cref="Person"/>.</param> /// <param name="reminder">The <see cref="SystemCommunication"/> to be sent as a reminder.</param> private int SendReminder(Group group, AttendanceOccurrence occurrence, Person person, SystemCommunication reminder) { // Build Lava merge fields. Dictionary <string, object> lavaMergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(null, person); lavaMergeFields.Add("Person", person); lavaMergeFields.Add("Group", group); lavaMergeFields.Add("Occurrence", occurrence); lavaMergeFields.Add("OccurrenceTitle", GetOccurrenceTitle(occurrence)); var smsNumber = person.PhoneNumbers.GetFirstSmsNumber(); if (person.CommunicationPreference == CommunicationType.SMS && !string.IsNullOrWhiteSpace(reminder.SMSMessage) && reminder.SMSFromDefinedValueId.HasValue && !string.IsNullOrWhiteSpace(smsNumber)) { return(SendReminderSMS(person, reminder, lavaMergeFields, smsNumber)); } return(SendReminderEmail(person, reminder, lavaMergeFields)); }
/// <summary> /// Initializes a new instance of the <see cref="RockPushMessage"/> class. /// </summary> /// <param name="systemCommunication">The system communication.</param> public RockPushMessage(SystemCommunication systemCommunication) : this() { InitializePushMessage(systemCommunication); }
/// <summary> /// Sends the expired credit card notices. /// </summary> /// <param name="context">The context.</param> /// <returns></returns> /// <exception cref="Exception">Expiring credit card email is missing.</exception> private SendExpiredCreditCardNoticesResult SendExpiredCreditCardNotices(IJobExecutionContext context) { var dataMap = context.JobDetail.JobDataMap; var rockContext = new RockContext(); // Get the details for the email that we'll be sending out. Guid?systemEmailGuid = dataMap.GetString(AttributeKey.ExpiringCreditCardEmail).AsGuidOrNull(); SystemCommunication systemCommunication = null; if (systemEmailGuid.HasValue) { var systemCommunicationService = new SystemCommunicationService(rockContext); systemCommunication = systemCommunicationService.Get(systemEmailGuid.Value); } if (systemCommunication == null) { throw new Exception("Expiring credit card email is missing."); } // Fetch the configured Workflow once if one was set, we'll use it later. Guid?workflowGuid = dataMap.GetString(AttributeKey.Workflow).AsGuidOrNull(); WorkflowTypeCache workflowType = null; WorkflowService workflowService = new WorkflowService(rockContext); if (workflowGuid != null) { workflowType = WorkflowTypeCache.Get(workflowGuid.Value); } var financialScheduledTransactionQuery = new FinancialScheduledTransactionService(rockContext).Queryable() .Where(t => t.IsActive && t.FinancialPaymentDetail.ExpirationMonthEncrypted != null && (t.EndDate == null || t.EndDate > DateTime.Now)) .AsNoTracking(); List <ScheduledTransactionInfo> scheduledTransactionInfoList = financialScheduledTransactionQuery.Select(a => new ScheduledTransactionInfo { Id = a.Id, FinancialPaymentDetail = a.FinancialPaymentDetail, AuthorizedPersonAliasGuid = a.AuthorizedPersonAlias.Guid, Person = a.AuthorizedPersonAlias.Person }).ToList(); // Get the current month and year DateTime now = DateTime.Now; int currentMonth = now.Month; int currentYYYY = now.Year; // get the common merge fields once, so we don't have to keep calling it for every person, then create a new mergeFields for each person, starting with a copy of the common merge fields var commonMergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(null); var result = new SendExpiredCreditCardNoticesResult { ExaminedCount = scheduledTransactionInfoList.Count() }; foreach (ScheduledTransactionInfo scheduledTransactionInfo in scheduledTransactionInfoList.OrderByDescending(a => a.Id)) { int?expirationMonth = scheduledTransactionInfo.FinancialPaymentDetail.ExpirationMonth; int?expirationYYYY = scheduledTransactionInfo.FinancialPaymentDetail.ExpirationYear; if (!expirationMonth.HasValue || !expirationMonth.HasValue) { continue; } int warningYear = expirationYYYY.Value; int warningMonth = expirationMonth.Value - 1; if (warningMonth == 0) { warningYear -= 1; warningMonth = 12; } if ((warningYear == currentYYYY) && (warningMonth == currentMonth)) { string maskedCardNumber = string.Empty; if (!string.IsNullOrEmpty(scheduledTransactionInfo.FinancialPaymentDetail.AccountNumberMasked) && scheduledTransactionInfo.FinancialPaymentDetail.AccountNumberMasked.Length >= 4) { maskedCardNumber = scheduledTransactionInfo.FinancialPaymentDetail.AccountNumberMasked.Substring(scheduledTransactionInfo.FinancialPaymentDetail.AccountNumberMasked.Length - 4); } string expirationDateMMYYFormatted = scheduledTransactionInfo.FinancialPaymentDetail?.ExpirationDate; var recipients = new List <RockEmailMessageRecipient>(); var person = scheduledTransactionInfo.Person; if (!person.IsEmailActive || person.Email.IsNullOrWhiteSpace() || person.EmailPreference == EmailPreference.DoNotEmail) { continue; } // make a mergeFields for this person, starting with copy of the commonFieldFields var mergeFields = new Dictionary <string, object>(commonMergeFields); mergeFields.Add("Person", person); mergeFields.Add("Card", maskedCardNumber); mergeFields.Add("Expiring", expirationDateMMYYFormatted); recipients.Add(new RockEmailMessageRecipient(person, mergeFields)); var emailMessage = new RockEmailMessage(systemCommunication); emailMessage.SetRecipients(recipients); emailMessage.Send(out List <string> emailErrors); if (emailErrors.Any()) { var errorLines = new StringBuilder(); errorLines.AppendLine(string.Empty); foreach (string error in emailErrors) { errorLines.AppendLine(error); } // Provide better identifying context in case the errors are too vague. var exception = new Exception($"Unable to send email (Person ID = {person.Id}).{errorLines}"); result.EmailSendExceptions.Add(exception); } else { result.NoticesSentCount++; } // Start workflow for this person if (workflowType != null) { Dictionary <string, string> attributes = new Dictionary <string, string>(); attributes.Add("Person", scheduledTransactionInfo.AuthorizedPersonAliasGuid.ToString()); attributes.Add("Card", maskedCardNumber); attributes.Add("Expiring", expirationDateMMYYFormatted); attributes.Add("FinancialScheduledTransactionId", scheduledTransactionInfo.Id.ToString()); StartWorkflow(workflowService, workflowType, attributes, $"{person.FullName} (scheduled transaction Id: {scheduledTransactionInfo.Id})"); } } } return(result); }