/// <summary>
 /// Deprecated Method for adding a new object to the TicketEventNotifications EntitySet. Consider using the .Add method of the associated ObjectSet&lt;T&gt; 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);
 }