public string[] GetTagCompletionList(string prefixText, int count)
        {
            List<string> returnList = new List<string>();
            if(count == 0)
            {
                count = 10;
            }
            string[] tags = prefixText.Replace(" ,", ",").Replace(", ", ",").Split(',');//eliminate extra spaces around commas before split
            string textToCheck = tags[tags.Length - 1];//last element
            if(textToCheck.Trim().Length > 1)//only check if user has typed more than 1 character for the last item of the taglist
            {
                string fixedText = string.Empty;//all that stuff the user typed before the last comma in the text
                if(tags.Length > 1)
                {
                    StringBuilder sb = new StringBuilder();
                    for(int x = 0; x < tags.Length - 1; x++)
                    {
                        sb.Append(tags[x] + ",");
                    }
                    fixedText = sb.ToString();
                }

                TicketDataDataContext context = new TicketDataDataContext();
                var distinctTagsQuery = (
                            from tag in context.TicketTags
                            orderby tag.TagName
                            where tag.TagName.StartsWith(textToCheck)
                            select tag.TagName).Distinct().Take(count);

                
                foreach(var distinctTag in distinctTagsQuery)
                {
                    if(tags.Count(t => t.ToUpperInvariant() == distinctTag.ToUpperInvariant()) < 1)//eliminate any tags that were already used (that are in the fixedText).
                    {
                        returnList.Add(fixedText + distinctTag);//append the other items in the list plus the new tag possibilitiy
                    }
                }
            }
            return returnList.ToArray();


        }
 protected void CreateTicketButton_Click(object sender, EventArgs e)
 {
     if(Page.IsValid)
     {
         TicketDataDataContext context = new TicketDataDataContext();
         Ticket ticket = NewTicketForm.GetNewTicket();
         if(ticket != null)
         {
        //проверка на дублирование заявок
             if(context.Tickets.Count(t => (t.Title == ticket.Title && t.CurrentStatus == "New")) < 1)
             {
                 context.Tickets.InsertOnSubmit(ticket);
                 context.SubmitChanges();
                 NotificationService.QueueTicketEventNotification(ticket.TicketComments[0]);
                 Page.Response.Redirect(string.Format("ViewTicket.aspx?id={0}", ticket.TicketId), true);
             }
             else
             {
                 MessageLabel.Text = "Заявка с таким же названием уже зарегистрирована, переименуйте заявку.";
             }
         }
         MessageLabel.Text = "Невозможно добавить заявку.";
     }
 }
 protected void CreateTicketButton_Click(object sender, EventArgs e)
 {
     if(Page.IsValid)
     {
         TicketDataDataContext context = new TicketDataDataContext();
         Ticket ticket = NewTicketForm.GetNewTicket();
         if(ticket != null)
         {
             //if there is an existing ticket in DB with "New" status and the same title don't submit... this is probably a duplicate submission.
             if(context.Tickets.Count(t => (t.Title == ticket.Title && t.CurrentStatus == "New")) < 1)
             {
                 context.Tickets.InsertOnSubmit(ticket);
                 context.SubmitChanges();
                 NotificationService.QueueTicketEventNotification(ticket.TicketComments[0]);
                 Page.Response.Redirect(string.Format("ViewTicket.aspx?id={0}", ticket.TicketId), true);
             }
             else
             {
                 MessageLabel.Text = "Failed to create ticket. Another ticket was recently created with the same title.";
             }
         }
         MessageLabel.Text = "Unable to create ticket.";
     }
 }
        protected void TicketsLinqDataSource_Selecting(object sender, LinqDataSourceSelectEventArgs e)
        {

            TicketDataDataContext ctx = new TicketDataDataContext();
            var q = from t in ctx.Tickets
                    select t;

            var qfs = q.ApplyListViewSettings(ListSettings, 0);


            e.Result = qfs;

        }
        /// <summary>
        /// Queues the ticket event notification.
        /// </summary>
        /// <param name="comment">The comment associated with the event.</param>
        public static void QueueTicketEventNotification(TicketComment comment)
        {
            lock (registerLock)
            {
                string enabled = ConfigurationManager.AppSettings["EnableEmailNotifications"];
                if (!string.IsNullOrEmpty(enabled) && Convert.ToBoolean(enabled))
                {

                    TicketDataDataContext ctx = new TicketDataDataContext();
                    var ticket = comment.Ticket;
                    var newNotes = ticket.CreateTicketEventNotificationsForComment(comment.CommentId, comment.CommentedBy);
                    foreach (var note in newNotes)
                    {
                        note.CreatedDate = DateTime.Now;
                        note.DeliveryAttempts = 0;

                        if (note.NotifyEmail == "invalid")//notes with invalid email still added to table, but squash delivery schedule.
                        {
                            note.Status = "invalid";
                            note.NextDeliveryAttemptDate = null;
                            note.LastDeliveryAttemptDate = DateTime.Now;
                        }
                        else
                        {


                            note.Status = "queued";
                            var now = DateTime.Now;
                            note.NextDeliveryAttemptDate = now;

                            if (note.NotifyUserReason != "HelpDesk")// for non-broadcasts to helpdesk schedule on the delay 
                            {
                                string delay = ConfigurationManager.AppSettings["EmailNotificationInitialDelayMinutes"];
                                if (!string.IsNullOrEmpty(delay))
                                {
                                    note.NextDeliveryAttemptDate = now.AddMinutes(Convert.ToDouble(delay));
                                }
                            }
                        }
                    }

                    ctx.TicketEventNotifications.InsertAllOnSubmit(newNotes);
                    ctx.SubmitChanges();


                    //Deliver helpdesk broadcasts NOW! New ticket notifications for help desk only 
                    //  happen for brand-new tickets. These users aren't included on further changes
                    //  to the ticket either so there is no reason to wait for consolidations.
                    //
                    //  Since the creation of notes ensures that tickets with a reason of "HelpDesk" 
                    //    are only for tickets where the recipient is not the owner/assigned user we
                    //    don't have to worry about suppressions or consolidations; we can directly 
                    //    send without worrying about the pre-processing that happens with timer 
                    //    triggered mechanism.
                    foreach (var note in newNotes)
                    {
                        if (note.NotifyUserReason == "HelpDesk" && note.NextDeliveryAttemptDate != null)
                        {
                            SendTicketEventNotificationEmail(ctx, note, new List<TicketEventNotification>());
                        }
                    }
                    
                }
            }
        }
        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 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 static void SendTicketEventNotificationEmail(TicketDataDataContext ctx, TicketEventNotification note, List<TicketEventNotification> consolidations)
 {
     sendingLock = new object();
     lock (sendingLock)
     {
         PrepareTicketEventNotificationForDelivery(ctx, note, consolidations);
         DeliverTicketEventNotificationEmail(ctx, note, consolidations);
     }
 }
        /// <summary>
        /// Sends the waiting ticket event notifications.
        /// </summary>
        public static void ProcessWaitingTicketEventNotifications()
        {
            processLock = new object();
            lock (processLock)
            {
                var now = DateTime.Now;

                TicketDataDataContext ctx = new TicketDataDataContext();
                var queuedNotes = from n in ctx.TicketEventNotifications
                                  where n.NextDeliveryAttemptDate.HasValue
                                  orderby n.NextDeliveryAttemptDate
                                  group n by new { n.NotifyUser, n.TicketId } into userTicketNotes
                                  select new { userTicketNotes.Key.NotifyUser, userTicketNotes.Key.TicketId, userTicketNotes };

                foreach (var ut in queuedNotes)
                {


                    string maxConsolidationWait = ConfigurationManager.AppSettings["EmailMaxConsolidationWaitMinutes"];
                    double consolidationWait = 10D;//10 minutes is default max wait
                    if (!string.IsNullOrEmpty(maxConsolidationWait))
                    {
                        consolidationWait = Convert.ToDouble(maxConsolidationWait);
                    }

                    if (ut.userTicketNotes.Count(n => n.NextDeliveryAttemptDate <= now) > 0)// at least 1 note ready, process
                    {
                        var minNextDeliveryTime = ut.userTicketNotes.Min(n => n.NextDeliveryAttemptDate);//gets the min next delivery date for pending notes
                        var minPending = ut.userTicketNotes.SingleOrDefault
                            (
                                note => (note.CommentId == (ut.userTicketNotes.Min(n => n.CommentId)))
                            );// user's oldest pending comment (by comment ID just in case next delivery dates get out of relative order by accident)


                        var maxPendingDeliveryTime = ut.userTicketNotes.Max(n => n.NextDeliveryAttemptDate);// gets the max next delivery date for pending notes
                        var maxPending = ut.userTicketNotes.SingleOrDefault
                            (
                                note =>
                                    (note.CommentId == (ut.userTicketNotes.Max(n => n.CommentId)))
                            );// user's most recent pending comment (by comment ID just in case next delivery dates get out of relative order by accident)



                        if (ut.userTicketNotes.Count(n => n.EventGeneratedByUser != n.NotifyUser) > 0)// at least 1 note generated by user other than recipient
                        {
                            if
                            (
                                (minNextDeliveryTime.Value.AddMinutes(consolidationWait) <= now) || // min delivery date is past the maximum allowable delay
                                (maxPendingDeliveryTime <= now)//max pending is ready to go... commit them all
                            )
                            {
                                List<TicketEventNotification> consolitations = new List<TicketEventNotification>();
                                foreach (var note in ut.userTicketNotes)
                                {
                                    if (note != maxPending)
                                    {
                                        note.Status = "consolidating";
                                        consolitations.Add(note);
                                    }
                                }
                                SendTicketEventNotificationEmail(ctx, maxPending, consolitations);//consolitations will be null if only 1 message
                            }
                            else //max pending not yet ready... continue to wait until it is ready before sending
                            {
                                foreach (var note in ut.userTicketNotes)
                                {
                                    if (note != maxPending)
                                    {
                                        note.Status = "consolidating";
                                    }
                                    // else : max pending not ready yet - do nothing for max pending (will get sent later)

                                }
                                ctx.SubmitChanges();
                            }
                        }
                        else // all waiting are for events generated by recipient
                        {
                            if
                            (
                                (minNextDeliveryTime.Value.AddMinutes(consolidationWait) <= now) || // min delivery date is past the maximum allowable delay
                                (maxPendingDeliveryTime <= now)//max pending is ready to go... commit them all
                            )
                            {
                                //max ready or min past max wait time, but all notes are for events by recipient... kill them all...

                                foreach (var note in ut.userTicketNotes)
                                {
                                    note.NextDeliveryAttemptDate = null;
                                    note.Status = "suppressed";
                                    note.LastDeliveryAttemptDate = now;
                                }
                                ctx.SubmitChanges();
                            }
                            //else max note ready, do nothing
                        }
                    }
                    //else : no notes ready - do nothing
                }
            }
        }
        private void SendFileToClient(HttpContext context, int fileId)
        {
            HttpResponse httpResponse = context.Response;
            int bufferSize = 100;
            byte[] outByte = new byte[bufferSize];

            long startIndex = 0;

            TicketDataDataContext ctx = new TicketDataDataContext();

            var query = from ta in ctx.TicketAttachments
                        where ta.FileId == fileId
                        select new
                        {
                            ta.FileName,
                            ta.FileSize,
                            ta.FileType,
                            ta.FileContents
                        };

            DbCommand cmd = ctx.GetCommand(query);

            DbConnection conn = cmd.Connection;
            conn.Open();
            DbDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess);

            while(reader.Read())
            {

                string filename = reader.GetString(0);
                int filelength = reader.GetInt32(1);//for some reason GetString doesn't convert int32 to string, throws an error. Using alternate access via indexer.
                string contenttype = reader.GetString(2);

                // set the mime type: must occur before getting the file content.
                httpResponse.ContentType = contenttype;

                httpResponse.Clear();
                httpResponse.BufferOutput = false;

                context.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + filename + "\";");
                httpResponse.AppendHeader("Content-Length", filelength.ToString());

                BinaryWriter writer = new BinaryWriter(httpResponse.OutputStream);

                bool fileComplete = false;
                while(!fileComplete)
                {
                    if(startIndex + bufferSize >= filelength)
                    {
                        fileComplete = true;
                    }
                    startIndex += reader.GetBytes(3, startIndex, outByte, 0, bufferSize);
                    writer.Write(outByte);
                    writer.Flush();
                }
                writer.Close();
                httpResponse.OutputStream.Close();
            }
            reader.Close();
            conn.Close();
        }
Exemple #11
0
        public SyndicationFeedFormatter CreateFeed()
        {
            SyndicationFeed feed = null;

            bool isEnabled = false;
            string enabledString = ConfigurationManager.AppSettings["EnableRSS"];

            if (!string.IsNullOrEmpty(enabledString))
            {
                isEnabled = Convert.ToBoolean(enabledString);
            }

            if (!isEnabled)
            {

                feed = new SyndicationFeed("TicketDesk Feed Disabled", "FeedDisabled", null, "FeedDisabled", DateTimeOffset.Now, null);

            }
            else
            {
                TicketDataDataContext ctx = new TicketDataDataContext();

                IQueryable<Ticket> tickets = from t in ctx.Tickets
                                             select t;

                string idArray = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["id"];
                if (idArray != null)
                {
                    tickets = from t in tickets
                              where idArray.Split(',').Contains(t.TicketId.ToString())
                              select t;
                }

                string categoryArray = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["category"];
                if (categoryArray != null)
                {
                    tickets = from t in tickets
                              where categoryArray.Split(',').Contains(t.Category)
                              select t;
                }

                string tagArray = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["tag"];
                if (tagArray != null)
                {
                    tickets = from t in tickets
                              join tags in ctx.TicketTags
                              on t.TicketId equals tags.TicketId
                              where tagArray.Split(',').Contains(tags.TagName)
                              select t;
                }

                string priorityArray = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["priority"];
                if (priorityArray != null)
                {
                    tickets = from t in tickets
                              where priorityArray.Split(',').Contains(t.Priority)
                              select t;
                }

                string assignedArray = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["assigned"];
                if (assignedArray != null)
                {
                    tickets = from t in tickets
                              where assignedArray.Split(',').Contains(t.AssignedTo)
                              select t;
                }

                string ownerArray = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["owner"];
                if (ownerArray != null)
                {
                    tickets = from t in tickets
                              where ownerArray.Split(',').Contains(t.Owner)
                              select t;
                }

                string statusArray = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["status"];
                if (statusArray != null)
                {
                    tickets = from t in tickets
                              where statusArray.Split(',').Contains(t.CurrentStatus)
                              select t;
                }

                string closed = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["closed"];
                if ((statusArray == null) && ((closed == null) || ((closed != "yes") && (closed != "1"))))
                {
                    tickets = from t in tickets
                              where t.CurrentStatus != "Closed"
                              select t;
                }

                List<SyndicationItem> items = new List<SyndicationItem>();
                foreach (Ticket ticket in tickets.Distinct().OrderByDescending(t => t.LastUpdateDate))
                {
                    SyndicationItem item = GetRSSEntryForTicket(ticket);
                    items.Add(item);
                }

                 feed = new SyndicationFeed("TicketDesk RSS", "TicketDeskRSS", null, "TicketDeskRSS", DateTimeOffset.Now, items);
            }
            string query = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["format"];
            SyndicationFeedFormatter formatter = null;
            if (query == "atom")
            {

                formatter = new Atom10FeedFormatter(feed);

            }
            else
            {

                formatter = new Rss20FeedFormatter(feed);
            }

            return formatter;
        }
 private void DeleteTicketAttachment(int fileId)
 {
     TicketDataDataContext ctx = new TicketDataDataContext();
     var file = ctx.TicketAttachments.SingleOrDefault(ta => ta.FileId == fileId);
     ctx.TicketAttachments.DeleteOnSubmit(file);
     ctx.SubmitChanges();
 }