private static void DeliverTicketEventNotificationEmail(TicketDataDataContext ctx, 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;

                SmtpClient client = new SmtpClient();
                string url = null;
                string rootUrl = ConfigurationManager.AppSettings["WebRootUrlForEmailLinks"];
                if (!string.IsNullOrEmpty(rootUrl))
                {
                    if (!rootUrl.EndsWith("/"))
                    {
                        rootUrl += "/";
                    }
                    url = string.Format("{0}ViewTicket.aspx?id={1}", rootUrl, ticket.TicketId.ToString());
                }

                int minComment = note.CommentId;
                if (consolidations.Count() > 0)
                {
                    minComment = consolidations.Min(c => c.CommentId);
                }
                string body = NotificationUtilities.GetHTMLBody(ticket, url, note.NotifyUser, minComment);


                string displayFrom = ConfigurationManager.AppSettings["FromEmailDisplayName"];
                string addressFrom = ConfigurationManager.AppSettings["FromEmailAddress"];
                string blindCopyTo = ConfigurationManager.AppSettings["BlindCopyToEmailAddress"];
                MailAddress bccAddr = null;
                if (!string.IsNullOrEmpty(blindCopyTo))
                {
                    bccAddr = new MailAddress(blindCopyTo);
                }

                MailAddress fromAddr = new MailAddress(addressFrom, displayFrom);
                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);
                MailAddress toAddr = new MailAddress(note.NotifyEmail, note.NotifyUserDisplayName);

                MailMessage msg = new MailMessage(fromAddr, toAddr);
                if (bccAddr != null)
                {
                    msg.Bcc.Add(bccAddr);
                }
                msg.Subject = subject;
                msg.Body = body;  //body;
                msg.IsBodyHtml = true;

                client.Send(msg);

                status = true;
            }
            catch (Exception ex)
            {
                Exception newEx = new ApplicationException("Failure in Email Delivery Timer", ex);
                HttpContext mockContext = (new TicketDesk.Engine.MockHttpContext(false)).Context;
                Elmah.ErrorLog.GetDefault(mockContext).Log(new Elmah.Error(newEx));
            }
            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.
                TicketDataDataContext ctxNew = new TicketDataDataContext();//existing context will have cached data from the past read. Do this on new context
                var newNotes = from tn in ctxNew.TicketEventNotifications
                               where tn.CommentId > note.CommentId && tn.NextDeliveryAttemptDate <= note.NextDeliveryAttemptDate
                               select tn;
                if (newNotes.Count() > 0)
                {
                    int advanceSeconds = 1;
                    foreach (var newNote in newNotes)
                    {
                        newNote.NextDeliveryAttemptDate = note.NextDeliveryAttemptDate.Value.AddSeconds(advanceSeconds);
                        advanceSeconds++;
                    }
                    ctxNew.SubmitChanges();
                }
            }
            else
            {
                note.Status = "sent";
                note.NextDeliveryAttemptDate = null;
                foreach (var consolidateNote in consolidations)
                {
                    consolidateNote.Status = "consolidated";
                    consolidateNote.NextDeliveryAttemptDate = null;

                }

            }
            ctx.SubmitChanges();//submit changes ticket-by-ticket so a failure later doesn't scrap the status update for tickets previously sent

        }
 private static void SendTicketEventNotificationEmail(TicketDataDataContext ctx, TicketEventNotification note, List<TicketEventNotification> consolidations)
 {
     sendingLock = new object();
     lock (sendingLock)
     {
         PrepareTicketEventNotificationForDelivery(ctx, note, consolidations);
         DeliverTicketEventNotificationEmail(ctx, note, consolidations);
     }
 }
        private static void PrepareTicketEventNotificationForDelivery(TicketDataDataContext ctx, TicketEventNotification note, List<TicketEventNotification> consolidations)
        {
            string resendDelaySetting = ConfigurationManager.AppSettings["EmailResendDelayMinutes"];
            double resendDelay = 5D;
            if (!string.IsNullOrEmpty(resendDelaySetting))
            {
                resendDelay = Convert.ToDouble(resendDelay);
            }

            string maxRetriesSetting = ConfigurationManager.AppSettings["EmailMaxDeliveryAttempts"];
            int maxRetries = 5;
            if (!string.IsNullOrEmpty(maxRetriesSetting))
            {
                maxRetries = Convert.ToInt32(maxRetriesSetting);
            }

            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;
                }
            }

            ctx.SubmitChanges();//submit changes before email attempt... this way if there is an unhandled failure, the ticket will still have incremented the delivery attempt and next retry values (prevents accidental spam)

            /* 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 detach_TicketEventNotifications(TicketEventNotification entity)
		{
			this.SendPropertyChanging();
			entity.TicketComment = null;
		}
 partial void DeleteTicketEventNotification(TicketEventNotification instance);
 partial void UpdateTicketEventNotification(TicketEventNotification instance);
 partial void InsertTicketEventNotification(TicketEventNotification instance);
Exemple #8
0
        private TicketEventNotification CreateTicketEventNotificationForUser(int commentId, string commentBy, string user, string userType)
        {
            TicketEventNotification note = null;
            if (!string.IsNullOrEmpty(user))
            {
                bool emailValid = false;
                string email = SecurityManager.GetUserEmailAddress(user);

                // replaced pattern with variation based on the stock MS regex validator control's built-in pattern for email addresses
                //var rxv = new RegexStringValidator(@"^[a-zA-Z\.\-_]+@([a-zA-Z\.\-_]+\.)+[a-zA-Z]{2,4}$");
                var rxv = new RegexStringValidator(@"^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");
                try
                {
                    rxv.Validate(email);
                    emailValid = true;
                }
                catch { }

                note = new TicketEventNotification();
                note.TicketId = TicketId;
                note.CommentId = commentId;
                note.NotifyUser = SecurityManager.GetFormattedUserName(user);

                note.NotifyUserDisplayName = SecurityManager.GetUserDisplayName(user);
                note.NotifyUserReason = userType;
                note.EventGeneratedByUser = commentBy;
                if (!string.IsNullOrEmpty(email) && emailValid)
                {
                    note.NotifyEmail = email;
                }
                else
                {
                    note.NotifyEmail = "invalid";
                }
            }
            return note;
        }