diff --git a/server/AyaNova/generator/CoreJobNotify.cs b/server/AyaNova/generator/CoreJobNotify.cs index d94c86dc..2d5c8be6 100644 --- a/server/AyaNova/generator/CoreJobNotify.cs +++ b/server/AyaNova/generator/CoreJobNotify.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; @@ -37,6 +38,7 @@ namespace AyaNova.Biz //temporary list to hold translations as required during delivery private static Dictionary> _transCache = new Dictionary>(); + private static Dictionary _UserTranslationIdCache = new Dictionary(); //////////////////////////////////////////////////////////////////////////////////////////////// // DoSweep @@ -100,10 +102,22 @@ namespace AyaNova.Biz //todo: create message here if not already set? //todo: generate and attach report here? + //All items have an event date, for non time delayed events it's just the moment it was created //which will predate this moment now if it's pre-existing var events = await ct.NotifyEvent.Include(z => z.NotifySubscription).ToListAsync(); log.LogTrace($"Found {events.Count} NotifyEvents to examine for potential delivery"); + + //cache translations + //Get all subscription unique userId's that aren't inapp deliveries + var usersNeedingTranslations = events.Where(z => z.NotifySubscription.DeliveryMethod != NotifyDeliveryMethod.App).Select(z => z.NotifySubscription.UserId).ToList().Distinct(); + foreach (long userid in usersNeedingTranslations) + { + long transId = (await ct.UserOptions.SingleAsync(z => z.UserId == userid)).TranslationId; + _UserTranslationIdCache.Add(userid, transId); + await CacheNotifyEventTypeTranslations(transId); + } + //iterate and deliver foreach (var notifyevent in events) { @@ -149,18 +163,29 @@ namespace AyaNova.Biz finally { log.LogTrace("Notify is done setting to not running state and tagging lastRun timestamp"); + _UserTranslationIdCache.Clear(); + _transCache.Clear(); lastRun = DateTime.UtcNow; NotifyIsRunning = false; + } } - //cache any translations required for email notification - private static async Task CacheNotifyEventTypeTranslations(long translationId){ - if(_transCache.ContainsKey(translationId)){ + //cache any translations required for email notification + private static async Task CacheNotifyEventTypeTranslations(long translationId) + { + if (_transCache.ContainsKey(translationId)) + { return; } - _transCache.Add(translationId,AyaNova.Api.Controllers.EnumListController.GetEnumList("NotifyEventType",translationId)); + _transCache.Add(translationId, await AyaNova.Api.Controllers.EnumListController.GetEnumList("NotifyEventType", translationId)); + } + + //Used for subject of email and message deliveries + private static string GetTranslatedNotifyEventName(NotifyEventType net, long translationId) + { + return _transCache[translationId][(int)net].Name; } private static async Task DeliverInApp(NotifyEvent ne, AyContext ct) @@ -171,29 +196,44 @@ namespace AyaNova.Biz await ct.SaveChangesAsync(); } - private static async Task DeliverSMTP(NotifyEvent ne, string toAddress, AyContext ct) + + + private static async Task DeliverSMTP(NotifyEvent ne, AyContext ct) { log.LogTrace($"DeliverSMTP deliving notify event: {ne}"); + if (string.IsNullOrWhiteSpace(ne.NotifySubscription.DeliveryAddress)) + { + await NotifyEventProcessor.AddGeneralNotifyEvent(NotifyEventType.DefaultNotification, $"Error: no email address is set in subscription to deliver email notification for this event:{ne}", null, ne.UserId); - var subject = $"Notification: {ne.EventType.ToString()}"; + } + + var transid = _UserTranslationIdCache[ne.NotifySubscription.UserId]; + var subject = $"AY:{GetTranslatedNotifyEventName(ne.EventType, transid)}:{ne.Name}"; IMailer m = AyaNova.Util.ServiceProviderProvider.Mailer; try { - await m.SendEmailAsync(toAddress, subject, "This is a test to confirm notification system is working", ServerGlobalOpsSettingsCache.Notify); - return "ok"; + var body = ""; + if (ne.ObjectId != 0) + { + body = OpenObjectUrlBuilder(ne.AyaType, ne.ObjectId) + "\n"; + } + body += ne.Message; + + await m.SendEmailAsync(ne.NotifySubscription.DeliveryAddress, subject, body, ServerGlobalOpsSettingsCache.Notify); + } catch (Exception ex) { - return ExceptionUtil.ExtractAllExceptionMessages(ex); + await NotifyEventProcessor.AddOpsProblemEvent("SMTP Notification failed", ex); + await NotifyEventProcessor.AddGeneralNotifyEvent(NotifyEventType.DefaultNotification, $"Error: an error prevented delivering the following notification via email. System operator users have been notified:{ne}", null, ne.UserId); + } + finally + { + //remove event no matter what + ct.NotifyEvent.Remove(ne); + await ct.SaveChangesAsync(); } - - //todo: //Open question: what to do with failed deliveries? - //we dont' want them piling up but we don't want to just dump them do we? - //it should be only mail ones that fail, not app ones, there's no way for an app delivery to fail as it's just put in a table - //### PLAN if it's an smtp delivery that fails and it's to someone who can be delivered in app then it should send an inapp notification of - //delivery failure and still delete the smtp delivery - //If it's not possible to notify the person via in app of the failed smtp then perhaps it notifies OPS personnel and biz admin personnel } //Called from ops notification settings to test smtp setup by delivering to address of choosing @@ -218,6 +258,20 @@ namespace AyaNova.Biz } + //Open object url + //### NOTE: If this is required anywhere else, move it to a central BizUtils class in Biz folder callable from here and there + private static string OpenObjectUrlBuilder(AyaType otype, long id) + { + var ServerUrl = ServerGlobalOpsSettingsCache.Notify.AyaNovaServerURL; + if (string.IsNullOrWhiteSpace(ServerUrl)) + { + NotifyEventProcessor.AddOpsProblemEvent("Notification system: The OPS Notification setting is empty for AyaNova Server URL. This prevents Notification system from linking events to openable objects.").Wait(); + return "OPS ERROR NO SERVER URL CONFIGURED"; + } + ServerUrl = ServerUrl.Trim().TrimEnd('/'); + return $"{ServerUrl}?type={otype}&id={id}"; + } + ///////////////////////////////////////////////////////////////////// }//eoc diff --git a/server/AyaNova/models/GlobalOpsNotificationSettings.cs b/server/AyaNova/models/GlobalOpsNotificationSettings.cs index 1a79562e..84d95051 100644 --- a/server/AyaNova/models/GlobalOpsNotificationSettings.cs +++ b/server/AyaNova/models/GlobalOpsNotificationSettings.cs @@ -14,6 +14,7 @@ namespace AyaNova.Models public NotifyMailSecurity ConnectionSecurity { get; set; } public int SmtpServerPort { get; set; } public string NotifyFromAddress { get; set; } + public string AyaNovaServerURL { get; set; } public GlobalOpsNotificationSettings() { @@ -27,6 +28,7 @@ namespace AyaNova.Models ConnectionSecurity = NotifyMailSecurity.StartTls; SmtpServerPort = 465; NotifyFromAddress = "support@ayanova.com"; + AyaNovaServerURL="http://localhost:8080"; #else SmtpServerAddress="mail.example.com"; SmtpAccount="notifydeliverfromaccount@example.com"; @@ -34,6 +36,7 @@ namespace AyaNova.Models ConnectionSecurity= NotifyMailSecurity.SSLTLS; SmtpServerPort=587; NotifyFromAddress="noreply@example.com"; + AyaNovaServerURL="https://ayanovaserver.example.com"; #endif } diff --git a/server/AyaNova/resource/de.json b/server/AyaNova/resource/de.json index e934a383..a9957833 100644 --- a/server/AyaNova/resource/de.json +++ b/server/AyaNova/resource/de.json @@ -1901,5 +1901,6 @@ "SmtpPassword": "SMTP-Serverkennwort", "ConnectionSecurity": "SMTP-Verbindungssicherheit", "SmtpServerPort": "SMTP-Server-Port", - "NotifyFromAddress": "SMTP-Benachrichtigungsadresse" + "NotifyFromAddress": "SMTP-Benachrichtigungsadresse", + "AyaNovaServerUrl": "AyaNova Server URL" } \ No newline at end of file diff --git a/server/AyaNova/resource/en.json b/server/AyaNova/resource/en.json index 631b6d1b..ec1e2222 100644 --- a/server/AyaNova/resource/en.json +++ b/server/AyaNova/resource/en.json @@ -1901,5 +1901,6 @@ "SmtpPassword": "SMTP server password", "ConnectionSecurity": "SMTP connection security", "SmtpServerPort": "SMTP server port", - "NotifyFromAddress": "SMTP notify from address" + "NotifyFromAddress": "SMTP notify from address", + "AyaNovaServerUrl": "AyaNova server URL" } \ No newline at end of file diff --git a/server/AyaNova/resource/es.json b/server/AyaNova/resource/es.json index 52727a56..a856577b 100644 --- a/server/AyaNova/resource/es.json +++ b/server/AyaNova/resource/es.json @@ -1901,5 +1901,6 @@ "SmtpPassword": "Contraseña SMTP", "ConnectionSecurity": "Seguridad de conexión del servidor SMTP", "SmtpServerPort": "Puerto SMTP", - "NotifyFromAddress": "Notificación SMTP desde la dirección" + "NotifyFromAddress": "Notificación SMTP desde la dirección", + "AyaNovaServerUrl": "URL del servidor AyaNova" } \ No newline at end of file diff --git a/server/AyaNova/resource/fr.json b/server/AyaNova/resource/fr.json index d8c019da..2454610b 100644 --- a/server/AyaNova/resource/fr.json +++ b/server/AyaNova/resource/fr.json @@ -1901,5 +1901,6 @@ "SmtpPassword": "Mot de passe du serveur SMTP", "ConnectionSecurity": "Sécurité de la connexion SMTP", "SmtpServerPort": "Port du serveur SMTP", - "NotifyFromAddress": "Notification SMTP envoyée depuis l'adresse" + "NotifyFromAddress": "Notification SMTP envoyée depuis l'adresse", + "AyaNovaServerUrl": "URL du serveur AyaNova" } \ No newline at end of file diff --git a/server/AyaNova/util/AySchema.cs b/server/AyaNova/util/AySchema.cs index d062b925..8578de00 100644 --- a/server/AyaNova/util/AySchema.cs +++ b/server/AyaNova/util/AySchema.cs @@ -22,7 +22,7 @@ namespace AyaNova.Util //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImporting WHEN NEW TABLES ADDED!!!! private const int DESIRED_SCHEMA_LEVEL = 12; - internal const long EXPECTED_COLUMN_COUNT = 382; + internal const long EXPECTED_COLUMN_COUNT = 383; internal const long EXPECTED_INDEX_COUNT = 139; //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImporting WHEN NEW TABLES ADDED!!!! @@ -237,7 +237,7 @@ namespace AyaNova.Util "backuptime timestamp, backupsetstokeep int, backupattachments bool)"); await ExecQueryAsync("CREATE TABLE aglobalopsnotificationsettings (id integer NOT NULL PRIMARY KEY, smtpdeliveryactive bool not null, " + - "smtpserveraddress text, smtpaccount text, smtppassword text, connectionsecurity integer not null default 0, smtpserverport integer, notifyfromaddress text)"); + "smtpserveraddress text, smtpaccount text, smtppassword text, connectionsecurity integer not null default 0, smtpserverport integer, notifyfromaddress text, ayanovaserverurl text)"); //create aevent biz event log table await ExecQueryAsync("CREATE TABLE aevent (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, created timestamp not null, userid bigint not null," +