protected override Task AfterEventLoggedAsync(object sender, SysEventArgs e)
        {
            //if (e.GetEventCategory() == SysEventCategory.Issue)
            {
                try
                {
                    var config = EmailDispatcher.Instance.EmailSettings;

                    //if (config?.Notifications.Enabled ?? false)
                    {
                        // clone entities to avoid trouble when the dbcontext that created them goes out of scope (disposed)
                        e.User        = ((AppUser)e.User).Clone();
                        e.ObjectState = config;

                        return(_emailProducer.ProcessAsync(new SysEventArgs <Issue>(e)));
                    }
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(ex);
                }
            }

            return(Task.CompletedTask);
        }
 /// <summary>
 /// Attempts to invoke the <see cref="Notify"/> event.
 /// </summary>
 /// <param name="args">The event data.</param>
 internal static void OnNotify(SysEventArgs args)
 {
     try
     {
         Notify?.Invoke(typeof(ApplicationDbContext), args);
     }
     catch
     {
     }
 }
 Task TrackEmailConfigurationChange(object sender, SysEventArgs e)
 {
     if (!_updatingEmailSettings && e.EventType == SysEventType.EmailConfigUpdated)
     {
         Logger.LogInformation("E-mail configuration has been changed. Invaliding the application database context.");
         lock (_emailSettingsSyncLock)
         {
             return(SyncEmailSettings());
         }
     }
     return(Task.CompletedTask);
 }
Beispiel #4
0
 protected override Task AfterEventLoggedAsync(object sender, SysEventArgs e) => _notifier.ProcessAsync(e);
Beispiel #5
0
        /// <summary>
        /// Asynchronously produces notification messages for all intended recipients based on the specified event type.
        /// </summary>
        /// <param name="args">An object that holds the event data.</param>
        /// <param name="cancellationToken">The token used to cancel an ongoing async operation.</param>
        /// <returns></returns>
        public virtual Task ProcessAsync(SysEventArgs args, CancellationToken cancellationToken = default(CancellationToken))
        {
            CurrentEvent = args;
            if (args.EventType == SysEventType.Unspecified)
            {
                return(Task.CompletedTask);
            }

            try
            {
                var userName = GetUserName(args.User);
                var targetId = GetUserId(args.User);
                var cat      = args.GetEventCategory();

                if (cat == SysEventCategory.User && args.Data is IdentityUser iduser)
                {
                    userName = iduser.UserName;
                    targetId = iduser.Id;
                }

                int?dataId   = null;
                var dataName = Empty;

                if (args.Data is ILogItem data)
                {
                    dataId   = data.Id;
                    dataName = data.Name;
                }

                string      message     = null;
                MessageType messageType = MessageType.Info;

                switch (CurrentEvent.EventType)
                {
                case SysEventType.LoginFailure:
                    messageType = MessageType.Warn;
                    message     = Format(SysEventLoginFailure, userName);
                    break;

                case SysEventType.LoginSuccess:
                    message = Format(SysEventLoginSuccess, userName);
                    break;

                case SysEventType.Logout:
                    message = Format(SysEventLogout, userName);
                    break;

                case SysEventType.IssueCreated:
                    message = Format(SysEventIssueCreated, userName);
                    break;

                case SysEventType.IssueAssigned:
                    message = Format(SysEventIssueAssigned, dataId, userName);
                    break;

                case SysEventType.IssueUpdated:
                    message = Format(SysEventIssueUpdated, dataId, userName);
                    break;

                case SysEventType.IssueClosed:
                    message = Format(SysEventIssueClosed, dataId, userName);
                    break;

                case SysEventType.IssueReopened:
                    message = Format(SysEventIssueReopened, userName);
                    break;

                case SysEventType.IssueDeleted:
                    messageType = MessageType.Warn;
                    message     = Format(SysEventIssueDeleted, userName);
                    break;

                case SysEventType.UserRegistered:
                    message = Format(SysEventUserRegistered, userName);
                    break;

                case SysEventType.UserCreated:
                    messageType = MessageType.Success;
                    message     = Format(SysEventUserCreated, userName);
                    break;

                case SysEventType.UserUpdated:
                    messageType = MessageType.Success;
                    message     = Format(SysEventUserUpdated, userName);
                    break;

                case SysEventType.UserDeleted:
                    messageType = MessageType.Warn;
                    message     = Format(SysEventUserDeleted, userName);
                    break;

                case SysEventType.UserImported:
                    message = Format(SysEventUserImported, userName);
                    break;

                case SysEventType.UserPasswordChanged:
                    message = Format(SysEventUserPasswordChanged, userName);
                    break;

                case SysEventType.CategoryCreated:
                    messageType = MessageType.Success;
                    message     = Format(SysEventCategoryCreated, dataName);
                    break;

                case SysEventType.CategoryUpdated:
                    messageType = MessageType.Success;
                    message     = Format(SysEventCategoryUpdated, dataName);
                    break;

                case SysEventType.CategoryDeleted:
                    messageType = MessageType.Warn;
                    message     = Format(SysEventCategoryDeleted, dataName);
                    break;

                case SysEventType.EmailConfigUpdated:
                    messageType = MessageType.Success;
                    message     = SysEventEmailConfigUpdated;
                    break;

                case SysEventType.EmailSendFailed:
                    messageType = MessageType.Warn;
                    message     = SysEventEmailSendFailed;
                    break;

                case SysEventType.EmailSendSuccess:
                    messageType = MessageType.Success;
                    message     = SysEventEmailSendSuccess;
                    break;

                default:
                    break;
                }
                if (message != null)
                {
                    Consumer.Enqueue(CreateNotification(message, targetId, messageType, userName));
                    Consumer.Notify();
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, Empty);
            }

            return(Task.CompletedTask);
        }
 /// <summary>
 /// Events logged on behalf of the <see cref="ApplicationDbContext"/>.
 /// </summary>
 /// <param name="sender">The sender of the event.</param>
 /// <param name="e">The event data.</param>
 /// <returns></returns>
 private Task Db_SysEvent(object sender, SysEventArgs e) => EventLogger.LogAsync(e);
 /// <summary>
 /// Method invoked after a system event has been logged. Since it does nothing, classes that inherit <see cref="HelpDeskControllerBase"/> should override it to do something meaningful.
 /// </summary>
 /// <param name="sender">The sender of the event.</param>
 /// <param name="e">The event data.</param>
 /// <returns></returns>
 protected virtual Task AfterEventLoggedAsync(object sender, SysEventArgs e)
 => Task.CompletedTask;
Beispiel #8
0
        /// <summary>
        /// Asynchronously produces e-mails and notification messages for all intended recipients based on the specified event type.
        /// </summary>
        /// <param name="args">An object that holds issue-related event data.</param>
        /// <param name="cancellationToken">The token used to cancel an ongoing async operation.</param>
        /// <returns></returns>
        public override async Task ProcessAsync(SysEventArgs args, CancellationToken cancellationToken = default(CancellationToken))
        {
            // give a chance to the base message producer to process the event
            await base.ProcessAsync(args, cancellationToken);

            if (!(args is SysEventArgs <Issue> e))
            {
                throw new ArgumentException($"Must be of type {nameof(SysEventArgs<Issue>)}.", nameof(args));
            }

            var issue    = e.Data ?? throw new ArgumentNullException("Data");
            var user     = (e.User as AppUser) ?? throw new ArgumentNullException("User");
            var settings = e.ObjectState as EmailSettingsViewModel ?? throw new ArgumentNullException("ObjectState", $"ObjectState must be of type {nameof(EmailSettingsViewModel)}");

            var notifs = settings.Notifications;

            _emailNotificationsEnabled = notifs.Enabled ?? false;

            try
            {
                if (issue.User == null && e.EventType != SysEventType.IssueDeleted)
                {
                    issue = await _issueRepo.All(q => q.QueryIssues(id: issue.Id, withIncludes: true)).SingleOrDefaultAsync();
                }

                var fullName          = user.FullName();
                var userName          = user.UserName;
                var notify            = false;
                var outgoing          = settings.Outgoing;
                var from              = outgoing.FromDisplay;
                var useFromNameForAll = outgoing.UseFromNameForAll ?? false;
                var replyToAddrList   = new InternetAddressList();

                if (!string.IsNullOrWhiteSpace(outgoing.ReplyTo))
                {
                    replyToAddrList.AddRange(outgoing.ReplyTo);
                }

                EmailTemplate temp;
                var           templates = settings.Templates;

                switch (e.EventType)
                {
                case SysEventType.IssueCreated:
                {
                    // Sent to technicians when a new ticket arrives. All technicians that have permissions to the category get one of these.

                    // Ticket confirmation notification  (the one users get after submitting a new ticket)?
                    notify = notifs.TicketConfirmationNotification ?? false;

                    if (notify && user.SendEmail && user.EmailConfirmed)
                    {
                        // "Ticket confirmation" email template: Sent to the ticket-submitter after the app received his ticket.
                        temp = templates.TicketConfirmation;

                        var m = CreateMessage(
                            temp.ReplaceSubject(issue.Subject).ToString(),
                            temp.ReplaceBody(issue.Body, issue.Subject)
                            .Replace("#Numero_ticket#", $"{issue.Id}")
                            .Replace("#Articles_Base_Connaissances#", string.Empty)
                            //.Replace("#Suggested_KB_articles#", string.Empty)
                            .ToString(),
                            from,
                            to: user.Email
                            );

                        m.ReplyTo.AddRange(replyToAddrList);
                        Enqueue(WrapMessage(m, user.Id));
                    }

                    temp = templates.NewTicket;
                    var subj = temp.ReplaceSubject(issue.Subject).ToString();
                    var body = temp.ReplaceBody(issue.Body, issue.Subject).Replace("#Numero_ticket#", $"{issue.Id}").ToString();

                    from = user.GetEmailAddress();

                    // notify all admins?
                    if (notifs.NotifyAllAdmins ?? false)
                    {
                        var qry = _userRepo.All(q => q.NotDisabled().Admins().Not(user.Id).CanReceiveEmails());
                        await ForUsersAsync(qry, subj, body, from, replyToAddrList, cancellationToken);
                    }

                    // notify techs in their categories?
                    if (notifs.NotifyTechs ?? false)
                    {
                        var qry = _userRepo.All(q => q.NotDisabled().Techs().Not(user.Id).CanReceiveEmails());
                        await ForUsersAsync(qry, subj, body, from, replyToAddrList, cancellationToken);
                    }
                }
                break;

                case SysEventType.IssueAssigned:
                {
                    // Notify ALL technicians in a category when another technician TAKES a ticket
                    temp = await TemplateForUpdate($"{fullName} a pris en charge la résolution du ticket #{issue.Id}.");

                    if (user.IsTech && (notifs.NotifyAllTechsOnTechTakeOver ?? false))
                    {
                        await NotifyTechs(temp);
                    }

                    // notify ticket owner
                    NotifyOwner(temp);
                }
                break;

                case SysEventType.IssueUpdated:
                {
                    // Sent to both technicians and ticket-submitter (and all ticket-subscribers if any) when a new reply is added to the ticket
                    temp = await TemplateForUpdate($"Le ticket #{issue.Id} a été mis à jour par {fullName}.");

                    if (issue.UpdatedByUser)
                    {
                        from = user.GetEmailAddress();
                        var techsNotified = false;

                        // Notify ALL technicians in a category when a customer updates a ticket
                        // (not just the ticket-technician and ticket-subscribers)?
                        if (issue.UpdatedForTechView || (notifs.NotifyAllTechsOnCustomerUpdate ?? false))
                        {
                            await NotifyTechs(temp);

                            techsNotified = true;
                        }

                        await NotifyAssignee(temp);

                        // send to all subscribers but the submitter
                        var qry = _subsRepo.All(q => q.QueryIssueSubscribers(issue.Id).But(user.Id));

                        if (techsNotified)
                        {
                            qry = qry.NotTechs(); // exclude the techs who've been notified previously
                        }
                        await ForSubscribersAsync(qry, temp.Subject, temp.Body, from, replyToAddrList, cancellationToken);
                    }
                    else
                    {
                        // send to submitter
                        NotifyOwner(temp);

                        // send to subscribers except the owner and updater
                        var qry = _subsRepo.All(q => q.QueryIssueSubscribers(issue.Id).But(user.Id).But(issue.User.Id));

                        await ForSubscribersAsync(qry, temp.Subject, temp.Body, from, replyToAddrList, cancellationToken);
                    }
                }
                break;

                case SysEventType.IssueClosed:
                {
                    // Sent to subscribers when a ticket is closed. Note that "Ticket closed notifications" setting has to be on.
                    if (notifs.TicketClosedNotification ?? false)
                    {
                        temp = await TemplateForUpdate($"Le ticket #{issue.Id} a été fermé par {fullName}.");

                        NotifyOwner(temp);
                    }
                }

                break;

                case SysEventType.IssueReopened:
                    // no template for this scenario?
                    break;

                case SysEventType.IssueDeleted:
                    // no template for this scenario?
                    break;

                default:
                    // definitely no template for this scenario!
                    break;
                }

                Consumer.Notify();

                async Task <EmailTemplate> TemplateForUpdate(string whatHappened)
                {
                    var comments = await _commentRepo.GetAsync(q => q.QueryCommentsForIssue(issue.Id).Skip(0).Take(3).ToArray());

                    var recent = string.Empty;

                    if (comments.Length > 0)
                    {
                        try
                        {
                            recent = await _emailTemplatesViewRender.RenderToStringAsync(TEMPLATE_RECENT_COMMENTS, comments);
                        }
                        catch (Exception ex)
                        {
                            Logger.LogWarning(ex, "An error occured while rendering the recent comments e-mail template.");

                            var sb = new StringBuilder("<h3>Messages récents</h3>");

                            foreach (var c in comments)
                            {
                                sb.AppendLine();
                                sb.AppendLine(c.Body);
                            }

                            recent = sb.ReplaceLineBreaks().ToString().Trim();
                        }
                    }

                    temp = templates.TicketUpdated;
                    var subj = temp.ReplaceSubject(issue.Subject).ToString();
                    var body = temp
                               .ReplaceBody(issue.Body, issue.Subject)
                               .Replace("#Quoi_De_Neuf#", whatHappened)
                               .Replace("#Messages_recents#", recent)
                               .Replace("#Categorie#", issue.Category?.Name)
                               .Replace("#Statut#", issue.Status?.Name)
                               .Replace("#Priorite#", UtilExtensions.PriorityName(issue.Priority))
                               //.Replace("#What_Happened#", whatHappened)
                               //.Replace("#Recent_messages#", recent)
                               //.Replace("#Category#", catname)
                               //.Replace("#Status#", statname)
                               //.Replace("#Priority#", priority)
                               .ToString();

                    return(new EmailTemplate {
                        Body = body, Subject = subj
                    });
                }

                async Task NotifyTechs(EmailTemplate et)
                {
                    var qry = _userRepo.All(q => q.NotDisabled().Techs().Not(user.Id).CanReceiveEmails());

                    await ForUsersAsync(qry, et.Subject, et.Body, from, replyToAddrList, cancellationToken);
                }

                async Task NotifyAssignee(EmailTemplate et)
                {
                    if (issue.IsAssigned())
                    {
                        var owner = await _userRepo.GetAsync(q => q.Find(issue.AssignedToUserId));

                        if (owner != null)
                        {
                            EnqueueTemplate(et, owner);
                        }
                    }
                }

                void NotifyOwner(EmailTemplate et)
                {
                    var owner = issue.User;

                    if (owner.SendEmail && owner.Id != user.Id)
                    {
                        EnqueueTemplate(et, owner);
                    }
                }

                void SetFromName(AppUser u)
                {
                    if (!useFromNameForAll)
                    {
                        from = user.GetEmailAddress();
                    }
                }

                void EnqueueTemplate(EmailTemplate et, AppUser owner)
                {
                    SetFromName(owner);
                    Enqueue(WrapMessage(CreateMessage(et.Subject, et.Body, from, owner.Email), owner.Id));
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, $"Error while producing issue e-mails in {nameof(ProcessAsync)}.");
            }
        }