/// <summary> /// Deprecated Method for adding a new object to the TicketEventNotifications EntitySet. Consider using the .Add method of the associated ObjectSet<T> property instead. /// </summary> public void AddToTicketEventNotifications(TicketEventNotification ticketEventNotification) { base.AddObject("TicketEventNotifications", ticketEventNotification); }
private string GetTextBody(TicketEventNotification note, string notifyUser, int minComment) { return GetTicketNotificationTextEmailContent(note, minComment); }
/// <summary> /// Create a new TicketEventNotification object. /// </summary> /// <param name="ticketId">Initial value of the TicketId property.</param> /// <param name="commentId">Initial value of the CommentId property.</param> /// <param name="notifyUser">Initial value of the NotifyUser property.</param> /// <param name="notifyUserDisplayName">Initial value of the NotifyUserDisplayName property.</param> /// <param name="notifyEmail">Initial value of the NotifyEmail property.</param> /// <param name="notifyUserReason">Initial value of the NotifyUserReason property.</param> /// <param name="createdDate">Initial value of the CreatedDate property.</param> /// <param name="deliveryAttempts">Initial value of the DeliveryAttempts property.</param> /// <param name="status">Initial value of the Status property.</param> /// <param name="eventGeneratedByUser">Initial value of the EventGeneratedByUser property.</param> public static TicketEventNotification CreateTicketEventNotification(global::System.Int32 ticketId, global::System.Int32 commentId, global::System.String notifyUser, global::System.String notifyUserDisplayName, global::System.String notifyEmail, global::System.String notifyUserReason, global::System.DateTime createdDate, global::System.Int32 deliveryAttempts, global::System.String status, global::System.String eventGeneratedByUser) { TicketEventNotification ticketEventNotification = new TicketEventNotification(); ticketEventNotification.TicketId = ticketId; ticketEventNotification.CommentId = commentId; ticketEventNotification.NotifyUser = notifyUser; ticketEventNotification.NotifyUserDisplayName = notifyUserDisplayName; ticketEventNotification.NotifyEmail = notifyEmail; ticketEventNotification.NotifyUserReason = notifyUserReason; ticketEventNotification.CreatedDate = createdDate; ticketEventNotification.DeliveryAttempts = deliveryAttempts; ticketEventNotification.Status = status; ticketEventNotification.EventGeneratedByUser = eventGeneratedByUser; return ticketEventNotification; }
private void PrepareTicketEventNotificationForDelivery(TicketEventNotification note, List<TicketEventNotification> consolidations) { double resendDelay = GetEmailResendDelayMinutes(); int maxRetries = GetEmailMaxDeliveryAttempts(); note.DeliveryAttempts += 1; if (note.DeliveryAttempts < maxRetries) { note.Status = "pending"; note.NextDeliveryAttemptDate = DateTime.Now.AddMinutes(resendDelay * note.DeliveryAttempts); int backTrackSeconds = -1; foreach (var consolidateNote in consolidations.OrderByDescending(c => c.CommentId)) { //this will reorder the next delivery attempt date for consolidations in descending order keeping the relative order for them all consolidateNote.NextDeliveryAttemptDate = note.NextDeliveryAttemptDate.Value.AddSeconds(backTrackSeconds); consolidateNote.DeliveryAttempts += 1; backTrackSeconds--; } } else { note.Status = "final-attempt"; note.NextDeliveryAttemptDate = null; foreach (var consolidateNote in consolidations) { consolidateNote.NextDeliveryAttemptDate = null; consolidateNote.DeliveryAttempts += 1; } } /* Unhandled edge case: * We've set the next delivery date for the note we are sending to a future date in case something seriously goes wrong during sending the email. * This is important because if the sending routine exits and we hadn't committed a next delivery date and all that, it would potentially cause * an inifite retry-loop. * * The edge case is that new notes could have been queued after we read the queue from the DB into memory. Those notes would have a delivery * date much lower than the retry we just scheduled for this note. We could try to predict and work around this potential case, but it is * such an extreme edge case (something has to queue AND an unhandled exception has to happen in the sending routine both). * * By not handling this here, the worst that can happen is that the new queued note will simply cause a retry far sooner than we wanted. * A work-around for this case has a significant perforance impact though, and so we are not going to implement that here... besides, if * there is an unhandled exception in the send routine it will likely be something so severe that future send attemps are all going to fail * for this user's notes indefinitly until the issue is debugged or a major data corruption issue is fixed anyway. */ }
private void DeliverTicketEventNotificationEmail(TicketEventNotification note, List<TicketEventNotification> consolidations) { bool status = false; try { //TODO: Modify body generation to flag comments for the current event and all consolidations. TicketComment comment = note.TicketComment; Ticket ticket = comment.Ticket; int minComment = note.CommentId; if (consolidations.Count() > 0) { minComment = consolidations.Min(c => c.CommentId); } string htmlBody = GetHtmlBody(note, note.NotifyUser, minComment); string textBody = GetTextBody(note, note.NotifyUser, minComment); string displayFrom = GetFromEmailDisplayName(); string addressFrom = GetFromEmailAddress(); string blindCopyTo = GetBlindCopyToEmailAddress(); string tStat = ticket.CurrentStatus; if (string.IsNullOrEmpty(ticket.AssignedTo)) { tStat = (ticket.TicketComments.Count() < 2) ? "New" : "Unassigned"; } string subject = string.Format("Ticket {0} ({1}): {2}", ticket.TicketId.ToString(), tStat, ticket.Title); status = EmailHandler.SendEmail(addressFrom, displayFrom, note.NotifyEmail, note.NotifyUserDisplayName, blindCopyTo, subject, htmlBody, textBody); } catch(Exception ex) { var exception = new ApplicationException("Failure in Email Delivery", ex); //TODO: we need to log/record this here and NOT rethrow it } //TODO: There remains a small possibility that a note could be send above, but the below database update might break. // this is a case that could lead to a user being notified multiple times. // I would like to avoid the transactions namespace since it can require MSDTC in some cases. //wrap-up after sending, and update the notification status DateTime now = DateTime.Now; note.LastDeliveryAttemptDate = now; foreach (var consolidateNote in consolidations) { consolidateNote.LastDeliveryAttemptDate = now; } if (!status) { if (note.Status == "final-attempt") { note.Status = "failed"; foreach (var consolidateNote in consolidations) { consolidateNote.Status = "consolidated-failed"; } } if (note.Status == "pending") { note.Status = "queued"; } //check for notes that queued after the send process first started here, and increment their nextDeliveryDate so they don't cause // resends to happen sooner than the next delivery retry interval warrants. This is only needed when we are rescheduling a note // for later delivery. NotificationRepository.EnsureValidNextDeliveryDateForNewlyQueudNotifications(note.TicketId, note.NotifyUser, note.CommentId, note.NextDeliveryAttemptDate.Value); } else { note.Status = "sent"; note.NextDeliveryAttemptDate = null; foreach (var consolidateNote in consolidations) { consolidateNote.Status = "consolidated"; consolidateNote.NextDeliveryAttemptDate = null; } } }
private void SendTicketEventNotificationEmail(TicketEventNotification note, List<TicketEventNotification> consolidations) { lock (sendingLock) { PrepareTicketEventNotificationForDelivery(note, consolidations); DeliverTicketEventNotificationEmail(note, consolidations); } }
private List<TicketEventNotification> CreateNotesForUsers(Dictionary<string, string> userReasons, TicketComment comment) { List<TicketEventNotification> newNotes = new List<TicketEventNotification>(); var dt = DateTime.Now; foreach (var userReason in userReasons) { var note = new TicketEventNotification(); note.CreatedDate = dt; note.EventGeneratedByUser = comment.CommentedBy; //validate email address, if not valid we'll queue the note but not bother sending it (and having to go through the retries) bool emailValid = false; string email = Security.GetUserEmailAddress(userReason.Key); var rxv = new RegexStringValidator(@"^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"); try { rxv.Validate(email); emailValid = true; } catch { } note.NotifyEmail = (emailValid && !string.IsNullOrEmpty(email)) ? email : "invalid"; note.NotifyUser = userReason.Key; note.NotifyUserDisplayName = Security.GetUserDisplayName(userReason.Key); note.NotifyUserReason = userReason.Value; newNotes.Add(note); } return newNotes; }
private string GenerateTicketNotificationEmailBody(TicketEventNotification notification, int firstUnsentCommentId, string templateToRender) { this.Security.GetCurrentUserName = delegate() { return notification.NotifyUser; }; var ticket = notification.TicketComment.Ticket; var vd = new ViewDataDictionary(ticket); vd.Add("siteRootUrl", AppSettings.SiteRootUrlForEmail); vd.Add("firstUnsentCommentId", firstUnsentCommentId); vd.Add("formatForEmail", true); using (StringWriter sw = new StringWriter()) { var fakeResponse = new HttpResponse(sw); var fakeContext = new HttpContext(new HttpRequest("", AppSettings.SiteRootUrlForEmail, ""), fakeResponse); var fakeControllerContext = new ControllerContext ( new HttpContextWrapper(fakeContext), new RouteData(), this ); fakeControllerContext.RouteData.Values.Add("controller", "EmailTemplate"); ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(fakeControllerContext, templateToRender); ViewContext vc = new ViewContext(fakeControllerContext, new FakeView(), vd, new TempDataDictionary(), sw); HtmlHelper h = new HtmlHelper(vc, new ViewPage()); h.RenderPartial(templateToRender, ticket, vd); return sw.GetStringBuilder().ToString(); } }
public string GenerateTicketNotificationHtmlEmailBody(TicketEventNotification notification, int firstUnsentCommentId) { var forOutlook = (AppSettings.EnableOutlookFriendlyHtmlEmail); var templateToRender = (forOutlook) ? MVC.Admin.EmailTemplate.Views.TicketNotificationOutlookHtmlEmailTemplate : MVC.Admin.EmailTemplate.Views.TicketNotificationHtmlEmailTemplate; return GenerateTicketNotificationEmailBody(notification, firstUnsentCommentId, templateToRender); }
public string GenerateTicketNotificationTextEmailBody(TicketEventNotification notification, int firstUnsentCommentId) { var templateName = MVC.Admin.EmailTemplate.Views.TicketNotificationTextEmailTemplate; return GenerateTicketNotificationEmailBody(notification, firstUnsentCommentId, templateName); }