Refactor server side notifications (#2327)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov
2022-10-26 17:24:39 +07:00
committed by GitHub
parent 70e00dbb5f
commit 3a8d1e2bcb
58 changed files with 539 additions and 400 deletions
+1 -1
View File
@@ -99,7 +99,7 @@
"@hcengineering/server-attachment-resources": "~0.6.0",
"@hcengineering/server-contact": "~0.6.1",
"@hcengineering/server-contact-resources": "~0.6.0",
"@hcengineering/server-notification": "~0.6.0",
"@hcengineering/server-notification": "^0.6.0",
"@hcengineering/server-notification-resources": "~0.6.0",
"@hcengineering/server-setting": "~0.6.0",
"@hcengineering/server-setting-resources": "~0.6.0",
+1 -1
View File
@@ -77,7 +77,7 @@
"@hcengineering/attachment": "~0.6.1",
"@hcengineering/server-contact": "~0.6.1",
"@hcengineering/server-contact-resources": "~0.6.0",
"@hcengineering/server-notification": "~0.6.0",
"@hcengineering/server-notification": "^0.6.0",
"@hcengineering/server-notification-resources": "~0.6.0",
"@hcengineering/server-setting": "~0.6.0",
"@hcengineering/server-setting-resources": "~0.6.0",
+4 -1
View File
@@ -128,7 +128,10 @@ export function createModel (builder: Builder): void {
notification.class.NotificationType,
core.space.Model,
{
label: calendar.string.Reminder
label: calendar.string.Reminder,
textTemplate: 'Reminder: {doc}',
htmlTemplate: 'Reminder: {doc}',
subjectTemplate: 'Reminder: {doc}'
},
calendar.ids.ReminderNotification
)
+7 -1
View File
@@ -81,6 +81,9 @@ export class TEmaiNotification extends TDoc implements EmailNotification {
@Model(notification.class.NotificationType, core.class.Doc, DOMAIN_MODEL)
export class TNotificationType extends TDoc implements NotificationType {
label!: IntlString
textTemplate!: string
htmlTemplate!: string
subjectTemplate!: string
}
@Model(notification.class.NotificationProvider, core.class.Doc, DOMAIN_MODEL)
@@ -126,7 +129,10 @@ export function createModel (builder: Builder): void {
notification.class.NotificationType,
core.space.Model,
{
label: notification.string.MentionNotification
label: notification.string.MentionNotification,
textTemplate: '{sender} mentioned you in {doc} {data}',
htmlTemplate: '<p><b>{sender}</b> mentioned you in {doc}</p> {data}',
subjectTemplate: 'You was mentioned in {doc}'
},
notification.ids.MentionNotification
)
+1 -1
View File
@@ -31,6 +31,6 @@
"@hcengineering/server-chunter": "~0.6.0",
"@hcengineering/server-core": "~0.6.1",
"@hcengineering/chunter": "~0.6.1",
"@hcengineering/view": "^0.6.1"
"@hcengineering/server-notification": "^0.6.0"
}
}
+3 -3
View File
@@ -17,16 +17,16 @@ import { Builder } from '@hcengineering/model'
import core, { Class, Doc } from '@hcengineering/core'
import chunter from '@hcengineering/chunter'
import view from '@hcengineering/view'
import serverNotification from '@hcengineering/server-notification'
import serverCore, { ObjectDDParticipant } from '@hcengineering/server-core'
import serverChunter from '@hcengineering/server-chunter'
export function createModel (builder: Builder): void {
builder.mixin(chunter.class.Channel, core.class.Class, view.mixin.HTMLPresenter, {
builder.mixin(chunter.class.Channel, core.class.Class, serverNotification.mixin.HTMLPresenter, {
presenter: serverChunter.function.ChannelHTMLPresenter
})
builder.mixin(chunter.class.Channel, core.class.Class, view.mixin.TextPresenter, {
builder.mixin(chunter.class.Channel, core.class.Class, serverNotification.mixin.TextPresenter, {
presenter: serverChunter.function.ChannelTextPresenter
})
+1 -1
View File
@@ -31,6 +31,6 @@
"@hcengineering/server-contact": "~0.6.1",
"@hcengineering/server-core": "~0.6.1",
"@hcengineering/contact": "~0.6.5",
"@hcengineering/view": "^0.6.1"
"@hcengineering/server-notification": "^0.6.0"
}
}
+5 -5
View File
@@ -19,23 +19,23 @@ import { Builder } from '@hcengineering/model'
import serverCore from '@hcengineering/server-core'
import core from '@hcengineering/core'
import contact from '@hcengineering/contact'
import view from '@hcengineering/view'
import serverNotification from '@hcengineering/server-notification'
import serverContact from '@hcengineering/server-contact'
export function createModel (builder: Builder): void {
builder.mixin(contact.class.Person, core.class.Class, view.mixin.HTMLPresenter, {
builder.mixin(contact.class.Person, core.class.Class, serverNotification.mixin.HTMLPresenter, {
presenter: serverContact.function.PersonHTMLPresenter
})
builder.mixin(contact.class.Person, core.class.Class, view.mixin.TextPresenter, {
builder.mixin(contact.class.Person, core.class.Class, serverNotification.mixin.TextPresenter, {
presenter: serverContact.function.PersonTextPresenter
})
builder.mixin(contact.class.Organization, core.class.Class, view.mixin.HTMLPresenter, {
builder.mixin(contact.class.Organization, core.class.Class, serverNotification.mixin.HTMLPresenter, {
presenter: serverContact.function.OrganizationHTMLPresenter
})
builder.mixin(contact.class.Organization, core.class.Class, view.mixin.TextPresenter, {
builder.mixin(contact.class.Organization, core.class.Class, serverNotification.mixin.TextPresenter, {
presenter: serverContact.function.OrganizationTextPresenter
})
+1 -1
View File
@@ -31,6 +31,6 @@
"@hcengineering/server-inventory": "~0.6.0",
"@hcengineering/server-core": "~0.6.1",
"@hcengineering/inventory": "~0.6.0",
"@hcengineering/view": "^0.6.1"
"@hcengineering/server-notification": "^0.6.0"
}
}
+3 -3
View File
@@ -18,14 +18,14 @@ import { Builder } from '@hcengineering/model'
import core from '@hcengineering/core'
import inventory from '@hcengineering/inventory'
import serverInventory from '@hcengineering/server-inventory'
import view from '@hcengineering/view'
import serverNotification from '@hcengineering/server-notification'
export function createModel (builder: Builder): void {
builder.mixin(inventory.class.Product, core.class.Class, view.mixin.HTMLPresenter, {
builder.mixin(inventory.class.Product, core.class.Class, serverNotification.mixin.HTMLPresenter, {
presenter: serverInventory.function.ProductHTMLPresenter
})
builder.mixin(inventory.class.Product, core.class.Class, view.mixin.TextPresenter, {
builder.mixin(inventory.class.Product, core.class.Class, serverNotification.mixin.TextPresenter, {
presenter: serverInventory.function.ProductTextPresenter
})
}
+1 -1
View File
@@ -31,6 +31,6 @@
"@hcengineering/server-lead": "~0.6.0",
"@hcengineering/server-core": "~0.6.1",
"@hcengineering/lead": "~0.6.0",
"@hcengineering/view": "^0.6.1"
"@hcengineering/server-notification": "^0.6.0"
}
}
+3 -3
View File
@@ -17,16 +17,16 @@ import { Builder } from '@hcengineering/model'
import core from '@hcengineering/core'
import lead from '@hcengineering/lead'
import view from '@hcengineering/view'
import serverNotification from '@hcengineering/server-notification'
import serverLead from '@hcengineering/server-lead'
import serverCore from '@hcengineering/server-core'
export function createModel (builder: Builder): void {
builder.mixin(lead.class.Lead, core.class.Class, view.mixin.HTMLPresenter, {
builder.mixin(lead.class.Lead, core.class.Class, serverNotification.mixin.HTMLPresenter, {
presenter: serverLead.function.LeadHTMLPresenter
})
builder.mixin(lead.class.Lead, core.class.Class, view.mixin.TextPresenter, {
builder.mixin(lead.class.Lead, core.class.Class, serverNotification.mixin.TextPresenter, {
presenter: serverLead.function.LeadTextPresenter
})
+2 -1
View File
@@ -27,8 +27,9 @@
"dependencies": {
"@hcengineering/core": "^0.6.17",
"@hcengineering/model": "~0.6.0",
"@hcengineering/model-core": "~0.6.0",
"@hcengineering/platform": "^0.6.7",
"@hcengineering/server-notification": "~0.6.0",
"@hcengineering/server-notification": "^0.6.0",
"@hcengineering/server-core": "~0.6.1"
}
}
+16 -2
View File
@@ -14,13 +14,27 @@
// limitations under the License.
//
import { Builder } from '@hcengineering/model'
import { Builder, Mixin } from '@hcengineering/model'
import serverCore from '@hcengineering/server-core'
import core from '@hcengineering/core'
import serverNotification from '@hcengineering/server-notification'
import serverNotification, { HTMLPresenter, TextPresenter, Presenter } from '@hcengineering/server-notification'
import { Resource } from '@hcengineering/platform'
import { TClass } from '@hcengineering/model-core'
@Mixin(serverNotification.mixin.HTMLPresenter, core.class.Class)
export class THTMLPresenter extends TClass implements HTMLPresenter {
presenter!: Resource<Presenter>
}
@Mixin(serverNotification.mixin.TextPresenter, core.class.Class)
export class TTextPresenter extends TClass implements TextPresenter {
presenter!: Resource<Presenter>
}
export function createModel (builder: Builder): void {
builder.createModel(THTMLPresenter, TTextPresenter)
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverNotification.trigger.OnBacklinkCreate
})
+1 -1
View File
@@ -31,6 +31,6 @@
"@hcengineering/server-recruit": "~0.6.0",
"@hcengineering/server-core": "~0.6.1",
"@hcengineering/recruit": "~0.6.3",
"@hcengineering/view": "^0.6.1"
"@hcengineering/server-notification": "^0.6.0"
}
}
+5 -5
View File
@@ -17,24 +17,24 @@ import { Builder } from '@hcengineering/model'
import core from '@hcengineering/core'
import recruit from '@hcengineering/recruit'
import view from '@hcengineering/view'
import serverNotification from '@hcengineering/server-notification'
import serverRecruit from '@hcengineering/server-recruit'
import serverCore from '@hcengineering/server-core'
export function createModel (builder: Builder): void {
builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.HTMLPresenter, {
builder.mixin(recruit.class.Applicant, core.class.Class, serverNotification.mixin.HTMLPresenter, {
presenter: serverRecruit.function.ApplicationHTMLPresenter
})
builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.TextPresenter, {
builder.mixin(recruit.class.Applicant, core.class.Class, serverNotification.mixin.TextPresenter, {
presenter: serverRecruit.function.ApplicationTextPresenter
})
builder.mixin(recruit.class.Vacancy, core.class.Class, view.mixin.HTMLPresenter, {
builder.mixin(recruit.class.Vacancy, core.class.Class, serverNotification.mixin.HTMLPresenter, {
presenter: serverRecruit.function.VacancyHTMLPresenter
})
builder.mixin(recruit.class.Vacancy, core.class.Class, view.mixin.TextPresenter, {
builder.mixin(recruit.class.Vacancy, core.class.Class, serverNotification.mixin.TextPresenter, {
presenter: serverRecruit.function.VacancyTextPresenter
})
+1 -1
View File
@@ -31,6 +31,6 @@
"@hcengineering/server-task": "~0.6.0",
"@hcengineering/server-core": "~0.6.1",
"@hcengineering/task": "~0.6.0",
"@hcengineering/view": "^0.6.1"
"@hcengineering/server-notification": "^0.6.0"
}
}
+3 -3
View File
@@ -18,14 +18,14 @@ import { Builder } from '@hcengineering/model'
import core from '@hcengineering/core'
import serverTask from '@hcengineering/server-task'
import task from '@hcengineering/task'
import view from '@hcengineering/view'
import serverNotification from '@hcengineering/server-notification'
export function createModel (builder: Builder): void {
builder.mixin(task.class.Issue, core.class.Class, view.mixin.HTMLPresenter, {
builder.mixin(task.class.Issue, core.class.Class, serverNotification.mixin.HTMLPresenter, {
presenter: serverTask.function.IssueHTMLPresenter
})
builder.mixin(task.class.Issue, core.class.Class, view.mixin.TextPresenter, {
builder.mixin(task.class.Issue, core.class.Class, serverNotification.mixin.TextPresenter, {
presenter: serverTask.function.IssueTextPresenter
})
}
+2
View File
@@ -27,7 +27,9 @@
"dependencies": {
"@hcengineering/core": "^0.6.17",
"@hcengineering/model": "~0.6.0",
"@hcengineering/tracker": "^0.6.1",
"@hcengineering/server-core": "~0.6.1",
"@hcengineering/server-notification": "^0.6.0",
"@hcengineering/server-tracker": "~0.6.0"
}
}
+10
View File
@@ -17,8 +17,18 @@ import { Builder } from '@hcengineering/model'
import core from '@hcengineering/core'
import serverCore from '@hcengineering/server-core'
import serverTracker from '@hcengineering/server-tracker'
import tracker from '@hcengineering/tracker'
import serverNotification from '@hcengineering/server-notification'
export function createModel (builder: Builder): void {
builder.mixin(tracker.class.Issue, core.class.Class, serverNotification.mixin.HTMLPresenter, {
presenter: serverTracker.function.IssueHTMLPresenter
})
builder.mixin(tracker.class.Issue, core.class.Class, serverNotification.mixin.TextPresenter, {
presenter: serverTracker.function.IssueTextPresenter
})
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverTracker.trigger.OnIssueUpdate
})
+4 -1
View File
@@ -581,7 +581,10 @@ export function createModel (builder: Builder): void {
notification.class.NotificationType,
core.space.Model,
{
label: task.string.Assigned
label: task.string.Assigned,
textTemplate: '{doc} was assigned to you by {sender}',
htmlTemplate: '<p>{doc} was assigned to you by {sender}</p>',
subjectTemplate: '{doc} was assigned to you'
},
task.ids.AssigneedNotification
)
+1 -15
View File
@@ -17,6 +17,7 @@ import type { Class, Client, Data, Doc, DocumentQuery, Ref, Space } from '@hceng
import { DOMAIN_MODEL } from '@hcengineering/core'
import { Builder, Mixin, Model } from '@hcengineering/model'
import core, { TClass, TDoc } from '@hcengineering/model-core'
import preference, { TPreference } from '@hcengineering/model-preference'
import type { Asset, IntlString, Resource, Status } from '@hcengineering/platform'
import type { AnyComponent } from '@hcengineering/ui'
import type {
@@ -32,7 +33,6 @@ import type {
CollectionPresenter,
Filter,
FilterMode,
HTMLPresenter,
IgnoreActions,
InlineAttributEditor,
KeyBinding,
@@ -46,7 +46,6 @@ import type {
PreviewPresenter,
SpaceHeader,
SpaceName,
TextPresenter,
ViewAction,
ViewActionInput,
ViewContext,
@@ -54,7 +53,6 @@ import type {
ViewletDescriptor,
ViewletPreference
} from '@hcengineering/view'
import preference, { TPreference } from '@hcengineering/model-preference'
import view from './plugin'
export { viewOperation } from './migration'
@@ -223,16 +221,6 @@ export class TIgnoreActions extends TClass implements IgnoreActions {
actions!: Ref<Action>[]
}
@Mixin(view.mixin.HTMLPresenter, core.class.Class)
export class THTMLPresenter extends TClass implements HTMLPresenter {
presenter!: Resource<(doc: Doc) => string>
}
@Mixin(view.mixin.TextPresenter, core.class.Class)
export class TTextPresenter extends TClass implements TextPresenter {
presenter!: Resource<(doc: Doc) => string>
}
@Mixin(view.mixin.PreviewPresenter, core.class.Class)
export class TPreviewPresenter extends TClass implements PreviewPresenter {
presenter!: AnyComponent
@@ -291,10 +279,8 @@ export function createModel (builder: Builder): void {
TObjectFactory,
TObjectTitle,
TObjectEditorHeader,
THTMLPresenter,
TSpaceHeader,
TSpaceName,
TTextPresenter,
TIgnoreActions,
TPreviewPresenter,
TLinkPresenter,
+13 -5
View File
@@ -29,6 +29,15 @@ export interface LastView extends AttachedDoc {
user: Ref<Account>
}
/**
* @public
*/
export interface NotificationAction {
component: AnyComponent
objectId: Ref<Doc>
objectClass: Ref<Class<Doc>>
}
/**
* @public
*/
@@ -39,11 +48,7 @@ export interface Notification extends AttachedDoc {
type: Ref<NotificationType>
// Defined to open particular item if required.
action?: {
component: AnyComponent
objectId: Ref<Doc>
objectClass: Ref<Class<Doc>>
}
action?: NotificationAction
}
/**
@@ -72,6 +77,9 @@ export enum NotificationStatus {
*/
export interface NotificationType extends Doc {
label: IntlString
textTemplate: string
htmlTemplate: string
subjectTemplate: string
}
/**
-16
View File
@@ -306,20 +306,6 @@ export interface IgnoreActions extends Class<Doc> {
actions: Ref<Action>[]
}
/**
* @public
*/
export interface HTMLPresenter extends Class<Doc> {
presenter: Resource<(doc: Doc) => string>
}
/**
* @public
*/
export interface TextPresenter extends Class<Doc> {
presenter: Resource<(doc: Doc) => string>
}
/**
* @public
*/
@@ -420,8 +406,6 @@ const view = plugin(viewId, {
SpaceHeader: '' as Ref<Mixin<SpaceHeader>>,
SpaceName: '' as Ref<Mixin<SpaceName>>,
IgnoreActions: '' as Ref<Mixin<IgnoreActions>>,
HTMLPresenter: '' as Ref<Mixin<HTMLPresenter>>,
TextPresenter: '' as Ref<Mixin<TextPresenter>>,
PreviewPresenter: '' as Ref<Mixin<PreviewPresenter>>
},
class: {
+1 -1
View File
@@ -90,7 +90,7 @@
"@hcengineering/server-attachment-resources": "~0.6.0",
"@hcengineering/server-contact": "~0.6.1",
"@hcengineering/server-contact-resources": "~0.6.0",
"@hcengineering/server-notification": "~0.6.0",
"@hcengineering/server-notification": "^0.6.0",
"@hcengineering/server-notification-resources": "~0.6.0",
"@hcengineering/server-setting": "~0.6.0",
"@hcengineering/server-setting-resources": "~0.6.0",
@@ -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}`
}
+1
View File
@@ -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"
+3 -2
View File
@@ -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>`
}
/**
+1 -1
View File
@@ -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"
}
+5 -5
View File
@@ -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}`
}
+1 -2
View File
@@ -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"
}
}
+3 -3
View File
@@ -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>
}
})
+3 -5
View File
@@ -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>
)
+1 -3
View File
@@ -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"
}
}
+3 -3
View File
@@ -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",
+221 -186
View File
@@ -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)
}
+2 -1
View File
@@ -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"
}
}
+81 -4
View File
@@ -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>
)
+1 -1
View File
@@ -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"
}
}
+6 -6
View File
@@ -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>
+2 -1
View File
@@ -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",
+36 -74
View File
@@ -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)
}
/**
+1 -1
View File
@@ -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"
}
+3 -3
View File
@@ -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"
}
}
+32 -5
View File
@@ -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
}
+1
View File
@@ -28,6 +28,7 @@
},
"dependencies": {
"@hcengineering/platform": "^0.6.7",
"@hcengineering/server-notification": "^0.6.0",
"@hcengineering/server-core": "~0.6.1"
}
}
+5
View File
@@ -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>
}
+1
View File
@@ -408,6 +408,7 @@ class TServerStorage implements ServerStorage {
...(await ctx.with('process-move', { _class }, () => this.processMove(ctx, tx))),
...(await ctx.with('process-triggers', {}, (ctx) =>
this.triggers.apply(tx.modifiedBy, tx, {
workspace: this.workspace,
fx: triggerFx.fx,
fulltextFx: (f) => triggerFx.fx(() => f(this.fulltextAdapter)),
storageFx: (f) => {
+1
View File
@@ -98,6 +98,7 @@ export interface Pipeline extends LowLevelStorage {
* @public
*/
export interface TriggerControl {
workspace: string
txFactory: TxFactory
findAll: Storage['findAll']
hierarchy: Hierarchy
+1 -1
View File
@@ -49,7 +49,7 @@
"minio": "^7.0.26",
"@hcengineering/server-contact": "~0.6.1",
"@hcengineering/server-contact-resources": "~0.6.0",
"@hcengineering/server-notification": "~0.6.0",
"@hcengineering/server-notification": "^0.6.0",
"@hcengineering/server-notification-resources": "~0.6.0",
"@hcengineering/server-setting": "~0.6.0",
"@hcengineering/server-setting-resources": "~0.6.0",