mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-17 18:05:42 +02:00
Fix email notifications for export (#10506)
Signed-off-by: Artem Savchenko <armisav@gmail.com>
This commit is contained in:
Generated
+6
@@ -36578,6 +36578,12 @@ importers:
|
||||
'@hcengineering/drive-assets':
|
||||
specifier: workspace:^0.7.0
|
||||
version: link:../../plugins/drive-assets
|
||||
'@hcengineering/export':
|
||||
specifier: workspace:^0.7.0
|
||||
version: link:../../plugins/export
|
||||
'@hcengineering/export-assets':
|
||||
specifier: workspace:^0.7.0
|
||||
version: link:../../plugins/export-assets
|
||||
'@hcengineering/github':
|
||||
specifier: workspace:^0.7.0
|
||||
version: link:../../services/github/github
|
||||
|
||||
@@ -89,7 +89,12 @@ export function createModel (builder: Builder): void {
|
||||
group: exportPlugin.ids.ImportNotificationGroup,
|
||||
txClasses: [],
|
||||
objectClass: exportPlugin.class.ExportResultRecord,
|
||||
defaultEnabled: true
|
||||
defaultEnabled: true,
|
||||
templates: {
|
||||
textTemplate: '{body}',
|
||||
htmlTemplate: '<p>{body}</p><p>{link}</p>',
|
||||
subjectTemplate: '{title}'
|
||||
}
|
||||
},
|
||||
exportPlugin.ids.ImportedDocumentsNotification
|
||||
)
|
||||
|
||||
@@ -44,7 +44,10 @@ import serverNotification from '@hcengineering/server-notification'
|
||||
import {
|
||||
AvailableProvidersCache,
|
||||
AvailableProvidersCacheKey,
|
||||
getContentByTemplate
|
||||
getContentByTemplate,
|
||||
getNotificationProviderControl,
|
||||
getReceiversInfo,
|
||||
getAllowedProviders
|
||||
} from '@hcengineering/server-notification-resources'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import activity, { ActivityMessage } from '@hcengineering/activity'
|
||||
@@ -156,7 +159,7 @@ async function notifyByEmail (
|
||||
senderSocialId: PersonId,
|
||||
email: string,
|
||||
data: InboxNotification,
|
||||
message: ActivityMessage
|
||||
message?: ActivityMessage
|
||||
): Promise<void> {
|
||||
let senderName = sender !== undefined ? formatName(sender.name, control.branding?.lastNameFirst) : ''
|
||||
if (senderName === '' && senderSocialId === core.account.System) {
|
||||
@@ -166,6 +169,12 @@ async function notifyByEmail (
|
||||
|
||||
if (content !== undefined) {
|
||||
await sendEmailNotification(control.ctx, content.text, content.html, content.subject, email)
|
||||
} else {
|
||||
control.ctx.info('notifyByEmail: getContentByTemplate returned undefined, email not sent', {
|
||||
notificationId: data._id,
|
||||
type,
|
||||
docClass: doc._class
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +206,10 @@ async function processEmailNotifications (control: TriggerControl, notifications
|
||||
const docId = notifications[0].objectId
|
||||
const docClass = notifications[0].objectClass
|
||||
const doc = (await control.findAll(control.ctx, docClass, { _id: docId }))[0]
|
||||
if (doc === undefined) return
|
||||
if (doc === undefined) {
|
||||
control.ctx.info('processEmailNotifications: doc not found', { docId, docClass })
|
||||
return
|
||||
}
|
||||
const messages = await getNotificationMessages(notifications, control)
|
||||
const { hierarchy } = control
|
||||
|
||||
@@ -205,7 +217,10 @@ async function processEmailNotifications (control: TriggerControl, notifications
|
||||
|
||||
for (const n of notifications) {
|
||||
const type = (n.types ?? [])[0]
|
||||
if (type === undefined) continue
|
||||
if (type === undefined) {
|
||||
control.ctx.info('processEmailNotifications: skipping notification without type', { notificationId: n._id })
|
||||
continue
|
||||
}
|
||||
let message: ActivityMessage | undefined
|
||||
if (hierarchy.isDerived(n._class, notification.class.ActivityInboxNotification)) {
|
||||
const activityNotification = n as ActivityInboxNotification
|
||||
@@ -215,11 +230,22 @@ async function processEmailNotifications (control: TriggerControl, notifications
|
||||
if (hierarchy.isDerived(mentionNotification.mentionedInClass, activity.class.ActivityMessage)) {
|
||||
message = messages.find((m) => m._id === mentionNotification.mentionedIn)
|
||||
}
|
||||
} else if (hierarchy.isDerived(n._class, notification.class.CommonInboxNotification)) {
|
||||
message = undefined
|
||||
}
|
||||
|
||||
if (message === undefined) continue
|
||||
if (message === undefined && !hierarchy.isDerived(n._class, notification.class.CommonInboxNotification)) {
|
||||
control.ctx.info('processEmailNotifications: skipping - no ActivityMessage and not CommonInboxNotification', {
|
||||
notificationId: n._id,
|
||||
notificationClass: n._class
|
||||
})
|
||||
continue
|
||||
}
|
||||
const employee = await getEmployeeByAcc(control, n.user)
|
||||
if (employee === undefined) continue
|
||||
if (employee === undefined) {
|
||||
control.ctx.info('processEmailNotifications: no employee for user', { notificationId: n._id, user: n.user })
|
||||
continue
|
||||
}
|
||||
const emailQuery = {
|
||||
attachedTo: employee._id,
|
||||
type: { $in: [SocialIdType.EMAIL, SocialIdType.GOOGLE] },
|
||||
@@ -247,30 +273,85 @@ async function processEmailNotifications (control: TriggerControl, notifications
|
||||
continue
|
||||
}
|
||||
|
||||
const senderSocialId = message.createdBy ?? message.modifiedBy
|
||||
const senderSocialId = message !== undefined ? (message.createdBy ?? message.modifiedBy) : core.account.System
|
||||
const sender = senders.get(senderSocialId) ?? (await getPerson(control, senderSocialId))
|
||||
if (sender != null) {
|
||||
senders.set(senderSocialId, sender)
|
||||
}
|
||||
|
||||
control.ctx.info('processEmailNotifications: sending email', {
|
||||
notificationId: n._id,
|
||||
type,
|
||||
notificationClass: n._class
|
||||
})
|
||||
await notifyByEmail(control, type, doc, sender, senderSocialId, emails[0].value, n, message)
|
||||
}
|
||||
}
|
||||
|
||||
function hasEmailProvider (n: InboxNotification, availableProviders: AvailableProvidersCache): boolean {
|
||||
const providers = availableProviders.get(n._id) ?? availableProviders.get(n.objectId as Ref<InboxNotification>)
|
||||
return providers?.find((p) => p === gmail.providers.EmailNotificationProvider) !== undefined
|
||||
}
|
||||
|
||||
async function NotificationsHandler (txes: TxCreateDoc<InboxNotification>[], control: TriggerControl): Promise<Tx[]> {
|
||||
control.ctx.info('NotificationsHandler: received InboxNotification txes', {
|
||||
count: txes.length,
|
||||
workspace: control.workspace?.url,
|
||||
objectClasses: [...new Set(txes.map((tx) => tx.objectClass))]
|
||||
})
|
||||
|
||||
const availableProviders: AvailableProvidersCache = control.contextCache.get(AvailableProvidersCacheKey) ?? new Map()
|
||||
|
||||
const all: InboxNotification[] = txes
|
||||
.map((tx) => TxProcessor.createDoc2Doc(tx))
|
||||
.filter(
|
||||
(it) => availableProviders.get(it._id)?.find((p) => p === gmail.providers.EmailNotificationProvider) !== undefined
|
||||
)
|
||||
const all: InboxNotification[] = txes.map((tx) => TxProcessor.createDoc2Doc(tx))
|
||||
|
||||
if (all.length === 0) {
|
||||
const notificationsWithEmail = all.filter((it) => hasEmailProvider(it, availableProviders))
|
||||
|
||||
control.ctx.info('NotificationsHandler: processing inbox notifications', {
|
||||
total: all.length,
|
||||
withEmailFromCache: notificationsWithEmail.length,
|
||||
notificationClasses: [...new Set(all.map((n) => n._class))],
|
||||
notificationTypes: [...new Set(all.flatMap((n) => n.types ?? []))]
|
||||
})
|
||||
|
||||
if (notificationsWithEmail.length < all.length) {
|
||||
const notificationControl = await getNotificationProviderControl(control.ctx, control)
|
||||
const receivers = await getReceiversInfo(control.ctx, [...new Set(all.map((n) => n.user))], control)
|
||||
const receiverByAccount = new Map(receivers.map((r) => [r.account, r]))
|
||||
for (const n of all) {
|
||||
if (hasEmailProvider(n, availableProviders)) continue
|
||||
const type = (n.types ?? [])[0]
|
||||
if (type === undefined) {
|
||||
control.ctx.info('NotificationsHandler: skipping notification without type', { notificationId: n._id })
|
||||
continue
|
||||
}
|
||||
const notificationType = control.modelDb.getObject(type)
|
||||
const receiver = receiverByAccount.get(n.user)
|
||||
if (receiver === undefined) {
|
||||
control.ctx.info('NotificationsHandler: no receiver info for user', {
|
||||
notificationId: n._id,
|
||||
type,
|
||||
reason: 'user not in getReceiversInfo result'
|
||||
})
|
||||
continue
|
||||
}
|
||||
const allowedProviders = getAllowedProviders(control, receiver.socialIds, notificationType, notificationControl)
|
||||
if (allowedProviders.includes(gmail.providers.EmailNotificationProvider)) {
|
||||
notificationsWithEmail.push(n)
|
||||
} else {
|
||||
control.ctx.info('NotificationsHandler: email provider not enabled for notification type', {
|
||||
notificationId: n._id,
|
||||
type
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (notificationsWithEmail.length === 0) {
|
||||
control.ctx.info('NotificationsHandler: no notifications with email provider, skipping')
|
||||
return []
|
||||
}
|
||||
|
||||
const notificationsByDocId = groupByArray(all, (n) => n.objectId)
|
||||
const notificationsByDocId = groupByArray(notificationsWithEmail, (n) => n.objectId)
|
||||
|
||||
await Promise.all(
|
||||
Array.from(notificationsByDocId.entries()).map(([docId, notifications]) =>
|
||||
|
||||
@@ -183,13 +183,22 @@ export async function getContentByTemplate (
|
||||
const notificationType = control.modelDb.getObject(type)
|
||||
if (notificationType.templates === undefined) return
|
||||
|
||||
const textPart = await getTextPart(doc, control)
|
||||
if (textPart === undefined) return
|
||||
const params: Record<string, string> =
|
||||
notificationData !== undefined
|
||||
? await getTranslatedNotificationContent(notificationData, notificationData._class, control)
|
||||
: {}
|
||||
|
||||
let textPart = await getTextPart(doc, control)
|
||||
if (textPart === undefined) {
|
||||
if (
|
||||
notificationData !== undefined &&
|
||||
control.hierarchy.isDerived(notificationData._class, notification.class.CommonInboxNotification)
|
||||
) {
|
||||
textPart = params.title ?? params.body ?? ''
|
||||
}
|
||||
if (textPart === undefined || textPart === '') return
|
||||
}
|
||||
|
||||
if (
|
||||
notificationData !== undefined &&
|
||||
control.hierarchy.isDerived(notificationData._class, notification.class.MentionInboxNotification)
|
||||
|
||||
@@ -127,6 +127,8 @@
|
||||
"@hcengineering/hr-assets": "workspace:^0.7.0",
|
||||
"@hcengineering/request-assets": "workspace:^0.7.0",
|
||||
"@hcengineering/document-assets": "workspace:^0.7.0",
|
||||
"@hcengineering/export": "workspace:^0.7.0",
|
||||
"@hcengineering/export-assets": "workspace:^0.7.0",
|
||||
"@hcengineering/controlled-documents-assets": "workspace:^0.7.0",
|
||||
"@hcengineering/products-assets": "workspace:^0.7.0",
|
||||
"@hcengineering/training-assets": "workspace:^0.7.0",
|
||||
|
||||
@@ -8,6 +8,7 @@ import { chunterId } from '@hcengineering/chunter'
|
||||
import { contactId } from '@hcengineering/contact'
|
||||
import { documentsId } from '@hcengineering/controlled-documents'
|
||||
import { documentId } from '@hcengineering/document'
|
||||
import { exportId } from '@hcengineering/export'
|
||||
import { driveId } from '@hcengineering/drive'
|
||||
import { githubId } from '@hcengineering/github'
|
||||
import { gmailId } from '@hcengineering/gmail'
|
||||
@@ -50,6 +51,7 @@ import chunterEn from '@hcengineering/chunter-assets/lang/en.json'
|
||||
import contactEn from '@hcengineering/contact-assets/lang/en.json'
|
||||
import documentsEn from '@hcengineering/controlled-documents-assets/lang/en.json'
|
||||
import documentEn from '@hcengineering/document-assets/lang/en.json'
|
||||
import exportEn from '@hcengineering/export-assets/lang/en.json'
|
||||
import driveEn from '@hcengineering/drive-assets/lang/en.json'
|
||||
import githubEn from '@hcengineering/github-assets/lang/en.json'
|
||||
import gmailEn from '@hcengineering/gmail-assets/lang/en.json'
|
||||
@@ -109,6 +111,7 @@ export function registerStringLoaders (): void {
|
||||
addStringsLoader(preferenceId, async (lang: string) => preferenceEn)
|
||||
addStringsLoader(hrId, async (lang: string) => hrEn)
|
||||
addStringsLoader(documentId, async (lang: string) => documentEn)
|
||||
addStringsLoader(exportId, async (lang: string) => exportEn)
|
||||
addStringsLoader(requestId, async (lang: string) => requestEn)
|
||||
addStringsLoader(loveId, async (lang: string) => loveEn)
|
||||
addStringsLoader(driveId, async (lang: string) => driveEn)
|
||||
|
||||
@@ -85,6 +85,7 @@ export async function sendExportCompletionNotification (
|
||||
objectId: resultId,
|
||||
objectClass: exportPlugin.class.ExportResultRecord,
|
||||
icon: exportPlugin.icon.Export,
|
||||
header: exportPlugin.string.ImportCompleted,
|
||||
message: exportPlugin.string.ImportToWorkspaceNotificationMessage,
|
||||
props: {
|
||||
count,
|
||||
|
||||
Reference in New Issue
Block a user