mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-11 12:17:44 +02:00
Refactor server side notifications (#2327)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
@@ -38,16 +38,16 @@ import { workbenchId } from '@hcengineering/workbench'
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function channelHTMLPresenter (doc: Doc): string {
|
||||
export async function channelHTMLPresenter (doc: Doc, control: TriggerControl): Promise<string> {
|
||||
const channel = doc as ChunterSpace
|
||||
const front = getMetadata(login.metadata.FrontUrl) ?? ''
|
||||
return `<a href="${front}/${workbenchId}/${chunterId}/${channel._id}">${channel.name}</a>`
|
||||
return `<a href="${front}/${workbenchId}/${control.workspace}/${chunterId}/${channel._id}">${channel.name}</a>`
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function channelTextPresenter (doc: Doc): string {
|
||||
export async function channelTextPresenter (doc: Doc): Promise<string> {
|
||||
const channel = doc as ChunterSpace
|
||||
return `${channel.name}`
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/server-notification": "^0.6.0",
|
||||
"@hcengineering/core": "^0.6.17",
|
||||
"@hcengineering/platform": "^0.6.7",
|
||||
"@hcengineering/server-core": "~0.6.1"
|
||||
|
||||
@@ -17,6 +17,7 @@ import { Class, Doc, DocumentQuery, FindOptions, FindResult, Hierarchy, Ref } fr
|
||||
import type { Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import { TriggerFunc } from '@hcengineering/server-core'
|
||||
import { Presenter } from '@hcengineering/server-notification'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -42,7 +43,7 @@ export default plugin(serverChunterId, {
|
||||
) => Promise<FindResult<T>>
|
||||
) => Promise<Doc[]>
|
||||
>,
|
||||
ChannelHTMLPresenter: '' as Resource<(doc: Doc) => string>,
|
||||
ChannelTextPresenter: '' as Resource<(doc: Doc) => string>
|
||||
ChannelHTMLPresenter: '' as Resource<Presenter>,
|
||||
ChannelTextPresenter: '' as Resource<Presenter>
|
||||
}
|
||||
})
|
||||
|
||||
@@ -67,10 +67,10 @@ export async function OnContactDelete (tx: Tx, { findAll, hierarchy, storageFx }
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function personHTMLPresenter (doc: Doc): string {
|
||||
export function personHTMLPresenter (doc: Doc, control: TriggerControl): string {
|
||||
const person = doc as Person
|
||||
const front = getMetadata(login.metadata.FrontUrl) ?? ''
|
||||
return `<a href="${front}/${workbenchId}/${contactId}#${view.component.EditDoc}|${person._id}|${
|
||||
return `<a href="${front}/${workbenchId}/${control.workspace}/${contactId}#${view.component.EditDoc}|${person._id}|${
|
||||
person._class
|
||||
}">${formatName(person.name)}</a>`
|
||||
}
|
||||
@@ -86,10 +86,10 @@ export function personTextPresenter (doc: Doc): string {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function organizationHTMLPresenter (doc: Doc): string {
|
||||
export function organizationHTMLPresenter (doc: Doc, control: TriggerControl): string {
|
||||
const organization = doc as Organization
|
||||
const front = getMetadata(login.metadata.FrontUrl) ?? ''
|
||||
return `<a href="${front}/${workbenchId}/${contactId}#${view.component.EditDoc}|${organization._id}|${organization._class}">${organization.name}</a>`
|
||||
return `<a href="${front}/${workbenchId}/${control.workspace}/${contactId}#${view.component.EditDoc}|${organization._id}|${organization._class}">${organization.name}</a>`
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/core": "^0.6.17",
|
||||
"@hcengineering/server-notification": "^0.6.0",
|
||||
"@hcengineering/platform": "^0.6.7",
|
||||
"@hcengineering/server-core": "~0.6.1"
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import type { Resource, Plugin } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import type { TriggerFunc } from '@hcengineering/server-core'
|
||||
import { Doc } from '@hcengineering/core'
|
||||
import { Presenter } from '@hcengineering/server-notification'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -32,9 +32,9 @@ export default plugin(serverContactId, {
|
||||
OnContactDelete: '' as Resource<TriggerFunc>
|
||||
},
|
||||
function: {
|
||||
PersonHTMLPresenter: '' as Resource<(doc: Doc) => string>,
|
||||
PersonTextPresenter: '' as Resource<(doc: Doc) => string>,
|
||||
OrganizationHTMLPresenter: '' as Resource<(doc: Doc) => string>,
|
||||
OrganizationTextPresenter: '' as Resource<(doc: Doc) => string>
|
||||
PersonHTMLPresenter: '' as Resource<Presenter>,
|
||||
PersonTextPresenter: '' as Resource<Presenter>,
|
||||
OrganizationHTMLPresenter: '' as Resource<Presenter>,
|
||||
OrganizationTextPresenter: '' as Resource<Presenter>
|
||||
}
|
||||
})
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"@hcengineering/core": "^0.6.17",
|
||||
"@hcengineering/platform": "^0.6.7",
|
||||
"@hcengineering/server-core": "~0.6.1",
|
||||
"@hcengineering/server-notification": "~0.6.0",
|
||||
"@hcengineering/server-notification": "^0.6.0",
|
||||
"@hcengineering/inventory": "~0.6.0",
|
||||
"@hcengineering/view": "^0.6.1",
|
||||
"@hcengineering/login": "~0.6.1",
|
||||
|
||||
@@ -17,22 +17,23 @@ import { Doc } from '@hcengineering/core'
|
||||
import { inventoryId, Product } from '@hcengineering/inventory'
|
||||
import login from '@hcengineering/login'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import { TriggerControl } from '@hcengineering/server-core'
|
||||
import view from '@hcengineering/view'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function productHTMLPresenter (doc: Doc): string {
|
||||
export async function productHTMLPresenter (doc: Doc, control: TriggerControl): Promise<string> {
|
||||
const product = doc as Product
|
||||
const front = getMetadata(login.metadata.FrontUrl) ?? ''
|
||||
return `<a href="${front}/${workbenchId}/${inventoryId}/Products/#${view.component.EditDoc}|${product._id}|${product._class}">${product.name}</a>`
|
||||
return `<a href="${front}/${workbenchId}/${control.workspace}/${inventoryId}/Products/#${view.component.EditDoc}|${product._id}|${product._class}">${product.name}</a>`
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function productTextPresenter (doc: Doc): string {
|
||||
export async function productTextPresenter (doc: Doc): Promise<string> {
|
||||
const product = doc as Product
|
||||
return `${product.name}`
|
||||
}
|
||||
|
||||
@@ -27,8 +27,7 @@
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/core": "^0.6.17",
|
||||
"@hcengineering/platform": "^0.6.7",
|
||||
"@hcengineering/server-core": "~0.6.1"
|
||||
"@hcengineering/server-notification": "^0.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Doc } from '@hcengineering/core'
|
||||
import type { Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import { Presenter } from '@hcengineering/server-notification'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -27,7 +27,7 @@ export const serverInventoryId = 'server-inventory' as Plugin
|
||||
*/
|
||||
export default plugin(serverInventoryId, {
|
||||
function: {
|
||||
ProductHTMLPresenter: '' as Resource<(doc: Doc) => string>,
|
||||
ProductTextPresenter: '' as Resource<(doc: Doc) => string>
|
||||
ProductHTMLPresenter: '' as Resource<Presenter>,
|
||||
ProductTextPresenter: '' as Resource<Presenter>
|
||||
}
|
||||
})
|
||||
|
||||
@@ -34,16 +34,16 @@ import { addAssigneeNotification } from '@hcengineering/server-task-resources'
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function leadHTMLPresenter (doc: Doc): string {
|
||||
export async function leadHTMLPresenter (doc: Doc, control: TriggerControl): Promise<string> {
|
||||
const lead = doc as Lead
|
||||
const front = getMetadata(login.metadata.FrontUrl) ?? ''
|
||||
return `<a href="${front}/${workbenchId}/${leadId}/${lead.space}/#${view.component.EditDoc}|${lead._id}|${lead._class}">${lead.title}</a>`
|
||||
return `<a href="${front}/${workbenchId}/${control.workspace}/${leadId}/${lead.space}/#${view.component.EditDoc}|${lead._id}|${lead._class}">${lead.title}</a>`
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function leadTextPresenter (doc: Doc): string {
|
||||
export async function leadTextPresenter (doc: Doc): Promise<string> {
|
||||
const lead = doc as Lead
|
||||
return `LEAD-${lead.number}`
|
||||
}
|
||||
@@ -77,7 +77,6 @@ async function handleLeadCreate (control: TriggerControl, cud: TxCUD<Doc>, res:
|
||||
control,
|
||||
res,
|
||||
leadValue,
|
||||
leadTextPresenter(leadValue),
|
||||
leadValue.assignee,
|
||||
tx as TxCollectionCUD<Lead, AttachedDoc>
|
||||
)
|
||||
@@ -96,7 +95,6 @@ async function handleLeadUpdate (control: TriggerControl, cud: TxCUD<Doc>, res:
|
||||
control,
|
||||
res,
|
||||
leadValue,
|
||||
leadTextPresenter(leadValue),
|
||||
leadValue.assignee,
|
||||
tx as TxCollectionCUD<Lead, AttachedDoc>
|
||||
)
|
||||
|
||||
@@ -27,10 +27,8 @@
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/core": "^0.6.17",
|
||||
"@hcengineering/platform": "^0.6.7",
|
||||
"@hcengineering/server-core": "~0.6.1",
|
||||
"@hcengineering/contact": "~0.6.5",
|
||||
"@hcengineering/server-task-resources": "~0.6.0"
|
||||
"@hcengineering/server-notification": "^0.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Doc } from '@hcengineering/core'
|
||||
import type { Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import { TriggerFunc } from '@hcengineering/server-core'
|
||||
import { Presenter } from '@hcengineering/server-notification'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -28,8 +28,8 @@ export const serverLeadId = 'server-lead' as Plugin
|
||||
*/
|
||||
export default plugin(serverLeadId, {
|
||||
function: {
|
||||
LeadHTMLPresenter: '' as Resource<(doc: Doc) => string>,
|
||||
LeadTextPresenter: '' as Resource<(doc: Doc) => string>
|
||||
LeadHTMLPresenter: '' as Resource<Presenter>,
|
||||
LeadTextPresenter: '' as Resource<Presenter>
|
||||
},
|
||||
trigger: {
|
||||
OnLeadUpdate: '' as Resource<TriggerFunc>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"@hcengineering/core": "^0.6.17",
|
||||
"@hcengineering/platform": "^0.6.7",
|
||||
"@hcengineering/server-core": "~0.6.1",
|
||||
"@hcengineering/server-notification": "~0.6.0",
|
||||
"@hcengineering/server-notification": "^0.6.0",
|
||||
"@hcengineering/notification": "~0.6.0",
|
||||
"@hcengineering/chunter": "~0.6.1",
|
||||
"@hcengineering/view": "^0.6.1",
|
||||
|
||||
@@ -36,14 +36,24 @@ import core, {
|
||||
} from '@hcengineering/core'
|
||||
import notification, {
|
||||
EmailNotification,
|
||||
NotificationAction,
|
||||
Notification,
|
||||
NotificationProvider,
|
||||
NotificationStatus
|
||||
NotificationStatus,
|
||||
NotificationType
|
||||
} from '@hcengineering/notification'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import type { TriggerControl } from '@hcengineering/server-core'
|
||||
import { createLastViewTx, getUpdateLastViewTx } from '@hcengineering/server-notification'
|
||||
import view, { HTMLPresenter, TextPresenter } from '@hcengineering/view'
|
||||
import serverNotification, {
|
||||
HTMLPresenter,
|
||||
TextPresenter,
|
||||
createLastViewTx,
|
||||
getEmployeeAccount,
|
||||
getEmployeeAccountById,
|
||||
getUpdateLastViewTx
|
||||
} from '@hcengineering/server-notification'
|
||||
import { replaceAll } from './utils'
|
||||
import { Content } from './types'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -54,33 +64,21 @@ export async function OnBacklinkCreate (tx: Tx, control: TriggerControl): Promis
|
||||
|
||||
if (!checkTx(ptx, hierarchy)) return []
|
||||
|
||||
const result: Tx[] = []
|
||||
|
||||
const receiver = await getReceiver(ptx, control)
|
||||
const receiver = await getEmployeeAccount(ptx.objectId as Ref<Employee>, control)
|
||||
if (receiver === undefined) return []
|
||||
const sender = await getSender(ptx, control)
|
||||
const sender = await getEmployeeAccountById(ptx.modifiedBy, control)
|
||||
if (sender === undefined) return []
|
||||
const backlink = getBacklink(ptx)
|
||||
const doc = await getBacklinkDoc(backlink, control)
|
||||
const textPart = doc !== undefined ? await getTextPart(doc, hierarchy) : undefined
|
||||
const htmlPart = doc !== undefined ? await getHtmlPart(doc, hierarchy) : undefined
|
||||
|
||||
const createNotificationTx = await getPlatformNotificationTx(ptx, backlink, textPart, sender)
|
||||
|
||||
if (createNotificationTx !== undefined) {
|
||||
result.push(createNotificationTx)
|
||||
}
|
||||
|
||||
if (
|
||||
sender !== undefined &&
|
||||
textPart !== undefined &&
|
||||
(await isAllowed(control, receiver, notification.ids.EmailNotification))
|
||||
) {
|
||||
const emailTx = await getEmailTx(ptx, backlink, sender, textPart, htmlPart, receiver)
|
||||
if (emailTx !== undefined) {
|
||||
result.push(emailTx)
|
||||
}
|
||||
}
|
||||
return result
|
||||
return await createNotificationTxes(
|
||||
control,
|
||||
ptx,
|
||||
notification.ids.MentionNotification,
|
||||
doc,
|
||||
sender,
|
||||
receiver,
|
||||
backlink.message
|
||||
)
|
||||
}
|
||||
|
||||
function checkTx (ptx: TxCollectionCUD<Doc, Backlink>, hierarchy: Hierarchy): boolean {
|
||||
@@ -98,6 +96,202 @@ function checkTx (ptx: TxCollectionCUD<Doc, Backlink>, hierarchy: Hierarchy): bo
|
||||
return true
|
||||
}
|
||||
|
||||
async function isAllowed (
|
||||
control: TriggerControl,
|
||||
receiver: EmployeeAccount,
|
||||
providerId: Ref<NotificationProvider>
|
||||
): Promise<boolean> {
|
||||
const setting = (
|
||||
await control.findAll(
|
||||
notification.class.NotificationSetting,
|
||||
{
|
||||
provider: providerId,
|
||||
type: notification.ids.MentionNotification,
|
||||
space: receiver._id as unknown as Ref<Space>
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
if (setting !== undefined) {
|
||||
return setting.enabled
|
||||
}
|
||||
const provider = (
|
||||
await control.modelDb.findAll(notification.class.NotificationProvider, {
|
||||
_id: providerId
|
||||
})
|
||||
)[0]
|
||||
if (provider === undefined) return false
|
||||
return provider.default
|
||||
}
|
||||
|
||||
async function getTextPart (doc: Doc, control: TriggerControl): Promise<string | undefined> {
|
||||
const TextPresenter = getTextPresenter(doc._class, control.hierarchy)
|
||||
if (TextPresenter === undefined) return
|
||||
return await (
|
||||
await getResource(TextPresenter.presenter)
|
||||
)(doc, control)
|
||||
}
|
||||
|
||||
async function getHtmlPart (doc: Doc, control: TriggerControl): Promise<string | undefined> {
|
||||
const HTMLPresenter = getHTMLPresenter(doc._class, control.hierarchy)
|
||||
const htmlPart =
|
||||
HTMLPresenter !== undefined ? await (await getResource(HTMLPresenter.presenter))(doc, control) : undefined
|
||||
return htmlPart
|
||||
}
|
||||
|
||||
function getHTMLPresenter (_class: Ref<Class<Doc>>, hierarchy: Hierarchy): HTMLPresenter | undefined {
|
||||
let clazz: Ref<Class<Obj>> | undefined = _class
|
||||
while (clazz !== undefined) {
|
||||
const _class = hierarchy.getClass(clazz)
|
||||
const presenter = hierarchy.as(_class, serverNotification.mixin.HTMLPresenter)
|
||||
if (presenter.presenter != null) return presenter
|
||||
clazz = _class.extends
|
||||
}
|
||||
}
|
||||
|
||||
function getTextPresenter (_class: Ref<Class<Doc>>, hierarchy: Hierarchy): TextPresenter | undefined {
|
||||
let clazz: Ref<Class<Obj>> | undefined = _class
|
||||
while (clazz !== undefined) {
|
||||
const _class = hierarchy.getClass(clazz)
|
||||
const presenter = hierarchy.as(_class, serverNotification.mixin.TextPresenter)
|
||||
if (presenter.presenter != null) return presenter
|
||||
clazz = _class.extends
|
||||
}
|
||||
}
|
||||
|
||||
function fillTemplate (template: string, sender: string, doc: string, data: string): string {
|
||||
let res = replaceAll(template, '{sender}', sender)
|
||||
res = replaceAll(res, '{doc}', doc)
|
||||
res = replaceAll(res, '{data}', data)
|
||||
return res
|
||||
}
|
||||
|
||||
async function getContent (
|
||||
doc: Doc | undefined,
|
||||
sender: string,
|
||||
type: Ref<NotificationType>,
|
||||
control: TriggerControl,
|
||||
data: string
|
||||
): Promise<Content | undefined> {
|
||||
if (doc === undefined) return
|
||||
const notificationType = control.modelDb.getObject(type)
|
||||
|
||||
const textPart = await getTextPart(doc, control)
|
||||
if (textPart === undefined) return
|
||||
const text = fillTemplate(notificationType.textTemplate, sender, textPart, data)
|
||||
const htmlPart = await getHtmlPart(doc, control)
|
||||
const html = fillTemplate(notificationType.htmlTemplate, sender, htmlPart ?? textPart, data)
|
||||
const subject = fillTemplate(notificationType.subjectTemplate, sender, textPart, data)
|
||||
return {
|
||||
text,
|
||||
html,
|
||||
subject
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function createNotificationTxes (
|
||||
control: TriggerControl,
|
||||
ptx: TxCollectionCUD<Doc, AttachedDoc>,
|
||||
type: Ref<NotificationType>,
|
||||
doc: Doc | undefined,
|
||||
sender: EmployeeAccount,
|
||||
receiver: EmployeeAccount,
|
||||
data: string = '',
|
||||
action?: NotificationAction
|
||||
): Promise<Tx[]> {
|
||||
const res: Tx[] = []
|
||||
|
||||
const senderName = formatName(sender.name)
|
||||
|
||||
const content = await getContent(doc, senderName, type, control, data)
|
||||
|
||||
if (await isAllowed(control, receiver, notification.ids.PlatformNotification)) {
|
||||
const createNotificationTx = await getPlatformNotificationTx(ptx, type, content?.text, action)
|
||||
|
||||
res.push(createNotificationTx)
|
||||
}
|
||||
|
||||
if (content !== undefined && (await isAllowed(control, receiver, notification.ids.EmailNotification))) {
|
||||
const emailTx = await getEmailNotificationTx(ptx, senderName, content.text, content.html, content.subject, receiver)
|
||||
if (emailTx !== undefined) {
|
||||
res.push(emailTx)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
async function getPlatformNotificationTx (
|
||||
ptx: TxCollectionCUD<Doc, AttachedDoc>,
|
||||
type: Ref<NotificationType>,
|
||||
text?: string,
|
||||
action?: NotificationAction
|
||||
): Promise<TxCollectionCUD<Doc, Notification>> {
|
||||
const createTx: TxCreateDoc<Notification> = {
|
||||
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,
|
||||
type
|
||||
} as unknown as Data<Notification>
|
||||
}
|
||||
|
||||
if (text !== undefined) {
|
||||
createTx.attributes.text = text
|
||||
}
|
||||
|
||||
if (action !== undefined) {
|
||||
createTx.attributes.action = action
|
||||
}
|
||||
|
||||
const createNotificationTx: TxCollectionCUD<Doc, Notification> = {
|
||||
...ptx,
|
||||
_id: generateId(),
|
||||
collection: 'notifications',
|
||||
tx: createTx
|
||||
}
|
||||
|
||||
return createNotificationTx
|
||||
}
|
||||
|
||||
async function getEmailNotificationTx (
|
||||
ptx: TxCollectionCUD<Doc, AttachedDoc>,
|
||||
sender: string,
|
||||
text: string,
|
||||
html: string,
|
||||
subject: string,
|
||||
receiver: EmployeeAccount
|
||||
): Promise<TxCreateDoc<EmailNotification> | undefined> {
|
||||
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.email],
|
||||
subject,
|
||||
text,
|
||||
html
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getUpdateLastViewTxes (
|
||||
doc: Doc,
|
||||
_id: Ref<Doc>,
|
||||
@@ -197,86 +391,6 @@ export async function UpdateLastView (tx: Tx, control: TriggerControl): Promise<
|
||||
return result
|
||||
}
|
||||
|
||||
async function getReceiver (
|
||||
ptx: TxCollectionCUD<Doc, Backlink>,
|
||||
control: TriggerControl
|
||||
): Promise<EmployeeAccount | undefined> {
|
||||
return (
|
||||
await control.modelDb.findAll(
|
||||
contact.class.EmployeeAccount,
|
||||
{
|
||||
employee: ptx.objectId as Ref<Employee>
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
}
|
||||
|
||||
async function isAllowed (
|
||||
control: TriggerControl,
|
||||
receiver: EmployeeAccount,
|
||||
providerId: Ref<NotificationProvider>
|
||||
): Promise<boolean> {
|
||||
const setting = (
|
||||
await control.findAll(
|
||||
notification.class.NotificationSetting,
|
||||
{
|
||||
provider: providerId,
|
||||
type: notification.ids.MentionNotification,
|
||||
space: receiver._id as unknown as Ref<Space>
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
if (setting !== undefined) {
|
||||
return setting.enabled
|
||||
}
|
||||
const provider = (
|
||||
await control.modelDb.findAll(notification.class.NotificationProvider, {
|
||||
_id: providerId
|
||||
})
|
||||
)[0]
|
||||
if (provider === undefined) return false
|
||||
return provider.default
|
||||
}
|
||||
|
||||
async function getPlatformNotificationTx (
|
||||
ptx: TxCollectionCUD<Doc, Backlink>,
|
||||
backlink: Backlink,
|
||||
textPart: string | undefined,
|
||||
sender: string | undefined
|
||||
): Promise<TxCollectionCUD<Doc, Notification> | undefined> {
|
||||
const createTx: TxCreateDoc<Notification> = {
|
||||
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,
|
||||
type: notification.ids.MentionNotification
|
||||
} as unknown as Data<Notification>
|
||||
}
|
||||
|
||||
if (sender !== undefined && textPart !== undefined) {
|
||||
const text = `${sender} mentioned you in ${textPart} ${backlink.message}`
|
||||
createTx.attributes.text = text
|
||||
}
|
||||
|
||||
const createNotificationTx: TxCollectionCUD<Doc, Notification> = {
|
||||
...ptx,
|
||||
_id: generateId(),
|
||||
collection: 'notifications',
|
||||
tx: createTx
|
||||
}
|
||||
|
||||
return createNotificationTx
|
||||
}
|
||||
|
||||
function getBacklink (ptx: TxCollectionCUD<Doc, Backlink>): Backlink {
|
||||
return TxProcessor.createDoc2Doc(ptx.tx as TxCreateDoc<Backlink>)
|
||||
}
|
||||
@@ -293,85 +407,6 @@ async function getBacklinkDoc (backlink: Backlink, control: TriggerControl): Pro
|
||||
)[0]
|
||||
}
|
||||
|
||||
async function getTextPart (doc: Doc, hierarchy: Hierarchy): Promise<string | undefined> {
|
||||
const TextPresenter = getTextPresenter(doc._class, hierarchy)
|
||||
if (TextPresenter === undefined) return
|
||||
return (await getResource(TextPresenter.presenter))(doc)
|
||||
}
|
||||
|
||||
async function getHtmlPart (doc: Doc, hierarchy: Hierarchy): Promise<string | undefined> {
|
||||
const HTMLPresenter = getHTMLPresenter(doc._class, hierarchy)
|
||||
const htmlPart = HTMLPresenter !== undefined ? (await getResource(HTMLPresenter.presenter))(doc) : undefined
|
||||
return htmlPart
|
||||
}
|
||||
|
||||
async function getSender (ptx: TxCollectionCUD<Doc, Backlink>, control: TriggerControl): Promise<string | undefined> {
|
||||
const account = (
|
||||
await control.modelDb.findAll(
|
||||
contact.class.EmployeeAccount,
|
||||
{
|
||||
_id: ptx.modifiedBy as Ref<EmployeeAccount>
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
if (account === undefined) return undefined
|
||||
|
||||
return formatName(account.name)
|
||||
}
|
||||
|
||||
async function getEmailTx (
|
||||
ptx: TxCollectionCUD<Doc, Backlink>,
|
||||
backlink: Backlink,
|
||||
sender: string,
|
||||
textPart: string,
|
||||
htmlPart: string | undefined,
|
||||
receiver: EmployeeAccount
|
||||
): Promise<TxCreateDoc<EmailNotification> | undefined> {
|
||||
const html = `<p><b>${sender}</b> mentioned you in ${htmlPart !== undefined ? htmlPart : textPart}</p> ${
|
||||
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.email],
|
||||
subject: `You was mentioned in ${textPart}`,
|
||||
text,
|
||||
html
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getHTMLPresenter (_class: Ref<Class<Doc>>, hierarchy: Hierarchy): HTMLPresenter | undefined {
|
||||
let clazz: Ref<Class<Obj>> | 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<Class<Doc>>, hierarchy: Hierarchy): TextPresenter | undefined {
|
||||
let clazz: Ref<Class<Obj>> | 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: {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface Content {
|
||||
text: string
|
||||
html: string
|
||||
subject: string
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
function escapeRegExp (str: string): string {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
}
|
||||
|
||||
export function replaceAll (str: string, find: string, replace: string): string {
|
||||
return str.replace(new RegExp(escapeRegExp(find), 'g'), replace)
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
"@hcengineering/core": "^0.6.17",
|
||||
"@hcengineering/platform": "^0.6.7",
|
||||
"@hcengineering/notification": "~0.6.0",
|
||||
"@hcengineering/server-core": "~0.6.1"
|
||||
"@hcengineering/server-core": "~0.6.1",
|
||||
"@hcengineering/contact": "~0.6.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import core, { Account, Class, Doc, Ref, TxCreateDoc, TxFactory, TxUpdateDoc } from '@hcengineering/core'
|
||||
import type { Resource, Plugin } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import type { TriggerControl, TriggerFunc } from '@hcengineering/server-core'
|
||||
import contact, { Employee, EmployeeAccount } from '@hcengineering/contact'
|
||||
import core, { Account, Class, Doc, Mixin, Ref, TxCreateDoc, TxFactory, TxUpdateDoc } from '@hcengineering/core'
|
||||
import notification, { LastView } from '@hcengineering/notification'
|
||||
import { Plugin, plugin, Resource } from '@hcengineering/platform'
|
||||
import type { TriggerControl, TriggerFunc } from '@hcengineering/server-core'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -69,6 +69,60 @@ export async function getUpdateLastViewTx (
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function getEmployeeAccount (
|
||||
employee: Ref<Employee>,
|
||||
control: TriggerControl
|
||||
): Promise<EmployeeAccount | undefined> {
|
||||
const account = (
|
||||
await control.modelDb.findAll(
|
||||
contact.class.EmployeeAccount,
|
||||
{
|
||||
employee: employee
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
return account
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function getEmployeeAccountById (
|
||||
_id: Ref<Account>,
|
||||
control: TriggerControl
|
||||
): Promise<EmployeeAccount | undefined> {
|
||||
const account = (
|
||||
await control.modelDb.findAll(
|
||||
contact.class.EmployeeAccount,
|
||||
{
|
||||
_id: _id as Ref<EmployeeAccount>
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
return account
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function getEmployee (employee: Ref<Employee>, control: TriggerControl): Promise<Employee | undefined> {
|
||||
const account = (
|
||||
await control.findAll(
|
||||
contact.class.Employee,
|
||||
{
|
||||
_id: employee
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
return account
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@@ -103,10 +157,33 @@ export async function createLastViewTx (
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type Presenter = (doc: Doc, control: TriggerControl) => Promise<string>
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface HTMLPresenter extends Class<Doc> {
|
||||
presenter: Resource<Presenter>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface TextPresenter extends Class<Doc> {
|
||||
presenter: Resource<Presenter>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export default plugin(serverNotificationId, {
|
||||
mixin: {
|
||||
HTMLPresenter: '' as Ref<Mixin<HTMLPresenter>>,
|
||||
TextPresenter: '' as Ref<Mixin<TextPresenter>>
|
||||
},
|
||||
trigger: {
|
||||
OnBacklinkCreate: '' as Resource<TriggerFunc>,
|
||||
UpdateLastView: '' as Resource<TriggerFunc>
|
||||
|
||||
@@ -36,16 +36,16 @@ import { workbenchId } from '@hcengineering/workbench'
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function vacancyHTMLPresenter (doc: Doc): string {
|
||||
export async function vacancyHTMLPresenter (doc: Doc, control: TriggerControl): Promise<string> {
|
||||
const vacancy = doc as Vacancy
|
||||
const front = getMetadata(login.metadata.FrontUrl) ?? ''
|
||||
return `<a href="${front}/${workbenchId}/${recruitId}/${vacancy._id}/#${recruit.component.EditVacancy}|${vacancy._id}|${vacancy._class}">${vacancy.name}</a>`
|
||||
return `<a href="${front}/${workbenchId}/${control.workspace}/${recruitId}/${vacancy._id}/#${recruit.component.EditVacancy}|${vacancy._id}|${vacancy._class}">${vacancy.name}</a>`
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function vacancyTextPresenter (doc: Doc): string {
|
||||
export async function vacancyTextPresenter (doc: Doc): Promise<string> {
|
||||
const vacancy = doc as Vacancy
|
||||
return `${vacancy.name}`
|
||||
}
|
||||
@@ -53,16 +53,16 @@ export function vacancyTextPresenter (doc: Doc): string {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function applicationHTMLPresenter (doc: Doc): string {
|
||||
export async function applicationHTMLPresenter (doc: Doc, control: TriggerControl): Promise<string> {
|
||||
const applicant = doc as Applicant
|
||||
const front = getMetadata(login.metadata.FrontUrl) ?? ''
|
||||
return `<a href="${front}/${workbenchId}/${recruitId}/${applicant.space}/#${view.component.EditDoc}|${applicant._id}|${applicant._class}">APP-${applicant.number}</a>`
|
||||
return `<a href="${front}/${workbenchId}/${control.workspace}/${recruitId}/${applicant.space}/#${view.component.EditDoc}|${applicant._id}|${applicant._class}">APP-${applicant.number}</a>`
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function applicationTextPresenter (doc: Doc): string {
|
||||
export async function applicationTextPresenter (doc: Doc): Promise<string> {
|
||||
const applicant = doc as Applicant
|
||||
return `APP-${applicant.number}`
|
||||
}
|
||||
@@ -185,7 +185,6 @@ async function handleApplicantUpdate (control: TriggerControl, cud: TxCUD<Doc>,
|
||||
control,
|
||||
res,
|
||||
applicant,
|
||||
applicationTextPresenter(applicant),
|
||||
applicant.assignee,
|
||||
tx as TxCollectionCUD<Applicant, AttachedDoc>
|
||||
)
|
||||
@@ -203,7 +202,6 @@ async function handleApplicantCreate (control: TriggerControl, cud: TxCUD<Doc>,
|
||||
control,
|
||||
res,
|
||||
applicant,
|
||||
applicationTextPresenter(applicant),
|
||||
applicant.assignee,
|
||||
tx as TxCollectionCUD<Applicant, AttachedDoc>
|
||||
)
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/core": "^0.6.17",
|
||||
"@hcengineering/platform": "^0.6.7",
|
||||
"@hcengineering/server-notification": "^0.6.0",
|
||||
"@hcengineering/server-core": "~0.6.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Resource, Plugin } from '@hcengineering/platform'
|
||||
import type { Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import { Doc } from '@hcengineering/core'
|
||||
import { TriggerFunc } from '@hcengineering/server-core'
|
||||
import { Presenter } from '@hcengineering/server-notification'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -28,10 +28,10 @@ export const serverRecruitId = 'server-recruit' as Plugin
|
||||
*/
|
||||
export default plugin(serverRecruitId, {
|
||||
function: {
|
||||
ApplicationHTMLPresenter: '' as Resource<(doc: Doc) => string>,
|
||||
ApplicationTextPresenter: '' as Resource<(doc: Doc) => string>,
|
||||
VacancyHTMLPresenter: '' as Resource<(doc: Doc) => string>,
|
||||
VacancyTextPresenter: '' as Resource<(doc: Doc) => string>
|
||||
ApplicationHTMLPresenter: '' as Resource<Presenter>,
|
||||
ApplicationTextPresenter: '' as Resource<Presenter>,
|
||||
VacancyHTMLPresenter: '' as Resource<Presenter>,
|
||||
VacancyTextPresenter: '' as Resource<Presenter>
|
||||
},
|
||||
trigger: {
|
||||
OnRecruitUpdate: '' as Resource<TriggerFunc>
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
"@hcengineering/core": "^0.6.17",
|
||||
"@hcengineering/platform": "^0.6.7",
|
||||
"@hcengineering/server-core": "~0.6.1",
|
||||
"@hcengineering/server-notification": "~0.6.0",
|
||||
"@hcengineering/server-notification": "^0.6.0",
|
||||
"@hcengineering/server-notification-resources": "~0.6.0",
|
||||
"@hcengineering/task": "~0.6.0",
|
||||
"@hcengineering/view": "^0.6.1",
|
||||
"@hcengineering/login": "~0.6.1",
|
||||
|
||||
@@ -13,25 +13,19 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import contact, { Employee, EmployeeAccount, formatName } from '@hcengineering/contact'
|
||||
import core, {
|
||||
Account,
|
||||
AttachedDoc,
|
||||
Data,
|
||||
Doc,
|
||||
generateId,
|
||||
Ref,
|
||||
Tx,
|
||||
TxCollectionCUD,
|
||||
TxCreateDoc,
|
||||
TxProcessor,
|
||||
TxUpdateDoc
|
||||
} from '@hcengineering/core'
|
||||
import { Employee } from '@hcengineering/contact'
|
||||
import core, { AttachedDoc, Doc, Ref, Tx, TxCollectionCUD, TxProcessor, TxUpdateDoc } from '@hcengineering/core'
|
||||
import login from '@hcengineering/login'
|
||||
import notification, { Notification, NotificationStatus } from '@hcengineering/notification'
|
||||
import { NotificationAction } from '@hcengineering/notification'
|
||||
import { getMetadata, Resource } from '@hcengineering/platform'
|
||||
import { TriggerControl } from '@hcengineering/server-core'
|
||||
import { getUpdateLastViewTx } from '@hcengineering/server-notification'
|
||||
import {
|
||||
getEmployee,
|
||||
getEmployeeAccount,
|
||||
getEmployeeAccountById,
|
||||
getUpdateLastViewTx
|
||||
} from '@hcengineering/server-notification'
|
||||
import { createNotificationTxes } from '@hcengineering/server-notification-resources'
|
||||
import task, { Issue, Task, taskId } from '@hcengineering/task'
|
||||
import view from '@hcengineering/view'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
@@ -39,52 +33,20 @@ import { workbenchId } from '@hcengineering/workbench'
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function issueHTMLPresenter (doc: Doc): string {
|
||||
export async function issueHTMLPresenter (doc: Doc, control: TriggerControl): Promise<string> {
|
||||
const issue = doc as Issue
|
||||
const front = getMetadata(login.metadata.FrontUrl) ?? ''
|
||||
return `<a href="${front}/${workbenchId}/${taskId}/${issue.space}/#${view.component.EditDoc}|${issue._id}|${issue._class}">Task-${issue.number}</a>`
|
||||
return `<a href="${front}/${workbenchId}/${control.workspace}/${taskId}/${issue.space}/#${view.component.EditDoc}|${issue._id}|${issue._class}">Task-${issue.number}</a>`
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function issueTextPresenter (doc: Doc): string {
|
||||
export async function issueTextPresenter (doc: Doc): Promise<string> {
|
||||
const issue = doc as Issue
|
||||
return `Task-${issue.number}`
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function getEmployeeAccount (
|
||||
employee: Ref<Account>,
|
||||
control: TriggerControl
|
||||
): Promise<EmployeeAccount | undefined> {
|
||||
const account = (
|
||||
await control.modelDb.findAll(
|
||||
contact.class.EmployeeAccount,
|
||||
{
|
||||
_id: employee as Ref<EmployeeAccount>
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
return account
|
||||
}
|
||||
|
||||
async function getEmployee (employee: Ref<Employee>, control: TriggerControl): Promise<Employee | undefined> {
|
||||
const account = (
|
||||
await control.findAll(
|
||||
contact.class.Employee,
|
||||
{
|
||||
_id: employee
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
return account
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@@ -92,12 +54,11 @@ export async function addAssigneeNotification (
|
||||
control: TriggerControl,
|
||||
res: Tx[],
|
||||
issue: Doc,
|
||||
issueName: string,
|
||||
assignee: Ref<Employee>,
|
||||
ptx: TxCollectionCUD<AttachedDoc, AttachedDoc>,
|
||||
component?: Resource<string>
|
||||
): Promise<void> {
|
||||
const sender = await getEmployeeAccount(ptx.modifiedBy, control)
|
||||
const sender = await getEmployeeAccountById(ptx.modifiedBy, control)
|
||||
if (sender === undefined) {
|
||||
return
|
||||
}
|
||||
@@ -107,29 +68,30 @@ export async function addAssigneeNotification (
|
||||
return
|
||||
}
|
||||
|
||||
const createTx: TxCreateDoc<Notification> = {
|
||||
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,
|
||||
type: task.ids.AssigneedNotification,
|
||||
text: `${issueName} was assigned to you by ${formatName(sender.name)}`,
|
||||
action: {
|
||||
component: component ?? view.component.EditDoc,
|
||||
objectId: issue._id,
|
||||
objectClass: issue._class
|
||||
}
|
||||
} as unknown as Data<Notification>
|
||||
const receiver = await getEmployeeAccount(assignee, control)
|
||||
if (receiver === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
res.push(control.txFactory.createTxCollectionCUD(target._class, target._id, target.space, 'notifications', createTx))
|
||||
// eslint-disable-next-line
|
||||
const action: NotificationAction = {
|
||||
component: component ?? view.component.EditDoc,
|
||||
objectId: issue._id,
|
||||
objectClass: issue._class
|
||||
} as NotificationAction
|
||||
|
||||
const result = await createNotificationTxes(
|
||||
control,
|
||||
ptx,
|
||||
task.ids.AssigneedNotification,
|
||||
issue,
|
||||
sender,
|
||||
receiver,
|
||||
undefined,
|
||||
action
|
||||
)
|
||||
|
||||
res.push(...result)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/core": "^0.6.17",
|
||||
"@hcengineering/server-notification": "^0.6.0",
|
||||
"@hcengineering/platform": "^0.6.7",
|
||||
"@hcengineering/server-core": "~0.6.1"
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Doc } from '@hcengineering/core'
|
||||
import type { Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import { Presenter } from '@hcengineering/server-notification'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -27,7 +27,7 @@ export const serverTaskId = 'server-task' as Plugin
|
||||
*/
|
||||
export default plugin(serverTaskId, {
|
||||
function: {
|
||||
IssueHTMLPresenter: '' as Resource<(doc: Doc) => string>,
|
||||
IssueTextPresenter: '' as Resource<(doc: Doc) => string>
|
||||
IssueHTMLPresenter: '' as Resource<Presenter>,
|
||||
IssueTextPresenter: '' as Resource<Presenter>
|
||||
}
|
||||
})
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
"@hcengineering/contact": "~0.6.5",
|
||||
"@hcengineering/notification": "~0.6.0",
|
||||
"@hcengineering/task": "~0.6.0",
|
||||
"@hcengineering/view": "^0.6.1",
|
||||
"@hcengineering/login": "~0.6.1",
|
||||
"@hcengineering/workbench": "~0.6.1",
|
||||
"@hcengineering/server-task-resources": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import { Employee } from '@hcengineering/contact'
|
||||
import core, {
|
||||
AttachedDoc,
|
||||
Doc,
|
||||
DocumentUpdate,
|
||||
Ref,
|
||||
Space,
|
||||
@@ -28,10 +29,13 @@ import core, {
|
||||
TxUpdateDoc,
|
||||
WithLookup
|
||||
} from '@hcengineering/core'
|
||||
import login from '@hcengineering/login'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import { Resource } from '@hcengineering/platform/lib/platform'
|
||||
import { TriggerControl } from '@hcengineering/server-core'
|
||||
import { addAssigneeNotification } from '@hcengineering/server-task-resources'
|
||||
import tracker, { Issue, IssueParentInfo, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import tracker, { Issue, IssueParentInfo, TimeSpendReport, trackerId } from '@hcengineering/tracker'
|
||||
|
||||
async function updateSubIssues (
|
||||
updateTx: TxUpdateDoc<Issue>,
|
||||
@@ -46,6 +50,29 @@ async function updateSubIssues (
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function issueHTMLPresenter (doc: Doc, control: TriggerControl): Promise<string> {
|
||||
const issue = doc as Issue
|
||||
const team = (await control.findAll(tracker.class.Team, { _id: issue.space })).shift()
|
||||
const issueName = `${team?.identifier ?? '?'}-${issue.number}`
|
||||
|
||||
const front = getMetadata(login.metadata.FrontUrl) ?? ''
|
||||
return `<a href="${front}/${workbenchId}/${control.workspace}/${trackerId}/${issue.space}/#${tracker.component.EditIssue}|${issue._id}|${issue._class}">${issueName}</a>`
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function issueTextPresenter (doc: Doc, control: TriggerControl): Promise<string> {
|
||||
const issue = doc as Issue
|
||||
const team = (await control.findAll(tracker.class.Team, { _id: issue.space })).shift()
|
||||
const issueName = `${team?.identifier ?? '?'}-${issue.number}`
|
||||
|
||||
return issueName
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@@ -56,14 +83,10 @@ export async function addTrackerAssigneeNotification (
|
||||
assignee: Ref<Employee>,
|
||||
ptx: TxCollectionCUD<Issue, AttachedDoc>
|
||||
): Promise<void> {
|
||||
const team = (await control.findAll(tracker.class.Team, { _id: issue.space })).shift()
|
||||
const issueName = `${team?.identifier ?? '?'}-${issue.number}`
|
||||
|
||||
await addAssigneeNotification(
|
||||
control,
|
||||
res,
|
||||
issue,
|
||||
issueName,
|
||||
assignee,
|
||||
ptx,
|
||||
tracker.component.EditIssue as unknown as Resource<string>
|
||||
@@ -142,6 +165,10 @@ export async function OnIssueUpdate (tx: Tx, control: TriggerControl): Promise<T
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export default async () => ({
|
||||
function: {
|
||||
IssueHTMLPresenter: issueHTMLPresenter,
|
||||
IssueTextPresenter: issueTextPresenter
|
||||
},
|
||||
trigger: {
|
||||
OnIssueUpdate
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/platform": "^0.6.7",
|
||||
"@hcengineering/server-notification": "^0.6.0",
|
||||
"@hcengineering/server-core": "~0.6.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import type { Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import { TriggerFunc } from '@hcengineering/server-core'
|
||||
import { Presenter } from '@hcengineering/server-notification'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -26,6 +27,10 @@ export const serverTrackerId = 'server-tracker' as Plugin
|
||||
* @public
|
||||
*/
|
||||
export default plugin(serverTrackerId, {
|
||||
function: {
|
||||
IssueHTMLPresenter: '' as Resource<Presenter>,
|
||||
IssueTextPresenter: '' as Resource<Presenter>
|
||||
},
|
||||
trigger: {
|
||||
OnIssueUpdate: '' as Resource<TriggerFunc>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user