// // Copyright © 2020, 2021 Anticrm Platform Contributors. // Copyright © 2021, 2022 Hardcore Engineering Inc. // // Licensed under the Eclipse Public License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. You may // obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // // See the License for the specific language governing permissions and // limitations under the License. // import chunter, { Backlink } from '@anticrm/chunter' import contact, { Employee, EmployeeAccount, formatName } from '@anticrm/contact' import core, { AttachedDoc, Class, Data, Doc, generateId, Hierarchy, Obj, Ref, Space, Tx, TxCollectionCUD, TxCreateDoc, TxCUD, TxProcessor } from '@anticrm/core' import notification, { EmailNotification, Notification, NotificationStatus } from '@anticrm/notification' import { getResource } from '@anticrm/platform' import type { TriggerControl } from '@anticrm/server-core' import { getUpdateLastViewTx } from '@anticrm/server-notification' import view, { HTMLPresenter, TextPresenter } from '@anticrm/view' const extractTx = (tx: Tx): Tx => { if (tx._class === core.class.TxCollectionCUD) { const ctx = (tx as TxCollectionCUD) if (ctx.tx._class === core.class.TxCreateDoc) { const create = ctx.tx as TxCreateDoc create.attributes.attachedTo = ctx.objectId create.attributes.attachedToClass = ctx.objectClass create.attributes.collection = ctx.collection return create } return ctx } return tx } /** * @public */ export async function OnBacklinkCreate (tx: Tx, control: TriggerControl): Promise { const hierarchy = control.hierarchy if (tx._class !== core.class.TxCollectionCUD) { return [] } const ptx = tx as TxCollectionCUD if ( ptx.tx._class !== core.class.TxCreateDoc || !hierarchy.isDerived(ptx.tx.objectClass, chunter.class.Backlink) || !hierarchy.isDerived(ptx.objectClass, contact.class.Employee) ) { return [] } const result: Tx[] = [] const createNotificationTx = await getPlatformNotificationTx(ptx, control) if (createNotificationTx !== undefined) { result.push(createNotificationTx) } const emailTx = await getEmailTx(ptx, control) if (emailTx !== undefined) { result.push(emailTx) } return result } /** * @public */ export async function UpdateLastView (tx: Tx, control: TriggerControl): Promise { const actualTx = extractTx(tx) if (![core.class.TxUpdateDoc, core.class.TxCreateDoc, core.class.TxMixin].includes(actualTx._class)) { return [] } const result: Tx[] = [] switch (actualTx._class) { case core.class.TxCreateDoc: { const createTx = actualTx as TxCreateDoc if (control.hierarchy.isDerived(createTx.objectClass, core.class.AttachedDoc)) { const doc = TxProcessor.createDoc2Doc(createTx as TxCreateDoc) const attachedTx = await getUpdateLastViewTx(control.findAll, doc.attachedTo, doc.attachedToClass, createTx.modifiedOn, createTx.modifiedBy) if (attachedTx !== undefined) { result.push(attachedTx) } } else { const doc = TxProcessor.createDoc2Doc(createTx) const tx = await getUpdateLastViewTx(control.findAll, doc._id, doc._class, createTx.modifiedOn, createTx.modifiedBy) if (tx !== undefined) { result.push(tx) } } break } case core.class.TxUpdateDoc: case core.class.TxMixin: { const tx = actualTx as TxCUD const doc = (await control.findAll(tx.objectClass, { _id: tx.objectId }, { limit: 1 }))[0] if (control.hierarchy.isDerived(doc._class, core.class.AttachedDoc)) { const attachedDoc = doc as AttachedDoc const attachedTx = await getUpdateLastViewTx(control.findAll, attachedDoc.attachedTo, attachedDoc.attachedToClass, tx.modifiedOn, tx.modifiedBy) if (attachedTx !== undefined) { result.push(attachedTx) } } else { const resTx = await getUpdateLastViewTx(control.findAll, doc._id, doc._class, tx.modifiedOn, tx.modifiedBy) if (resTx !== undefined) { result.push(resTx) } } break } default: break } return result } async function getPlatformNotificationTx (ptx: TxCollectionCUD, control: TriggerControl): Promise | undefined> { const attached = (await control.modelDb.findAll(contact.class.EmployeeAccount, { employee: ptx.objectId as Ref }, { limit: 1 }))[0] if (attached === undefined) return const setting = (await control.findAll(notification.class.NotificationSetting, { provider: notification.ids.PlatformNotification, type: notification.ids.MentionNotification, space: attached._id as unknown as Ref }, { limit: 1 }))[0] if (setting === undefined) { const provider = (await control.modelDb.findAll(notification.class.NotificationProvider, { _id: notification.ids.PlatformNotification }))[0] if (provider === undefined) return if (!provider.default) return } const createTx: TxCreateDoc = { objectClass: notification.class.Notification, objectSpace: notification.space.Notifications, objectId: generateId(), modifiedOn: ptx.modifiedOn, modifiedBy: ptx.modifiedBy, space: ptx.space, _id: generateId(), _class: core.class.TxCreateDoc, attributes: { tx: ptx._id, status: NotificationStatus.New } as unknown as Data } const createNotificationTx: TxCollectionCUD = { ...ptx, _id: generateId(), collection: 'notifications', tx: createTx } return createNotificationTx } async function getEmailTx (ptx: TxCollectionCUD, control: TriggerControl): Promise | undefined> { const hierarchy = control.hierarchy const backlink = TxProcessor.createDoc2Doc(ptx.tx as TxCreateDoc) const account = (await control.modelDb.findAll(contact.class.EmployeeAccount, { _id: ptx.modifiedBy as Ref }, { limit: 1 }))[0] if (account === undefined) return undefined const sender = formatName(account.name) const attached = (await control.modelDb.findAll(contact.class.EmployeeAccount, { employee: ptx.objectId as Ref }, { limit: 1 }))[0] if (attached === undefined) return undefined const setting = (await control.findAll(notification.class.NotificationSetting, { provider: notification.ids.EmailNotification, type: notification.ids.MentionNotification, space: attached._id as unknown as Ref }, { limit: 1 }))[0] if (setting === undefined) { const provider = (await control.modelDb.findAll(notification.class.NotificationProvider, { _id: notification.ids.PlatformNotification }))[0] if (provider === undefined) return if (!provider.default) return } const receiver = attached.email const doc = (await control.findAll(backlink.backlinkClass, { _id: backlink.backlinkId }, { limit: 1 }))[0] if (doc === undefined) return undefined const TextPresenter = getTextPresenter(doc._class, hierarchy) if (TextPresenter === undefined) return const HTMLPresenter = getHTMLPresenter(doc._class, hierarchy) const htmlPart = HTMLPresenter !== undefined ? (await getResource(HTMLPresenter.presenter))(doc) : undefined const textPart = (await getResource(TextPresenter.presenter))(doc) const html = `

${sender} mentioned you in ${htmlPart !== undefined ? htmlPart : textPart}

${backlink.message}` const text = `${sender} mentioned you in ${textPart}` return { _id: generateId(), objectId: generateId(), _class: core.class.TxCreateDoc, space: core.space.DerivedTx, objectClass: notification.class.EmailNotification, objectSpace: notification.space.Notifications, modifiedOn: ptx.modifiedOn, modifiedBy: ptx.modifiedBy, attributes: { status: 'new', sender, receivers: [receiver], subject: `You was mentioned in ${textPart}`, text, html } } } function getHTMLPresenter (_class: Ref>, hierarchy: Hierarchy): HTMLPresenter | undefined { let clazz: Ref> | undefined = _class while (clazz !== undefined) { const _class = hierarchy.getClass(clazz) const presenter = hierarchy.as(_class, view.mixin.HTMLPresenter) if (presenter.presenter != null) return presenter clazz = _class.extends } } function getTextPresenter (_class: Ref>, hierarchy: Hierarchy): TextPresenter | undefined { let clazz: Ref> | undefined = _class while (clazz !== undefined) { const _class = hierarchy.getClass(clazz) const presenter = hierarchy.as(_class, view.mixin.TextPresenter) if (presenter.presenter != null) return presenter clazz = _class.extends } } // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export default async () => ({ trigger: { OnBacklinkCreate, UpdateLastView } })