From 98d71a25dfae30efdf004d7e089e36717696f3ad Mon Sep 17 00:00:00 2001 From: Denis Bykhov Date: Tue, 25 Apr 2023 14:34:38 +0600 Subject: [PATCH] TSK-1263 Inbox configurator (#3067) Signed-off-by: Denis Bykhov --- models/calendar/src/index.ts | 26 +- models/calendar/src/plugin.ts | 4 +- models/chunter/package.json | 1 + models/chunter/src/index.ts | 93 ++++- models/chunter/src/plugin.ts | 9 +- models/hr/src/index.ts | 92 ++++- models/hr/src/plugin.ts | 7 + models/lead/package.json | 2 + models/lead/src/index.ts | 47 +++ models/lead/src/plugin.ts | 5 + models/notification/package.json | 1 + models/notification/src/index.ts | 139 +++++-- models/notification/src/migration.ts | 80 +++- models/notification/src/plugin.ts | 4 +- models/recruit/package.json | 1 + models/recruit/src/index.ts | 82 +++- models/recruit/src/plugin.ts | 7 + models/server-chunter/package.json | 1 + models/server-chunter/src/index.ts | 14 +- models/server-lead/package.json | 2 + models/server-lead/src/index.ts | 15 +- models/server-notification/package.json | 2 + models/server-notification/src/index.ts | 30 +- models/server-recruit/package.json | 3 +- models/server-recruit/src/index.ts | 12 +- models/server-tracker/package.json | 2 + models/server-tracker/src/index.ts | 12 +- models/task/src/index.ts | 27 +- models/tracker/package.json | 1 + models/tracker/src/index.ts | 49 ++- models/tracker/src/plugin.ts | 6 +- plugins/chunter-assets/lang/en.json | 3 + plugins/chunter-assets/lang/ru.json | 3 + .../src/components/ChannelView.svelte | 2 +- plugins/chunter/package.json | 1 + plugins/chunter/src/index.ts | 9 +- plugins/hr-assets/lang/en.json | 3 + plugins/hr-assets/lang/ru.json | 3 + plugins/notification-assets/lang/en.json | 9 +- plugins/notification-assets/lang/ru.json | 9 +- .../components/BrowserNotificatator.svelte | 34 +- .../src/components/GroupElement.svelte | 60 +++ .../NotificationGroupSetting.svelte | 138 +++++++ .../components/NotificationSettings.svelte | 199 +++------ plugins/notification-resources/src/plugin.ts | 4 +- plugins/notification/package.json | 1 + plugins/notification/src/index.ts | 69 +++- plugins/task-assets/lang/en.json | 2 +- plugins/task-assets/lang/ru.json | 2 +- plugins/task-resources/src/plugin.ts | 1 - plugins/task/src/index.ts | 1 + plugins/tracker-assets/lang/en.json | 1 + plugins/tracker-assets/lang/ru.json | 1 + .../src/components/Workbench.svelte | 3 +- server-plugins/chunter-resources/src/index.ts | 80 ++-- server-plugins/chunter/src/index.ts | 9 +- server-plugins/hr-resources/src/index.ts | 13 +- server-plugins/lead-resources/src/index.ts | 73 +--- server-plugins/lead/src/index.ts | 4 - .../notification-resources/src/index.ts | 378 +++++++++++++----- server-plugins/notification/src/index.ts | 28 +- server-plugins/recruit-resources/src/index.ts | 42 -- server-plugins/task-resources/src/index.ts | 33 -- server-plugins/task/package.json | 3 +- server-plugins/tracker-resources/src/index.ts | 28 -- server/middleware/src/spaceSecurity.ts | 2 +- 66 files changed, 1348 insertions(+), 679 deletions(-) create mode 100644 plugins/notification-resources/src/components/GroupElement.svelte create mode 100644 plugins/notification-resources/src/components/NotificationGroupSetting.svelte diff --git a/models/calendar/src/index.ts b/models/calendar/src/index.ts index 2fe70ecffc..58318c804c 100644 --- a/models/calendar/src/index.ts +++ b/models/calendar/src/index.ts @@ -125,15 +125,35 @@ export function createModel (builder: Builder): void { calendar.viewlet.CalendarEvent ) + builder.createDoc( + notification.class.NotificationGroup, + core.space.Model, + { + label: calendar.string.Calendar, + icon: calendar.icon.Calendar + }, + calendar.ids.CalendarNotificationGroup + ) + builder.createDoc( notification.class.NotificationType, core.space.Model, { hidden: false, + generated: false, label: calendar.string.Reminder, - textTemplate: 'Reminder: {doc}', - htmlTemplate: 'Reminder: {doc}', - subjectTemplate: 'Reminder: {doc}' + group: calendar.ids.CalendarNotificationGroup, + txClasses: [], + objectClass: calendar.mixin.Reminder, + templates: { + textTemplate: 'Reminder: {doc}', + htmlTemplate: 'Reminder: {doc}', + subjectTemplate: 'Reminder: {doc}' + }, + providers: { + [notification.providers.PlatformNotification]: true, + [notification.providers.EmailNotification]: false + } }, calendar.ids.ReminderNotification ) diff --git a/models/calendar/src/plugin.ts b/models/calendar/src/plugin.ts index d04fd02fd1..eb75a52646 100644 --- a/models/calendar/src/plugin.ts +++ b/models/calendar/src/plugin.ts @@ -17,6 +17,7 @@ import { TxViewlet } from '@hcengineering/activity' import { calendarId } from '@hcengineering/calendar' import calendar from '@hcengineering/calendar-resources/src/plugin' import { Ref } from '@hcengineering/core' +import { NotificationGroup } from '@hcengineering/notification' import type { IntlString } from '@hcengineering/platform' import { mergeIds } from '@hcengineering/platform' import { AnyComponent } from '@hcengineering/ui' @@ -54,6 +55,7 @@ export default mergeIds(calendarId, calendar, { CalendarEvent: '' as Ref }, ids: { - ReminderViewlet: '' as Ref + ReminderViewlet: '' as Ref, + CalendarNotificationGroup: '' as Ref } }) diff --git a/models/chunter/package.json b/models/chunter/package.json index babc677d8f..5d99cbd995 100644 --- a/models/chunter/package.json +++ b/models/chunter/package.json @@ -38,6 +38,7 @@ "@hcengineering/model-view": "^0.6.0", "@hcengineering/model-workbench": "^0.6.1", "@hcengineering/model-notification": "^0.6.0", + "@hcengineering/notification": "^0.6.10", "@hcengineering/activity": "^0.6.0", "@hcengineering/workbench": "^0.6.4", "@hcengineering/model-preference": "^0.6.0" diff --git a/models/chunter/src/index.ts b/models/chunter/src/index.ts index c0288a9c1f..c32793be8b 100644 --- a/models/chunter/src/index.ts +++ b/models/chunter/src/index.ts @@ -88,6 +88,7 @@ export class TChunterMessage extends TAttachedDoc implements ChunterMessage { attachments?: number @Prop(TypeRef(core.class.Account), chunter.string.CreateBy) + @ReadOnly() createBy!: Ref @Prop(TypeTimestamp(), chunter.string.Create) @@ -584,7 +585,7 @@ export function createModel (builder: Builder, options = { addApplication: true icon: chunter.icon.Chunter, txClass: core.class.TxCreateDoc, component: chunter.activity.TxMessageCreate, - label: notification.string.DMNotification, + label: chunter.string.DMNotification, display: 'content', editable: true, hideOnRemove: true @@ -641,6 +642,96 @@ export function createModel (builder: Builder, options = { addApplication: true filters: [] }) + builder.createDoc( + notification.class.NotificationGroup, + core.space.Model, + { + label: chunter.string.ApplicationLabelChunter, + icon: chunter.icon.Chunter + }, + chunter.ids.ChunterNotificationGroup + ) + + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + label: chunter.string.MentionNotification, + generated: false, + hidden: false, + txClasses: [core.class.TxCreateDoc], + objectClass: chunter.class.Backlink, + group: chunter.ids.ChunterNotificationGroup, + providers: { + [notification.providers.EmailNotification]: true, + [notification.providers.PlatformNotification]: true + }, + templates: { + textTemplate: '{sender} mentioned you in {doc} {data}', + htmlTemplate: '

{sender} mentioned you in {doc}

{data}', + subjectTemplate: 'You were mentioned in {doc}' + } + }, + chunter.ids.MentionNotification + ) + + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + label: chunter.string.DM, + generated: false, + hidden: false, + txClasses: [core.class.TxCreateDoc], + objectClass: chunter.class.Message, + providers: { + [notification.providers.EmailNotification]: false, + [notification.providers.PlatformNotification]: true + }, + group: chunter.ids.ChunterNotificationGroup, + templates: { + textTemplate: '{sender} has send you a message: {doc} {data}', + htmlTemplate: '

{sender} has send you a message {doc}

{data}', + subjectTemplate: 'You have new direct message in {doc}' + } + }, + chunter.ids.DMNotification + ) + + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + label: chunter.string.Message, + generated: false, + hidden: false, + txClasses: [core.class.TxCreateDoc], + objectClass: chunter.class.Message, + providers: { + [notification.providers.PlatformNotification]: true + }, + group: chunter.ids.ChunterNotificationGroup + }, + chunter.ids.ThreadNotification + ) + + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + label: chunter.string.ThreadMessage, + generated: false, + hidden: false, + txClasses: [core.class.TxCreateDoc], + objectClass: chunter.class.ThreadMessage, + providers: { + [notification.providers.PlatformNotification]: true + }, + group: chunter.ids.ChunterNotificationGroup + }, + chunter.ids.ChannelNotification + ) + createAction(builder, { ...viewTemplates.open, target: chunter.class.Channel, diff --git a/models/chunter/src/plugin.ts b/models/chunter/src/plugin.ts index 940b3b72b8..ccde071b3e 100644 --- a/models/chunter/src/plugin.ts +++ b/models/chunter/src/plugin.ts @@ -16,7 +16,8 @@ import type { DisplayTx, TxViewlet } from '@hcengineering/activity' import { Channel, chunterId } from '@hcengineering/chunter' import chunter from '@hcengineering/chunter-resources/src/plugin' -import type { Ref, Space, Doc } from '@hcengineering/core' +import type { Doc, Ref, Space } from '@hcengineering/core' +import { NotificationGroup } from '@hcengineering/notification' import type { IntlString, Resource } from '@hcengineering/platform' import { mergeIds } from '@hcengineering/platform' import type { AnyComponent, Location } from '@hcengineering/ui' @@ -68,6 +69,7 @@ export default mergeIds(chunterId, chunter, { Edit: '' as IntlString, MarkUnread: '' as IntlString, LastMessage: '' as IntlString, + MentionNotification: '' as IntlString, PinnedMessages: '' as IntlString, SavedMessages: '' as IntlString, ThreadMessage: '' as IntlString, @@ -75,6 +77,8 @@ export default mergeIds(chunterId, chunter, { Emoji: '' as IntlString, FilterComments: '' as IntlString, FilterBacklinks: '' as IntlString, + DM: '' as IntlString, + DMNotification: '' as IntlString, ConfigLabel: '' as IntlString, ConfigDescription: '' as IntlString }, @@ -86,7 +90,8 @@ export default mergeIds(chunterId, chunter, { TxBacklinkCreate: '' as Ref, TxCommentRemove: '' as Ref, TxBacklinkRemove: '' as Ref, - TxMessageCreate: '' as Ref + TxMessageCreate: '' as Ref, + ChunterNotificationGroup: '' as Ref }, activity: { TxCommentCreate: '' as AnyComponent, diff --git a/models/hr/src/index.ts b/models/hr/src/index.ts index 10f8279762..b861edf6a0 100644 --- a/models/hr/src/index.ts +++ b/models/hr/src/index.ts @@ -434,15 +434,36 @@ export function createModel (builder: Builder): void { presenter: hr.component.RequestPresenter }) + builder.createDoc( + notification.class.NotificationGroup, + core.space.Model, + { + label: hr.string.HRApplication, + icon: hr.icon.HR + }, + hr.ids.HRNotificationGroup + ) + builder.createDoc( notification.class.NotificationType, core.space.Model, { - hidden: true, - label: hr.string.Request, - textTemplate: 'New request: {doc}', - htmlTemplate: 'New request: {doc}', - subjectTemplate: 'New request' + hidden: false, + generated: false, + label: hr.string.RequestCreated, + group: hr.ids.HRNotificationGroup, + // will be created with different trigger + txClasses: [], + objectClass: hr.class.Request, + providers: { + [notification.providers.EmailNotification]: true, + [notification.providers.PlatformNotification]: true + }, + templates: { + textTemplate: 'New request: {doc}', + htmlTemplate: 'New request: {doc}', + subjectTemplate: 'New request' + } }, hr.ids.CreateRequestNotification ) @@ -451,11 +472,22 @@ export function createModel (builder: Builder): void { notification.class.NotificationType, core.space.Model, { - hidden: true, - label: hr.string.Request, - textTemplate: 'Request updated: {doc}', - htmlTemplate: 'Request updated: {doc}', - subjectTemplate: 'Request updated' + hidden: false, + generated: false, + group: hr.ids.HRNotificationGroup, + label: hr.string.RequestUpdated, + // will be created with different trigger + txClasses: [], + objectClass: hr.class.Request, + providers: { + [notification.providers.EmailNotification]: true, + [notification.providers.PlatformNotification]: true + }, + templates: { + textTemplate: 'Request updated: {doc}', + htmlTemplate: 'Request updated: {doc}', + subjectTemplate: 'Request updated' + } }, hr.ids.UpdateRequestNotification ) @@ -464,11 +496,22 @@ export function createModel (builder: Builder): void { notification.class.NotificationType, core.space.Model, { - hidden: true, - label: hr.string.Request, - textTemplate: 'Request removed: {doc}', - htmlTemplate: 'Request removed: {doc}', - subjectTemplate: 'Request removed' + hidden: false, + group: hr.ids.HRNotificationGroup, + generated: false, + label: hr.string.RequestRemoved, + // will be created with different trigger + txClasses: [], + objectClass: hr.class.Request, + providers: { + [notification.providers.EmailNotification]: true, + [notification.providers.PlatformNotification]: true + }, + templates: { + textTemplate: 'Request removed: {doc}', + htmlTemplate: 'Request removed: {doc}', + subjectTemplate: 'Request removed' + } }, hr.ids.RemoveRequestNotification ) @@ -477,11 +520,22 @@ export function createModel (builder: Builder): void { notification.class.NotificationType, core.space.Model, { - hidden: true, + hidden: false, + generated: false, + group: hr.ids.HRNotificationGroup, label: hr.string.PublicHoliday, - textTemplate: 'New public holiday: {doc}', - htmlTemplate: 'New public holiday: {doc}', - subjectTemplate: 'New public holiday' + // will be created with different trigger + txClasses: [], + objectClass: hr.class.PublicHoliday, + providers: { + [notification.providers.EmailNotification]: true, + [notification.providers.PlatformNotification]: true + }, + templates: { + textTemplate: 'New public holiday: {doc}', + htmlTemplate: 'New public holiday: {doc}', + subjectTemplate: 'New public holiday' + } }, hr.ids.CreatePublicHolidayNotification ) diff --git a/models/hr/src/plugin.ts b/models/hr/src/plugin.ts index dcffec6602..8289ac2651 100644 --- a/models/hr/src/plugin.ts +++ b/models/hr/src/plugin.ts @@ -16,6 +16,7 @@ import { Ref } from '@hcengineering/core' import { hrId } from '@hcengineering/hr' import hr from '@hcengineering/hr-resources/src/plugin' +import { NotificationGroup } from '@hcengineering/notification' import { IntlString, mergeIds } from '@hcengineering/platform' import { AnyComponent } from '@hcengineering/ui' import { Action, ActionCategory, ViewAction } from '@hcengineering/view' @@ -34,6 +35,9 @@ export default mergeIds(hrId, hr, { Overtime2: '' as IntlString, Subscribers: '' as IntlString, PublicHoliday: '' as IntlString, + RequestCreated: '' as IntlString, + RequestUpdated: '' as IntlString, + RequestRemoved: '' as IntlString, ConfigLabel: '' as IntlString, ConfigDescription: '' as IntlString }, @@ -60,5 +64,8 @@ export default mergeIds(hrId, hr, { }, actionImpl: { EditRequestType: '' as ViewAction + }, + ids: { + HRNotificationGroup: '' as Ref } }) diff --git a/models/lead/package.json b/models/lead/package.json index ded9161b6f..06463fa43e 100644 --- a/models/lead/package.json +++ b/models/lead/package.json @@ -41,6 +41,8 @@ "@hcengineering/setting": "^0.6.5", "@hcengineering/view": "^0.6.4", "@hcengineering/task": "^0.6.6", + "@hcengineering/model-notification": "^0.6.0", + "@hcengineering/notification": "^0.6.10", "@hcengineering/model-task": "^0.6.0", "@hcengineering/workbench": "^0.6.4" } diff --git a/models/lead/src/index.ts b/models/lead/src/index.ts index ee54d15bf2..b8078b3704 100644 --- a/models/lead/src/index.ts +++ b/models/lead/src/index.ts @@ -24,6 +24,7 @@ import { Mixin, Model, Prop, + ReadOnly, TypeMarkup, TypeRef, TypeString, @@ -38,6 +39,8 @@ import view, { createAction, actionTemplates as viewTemplates } from '@hcenginee import workbench from '@hcengineering/model-workbench' import setting from '@hcengineering/setting' import { ViewOptionsModel } from '@hcengineering/view' +import { generateClassNotificationTypes } from '@hcengineering/model-notification' +import notification from '@hcengineering/notification' import lead from './plugin' export { leadId } from '@hcengineering/lead' @@ -62,6 +65,7 @@ export class TFunnel extends TSpaceWithStates implements Funnel { @UX(lead.string.Lead, lead.icon.Lead, undefined, 'title') export class TLead extends TTask implements Lead { @Prop(TypeRef(contact.class.Contact), lead.string.Customer) + @ReadOnly() declare attachedTo: Ref @Prop(TypeString(), lead.string.Title) @@ -287,6 +291,49 @@ export function createModel (builder: Builder): void { } } + builder.createDoc( + notification.class.NotificationGroup, + core.space.Model, + { + label: lead.string.Lead, + icon: lead.icon.Lead, + objectClass: lead.class.Lead + }, + lead.ids.LeadNotificationGroup + ) + + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + hidden: false, + generated: false, + label: task.string.AssignedToMe, + group: lead.ids.LeadNotificationGroup, + field: 'assignee', + txClasses: [core.class.TxCreateDoc, core.class.TxUpdateDoc], + objectClass: lead.class.Lead, + templates: { + textTemplate: '{doc} was assigned to you by {sender}', + htmlTemplate: '

{doc} was assigned to you by {sender}

', + subjectTemplate: '{doc} was assigned to you' + }, + providers: { + [notification.providers.PlatformNotification]: true, + [notification.providers.EmailNotification]: true + } + }, + lead.ids.AssigneeNotification + ) + + generateClassNotificationTypes( + builder, + lead.class.Lead, + lead.ids.LeadNotificationGroup, + [], + ['comments', 'state', 'doneState'] + ) + builder.createDoc( view.class.Viewlet, core.space.Model, diff --git a/models/lead/src/plugin.ts b/models/lead/src/plugin.ts index 535e930475..2c4b97a97e 100644 --- a/models/lead/src/plugin.ts +++ b/models/lead/src/plugin.ts @@ -16,6 +16,7 @@ import type { Ref, Space } from '@hcengineering/core' import { leadId } from '@hcengineering/lead' +import { NotificationGroup, NotificationType } from '@hcengineering/notification' import lead from '@hcengineering/lead-resources/src/plugin' import type { IntlString } from '@hcengineering/platform' import { mergeIds } from '@hcengineering/platform' @@ -63,5 +64,9 @@ export default mergeIds(leadId, lead, { }, action: { CreateGlobalLead: '' as Ref + }, + ids: { + LeadNotificationGroup: '' as Ref, + AssigneeNotification: '' as Ref } }) diff --git a/models/notification/package.json b/models/notification/package.json index 0000cd41a6..7f87bc318a 100644 --- a/models/notification/package.json +++ b/models/notification/package.json @@ -30,6 +30,7 @@ "@hcengineering/ui": "^0.6.6", "@hcengineering/platform": "^0.6.8", "@hcengineering/model-core": "^0.6.0", + "@hcengineering/model-preference": "^0.6.0", "@hcengineering/model-view": "^0.6.0", "@hcengineering/view": "^0.6.4", "@hcengineering/workbench": "^0.6.4", diff --git a/models/notification/src/index.ts b/models/notification/src/index.ts index a577cc703a..c444aadd16 100644 --- a/models/notification/src/index.ts +++ b/models/notification/src/index.ts @@ -14,25 +14,42 @@ // limitations under the License. // -import { Account, Class, Doc, Domain, DOMAIN_MODEL, IndexKind, Ref, Timestamp, TxCUD } from '@hcengineering/core' +import { + Account, + AttachedDoc, + Class, + Collection, + Data, + Doc, + Domain, + DOMAIN_MODEL, + Hierarchy, + IndexKind, + Ref, + Timestamp, + Tx, + TxCUD +} from '@hcengineering/core' import { ArrOf, Builder, Index, Mixin, Model, Prop, TypeRef, TypeString, UX } from '@hcengineering/model' import core, { TAttachedDoc, TClass, TDoc } from '@hcengineering/model-core' +import preference, { TPreference } from '@hcengineering/model-preference' import view, { createAction } from '@hcengineering/model-view' import { - AnotherUserNotifications, DocUpdates, EmailNotification, LastView, Notification, + NotificationGroup, notificationId, NotificationObjectPresenter, NotificationProvider, NotificationSetting, NotificationStatus, + NotificationTemplate, NotificationType, SpaceLastEdit } from '@hcengineering/notification' -import type { IntlString } from '@hcengineering/platform' +import type { Asset, IntlString } from '@hcengineering/platform' import setting from '@hcengineering/setting' import { AnyComponent } from '@hcengineering/ui' import workbench from '@hcengineering/workbench' @@ -89,11 +106,22 @@ export class TEmaiNotification extends TDoc implements EmailNotification { @Model(notification.class.NotificationType, core.class.Doc, DOMAIN_MODEL) export class TNotificationType extends TDoc implements NotificationType { + generated!: boolean label!: IntlString - textTemplate!: string - htmlTemplate!: string - subjectTemplate!: string + group!: Ref + txClasses!: Ref>[] + providers!: Record, boolean> + objectClass!: Ref> hidden!: boolean + templates?: NotificationTemplate +} + +@Model(notification.class.NotificationGroup, core.class.Doc, DOMAIN_MODEL) +export class TNotificationGroup extends TDoc implements NotificationGroup { + label!: IntlString + icon!: Asset + // using for autogenerated settings + objectClass?: Ref> } @Model(notification.class.NotificationProvider, core.class.Doc, DOMAIN_MODEL) @@ -102,10 +130,10 @@ export class TNotificationProvider extends TDoc implements NotificationProvider default!: boolean } -@Model(notification.class.NotificationSetting, core.class.Doc, DOMAIN_NOTIFICATION) -export class TNotificationSetting extends TDoc implements NotificationSetting { +@Model(notification.class.NotificationSetting, preference.class.Preference) +export class TNotificationSetting extends TPreference implements NotificationSetting { + attachedTo!: Ref type!: Ref - provider!: Ref enabled!: boolean } @@ -114,11 +142,6 @@ export class TSpaceLastEdit extends TClass implements SpaceLastEdit { lastEditField!: string } -@Mixin(notification.mixin.AnotherUserNotifications, core.class.Class) -export class TAnotherUserNotifications extends TClass implements AnotherUserNotifications { - fields!: string[] -} - @Mixin(notification.mixin.ClassCollaborators, core.class.Class) export class TClassCollaborators extends TClass { fields!: string[] @@ -165,8 +188,8 @@ export function createModel (builder: Builder): void { TNotificationType, TNotificationProvider, TNotificationSetting, + TNotificationGroup, TSpaceLastEdit, - TAnotherUserNotifications, TClassCollaborators, TTrackedDoc, TCollaborators, @@ -174,32 +197,6 @@ export function createModel (builder: Builder): void { TNotificationObjectPresenter ) - builder.createDoc( - notification.class.NotificationType, - core.space.Model, - { - label: notification.string.MentionNotification, - hidden: false, - textTemplate: '{sender} mentioned you in {doc} {data}', - htmlTemplate: '

{sender} mentioned you in {doc}

{data}', - subjectTemplate: 'You were mentioned in {doc}' - }, - notification.ids.MentionNotification - ) - - builder.createDoc( - notification.class.NotificationType, - core.space.Model, - { - label: notification.string.DM, - hidden: false, - textTemplate: '{sender} has send you a message: {doc} {data}', - htmlTemplate: '

{sender} has send you a message {doc}

{data}', - subjectTemplate: 'You have new DM message in {doc}' - }, - notification.ids.DMNotification - ) - // Temporarily disabled, we should think about it // builder.createDoc( // notification.class.NotificationProvider, @@ -215,10 +212,18 @@ export function createModel (builder: Builder): void { notification.class.NotificationProvider, core.space.Model, { - label: notification.string.EmailNotification, - default: true + label: notification.string.Inbox }, - notification.ids.EmailNotification + notification.providers.PlatformNotification + ) + + builder.createDoc( + notification.class.NotificationProvider, + core.space.Model, + { + label: notification.string.EmailNotification + }, + notification.providers.EmailNotification ) builder.createDoc( @@ -300,3 +305,49 @@ export function createModel (builder: Builder): void { actions: [view.action.Delete, view.action.Open] }) } + +export function generateClassNotificationTypes ( + builder: Builder, + _class: Ref>, + group: Ref, + ignoreKeys: string[] = [], + defaultEnabled: string[] = [] +): void { + const txes = builder.getTxes() + const hierarchy = new Hierarchy() + for (const tx of txes) { + hierarchy.tx(tx) + } + const attributes = hierarchy.getAllAttributes( + _class, + hierarchy.isDerived(_class, core.class.AttachedDoc) ? core.class.AttachedDoc : core.class.Doc + ) + const filtered = Array.from(attributes.values()).filter((p) => p.hidden !== true && p.readonly !== true) + for (const attribute of filtered) { + if (ignoreKeys.includes(attribute.name)) continue + const isCollection: boolean = core.class.Collection === attribute.type._class + const objectClass = !isCollection ? _class : (attribute.type as Collection).of + const txClasses = !isCollection + ? hierarchy.isMixin(attribute.attributeOf) + ? [core.class.TxMixin] + : [core.class.TxUpdateDoc] + : [core.class.TxCreateDoc, core.class.TxRemoveDoc] + const data: Data = { + attribute: attribute._id, + group, + generated: true, + objectClass, + txClasses, + hidden: false, + providers: { + [notification.providers.PlatformNotification]: defaultEnabled.includes(attribute.name) + }, + label: attribute.label + } + if (isCollection) { + data.attachedToClass = _class + } + const id = `${notification.class.NotificationType}_${_class}_${attribute.name}` as Ref + builder.createDoc(notification.class.NotificationType, core.space.Model, data, id) + } +} diff --git a/models/notification/src/migration.ts b/models/notification/src/migration.ts index 91b24985a2..dd4781f57d 100644 --- a/models/notification/src/migration.ts +++ b/models/notification/src/migration.ts @@ -17,17 +17,18 @@ import core, { Account, AttachedDoc, Class, + Collection, + Data, Doc, DOMAIN_TX, generateId, Ref, TxCollectionCUD, - TxCreateDoc, TxOperations, TxRemoveDoc } from '@hcengineering/core' import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@hcengineering/model' -import notification, { LastView, Notification, NotificationType } from '@hcengineering/notification' +import notification, { LastView, NotificationType } from '@hcengineering/notification' import { DOMAIN_NOTIFICATION } from '.' async function fillNotificationText (client: MigrationClient): Promise { @@ -51,23 +52,11 @@ async function fillNotificationText (client: MigrationClient): Promise { ) } -async function fillNotificationType (client: MigrationUpgradeClient): Promise { - const notifications = await client.findAll(notification.class.Notification, { type: { $exists: false } }) - const txOp = new TxOperations(client, core.account.System) - const promises = notifications.map(async (doc) => { - const tx = await client.findOne(core.class.TxCUD, { _id: doc.tx }) - if (tx === undefined) return - const type = - tx._class === core.class.TxMixin - ? ('calendar:ids:ReminderNotification' as Ref) - : notification.ids.MentionNotification - const objectTx = txOp.update(doc, { type }) - const ctx = await client.findOne>(core.class.TxCreateDoc, { objectId: doc._id }) - if (ctx === undefined) return await objectTx - const updateTx = txOp.update(ctx, { 'attributes.type': type } as any) - return await Promise.all([objectTx, updateTx]) - }) - await Promise.all(promises) +async function removeSettings (client: MigrationClient): Promise { + const outdatedSettings = await client.find(DOMAIN_NOTIFICATION, { _class: notification.class.NotificationSetting }) + for (const setting of outdatedSettings) { + await client.delete(DOMAIN_NOTIFICATION, setting._id) + } } async function createSpace (client: MigrationUpgradeClient): Promise { @@ -197,15 +186,66 @@ async function fillDocUpdatesHidder (client: MigrationClient): Promise { ) } +async function createCustomFieldTypes (client: MigrationUpgradeClient): Promise { + const txop = new TxOperations(client, core.account.System) + const attributes = await client.findAll(core.class.Attribute, { isCustom: true }) + const groups = new Map( + (await client.findAll(notification.class.NotificationGroup, {})) + .filter((p) => p.objectClass !== undefined) + .map((p) => [p.objectClass, p]) + ) + const types = new Set((await client.findAll(notification.class.NotificationType, {})).map((p) => p.attribute)) + for (const attribute of attributes) { + if (attribute.hidden === true || attribute.readonly === true) continue + if (types.has(attribute._id)) continue + const group = groups.get(attribute.attributeOf) + if (group === undefined) continue + const isCollection: boolean = core.class.Collection === attribute.type._class + const _class = attribute.attributeOf + const objectClass = !isCollection ? _class : (attribute.type as Collection).of + const txClasses = !isCollection + ? [client.getHierarchy().isMixin(_class) ? core.class.TxMixin : core.class.TxUpdateDoc] + : [core.class.TxCreateDoc, core.class.TxRemoveDoc] + const data: Data = { + attribute: attribute._id, + group: group._id, + generated: true, + objectClass, + txClasses, + hidden: false, + providers: { + [notification.providers.PlatformNotification]: false + }, + label: attribute.label + } + if (isCollection) { + data.attachedToClass = _class + } + const id = `${notification.class.NotificationType}_${_class}_${attribute.name}` as Ref + await txop.createDoc(notification.class.NotificationType, core.space.Model, data, id) + } +} + +async function cleanOutdatedSettings (client: MigrationClient): Promise { + const res = await client.find(DOMAIN_NOTIFICATION, { + _class: notification.class.NotificationSetting + }) + for (const value of res) { + await client.delete(DOMAIN_NOTIFICATION, value._id) + } +} + export const notificationOperation: MigrateOperation = { async migrate (client: MigrationClient): Promise { + await removeSettings(client) await fillNotificationText(client) await migrateLastView(client) await fillCollaborators(client) await fillDocUpdatesHidder(client) + await cleanOutdatedSettings(client) }, async upgrade (client: MigrationUpgradeClient): Promise { await createSpace(client) - await fillNotificationType(client) + await createCustomFieldTypes(client) } } diff --git a/models/notification/src/plugin.ts b/models/notification/src/plugin.ts index 01cfede8e2..8e0916b704 100644 --- a/models/notification/src/plugin.ts +++ b/models/notification/src/plugin.ts @@ -24,9 +24,6 @@ import { Application } from '@hcengineering/workbench' export default mergeIds(notificationId, notification, { string: { LastView: '' as IntlString, - DM: '' as IntlString, - DMNotification: '' as IntlString, - MentionNotification: '' as IntlString, PlatformNotification: '' as IntlString, BrowserNotification: '' as IntlString, EmailNotification: '' as IntlString, @@ -46,6 +43,7 @@ export default mergeIds(notificationId, notification, { category: { Notification: '' as Ref }, + groups: {}, action: { Unsubscribe: '' as Ref, Hide: '' as Ref, diff --git a/models/recruit/package.json b/models/recruit/package.json index af83b9cfca..83861ced64 100644 --- a/models/recruit/package.json +++ b/models/recruit/package.json @@ -47,6 +47,7 @@ "@hcengineering/workbench": "^0.6.4", "@hcengineering/model-tracker": "^0.6.0", "@hcengineering/model-presentation": "^0.6.0", + "@hcengineering/model-notification": "^0.6.0", "@hcengineering/model-calendar": "^0.6.0", "@hcengineering/model-tags": "^0.6.0", "@anticrm/skillset": "^0.6.0" diff --git a/models/recruit/src/index.ts b/models/recruit/src/index.ts index b6d0a2c00a..f60f031f9f 100644 --- a/models/recruit/src/index.ts +++ b/models/recruit/src/index.ts @@ -59,6 +59,7 @@ import { KeyBinding, ViewOptionsModel } from '@hcengineering/view' import recruit from './plugin' import { createReviewModel, reviewTableConfig, reviewTableOptions } from './review' import { TOpinion, TReview } from './review-model' +import { generateClassNotificationTypes } from '@hcengineering/model-notification' export { recruitId } from '@hcengineering/recruit' export { recruitOperation } from './migration' @@ -87,8 +88,7 @@ export class TVacancy extends TSpaceWithStates implements Vacancy { @Prop(Collection(chunter.class.Comment), chunter.string.Comments) comments?: number - @Prop(Collection(chunter.class.Backlink), chunter.string.Comments) - relations!: number + relations!: number @Prop(TypeString(), recruit.string.Vacancy) @Index(IndexKind.FullText) @@ -151,6 +151,7 @@ export class TApplicant extends TTask implements Applicant { // We need to declare, to provide property with label @Prop(TypeRef(recruit.mixin.Candidate), recruit.string.Talent) @Index(IndexKind.Indexed) + @ReadOnly() declare attachedTo: Ref // We need to declare, to provide property with label @@ -329,7 +330,7 @@ export function createModel (builder: Builder): void { }, { id: assignedId, - label: task.string.Assigned, + label: task.string.AssignedToMe, icon: recruit.icon.AssignedToMe, component: task.component.AssignedTasks, position: 'event', @@ -1086,6 +1087,81 @@ export function createModel (builder: Builder): void { component: recruit.component.ApplicantFilter }) + builder.createDoc( + notification.class.NotificationGroup, + core.space.Model, + { + label: recruit.string.Application, + icon: recruit.icon.Application, + objectClass: recruit.class.Applicant + }, + recruit.ids.ApplicationNotificationGroup + ) + + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + hidden: false, + generated: false, + label: task.string.AssignedToMe, + group: recruit.ids.ApplicationNotificationGroup, + field: 'assignee', + txClasses: [core.class.TxCreateDoc, core.class.TxUpdateDoc], + objectClass: recruit.class.Applicant, + templates: { + textTemplate: '{doc} was assigned to you by {sender}', + htmlTemplate: '

{doc} was assigned to you by {sender}

', + subjectTemplate: '{doc} was assigned to you' + }, + providers: { + [notification.providers.PlatformNotification]: true, + [notification.providers.EmailNotification]: true + } + }, + recruit.ids.AssigneeNotification + ) + + generateClassNotificationTypes( + builder, + recruit.class.Applicant, + recruit.ids.ApplicationNotificationGroup, + [], + ['comments', 'state', 'doneState'] + ) + + builder.createDoc( + notification.class.NotificationGroup, + core.space.Model, + { + label: recruit.string.Vacancy, + icon: recruit.icon.Vacancy, + objectClass: recruit.class.Vacancy + }, + recruit.ids.VacancyNotificationGroup + ) + + generateClassNotificationTypes(builder, recruit.class.Vacancy, recruit.ids.VacancyNotificationGroup, [], ['comments']) + + builder.createDoc( + notification.class.NotificationGroup, + core.space.Model, + { + label: recruit.string.Talent, + icon: recruit.icon.CreateCandidate, + objectClass: recruit.mixin.Candidate + }, + recruit.ids.CandidateNotificationGroup + ) + + generateClassNotificationTypes( + builder, + recruit.mixin.Candidate, + recruit.ids.CandidateNotificationGroup, + ['vacancyMatch'], + ['comments'] + ) + builder.createDoc( view.class.FilterMode, core.space.Model, diff --git a/models/recruit/src/plugin.ts b/models/recruit/src/plugin.ts index 5fd1205063..5651be630c 100644 --- a/models/recruit/src/plugin.ts +++ b/models/recruit/src/plugin.ts @@ -14,6 +14,7 @@ // import type { Client, Doc, Ref } from '@hcengineering/core' +import { NotificationGroup, NotificationType } from '@hcengineering/notification' import type { IntlString, Resource, Status } from '@hcengineering/platform' import { mergeIds } from '@hcengineering/platform' import { recruitId } from '@hcengineering/recruit' @@ -67,6 +68,12 @@ export default mergeIds(recruitId, recruit, { validator: { ApplicantValidator: '' as Resource<(doc: T, client: Client) => Promise> }, + ids: { + VacancyNotificationGroup: '' as Ref, + CandidateNotificationGroup: '' as Ref, + ApplicationNotificationGroup: '' as Ref, + AssigneeNotification: '' as Ref + }, component: { CreateApplication: '' as AnyComponent, KanbanCard: '' as AnyComponent, diff --git a/models/server-chunter/package.json b/models/server-chunter/package.json index 2f24595e09..4f611253e2 100644 --- a/models/server-chunter/package.json +++ b/models/server-chunter/package.json @@ -31,6 +31,7 @@ "@hcengineering/server-chunter": "^0.6.0", "@hcengineering/server-core": "^0.6.1", "@hcengineering/chunter": "^0.6.5", + "@hcengineering/notification": "^0.6.10", "@hcengineering/server-notification": "^0.6.0" } } diff --git a/models/server-chunter/src/index.ts b/models/server-chunter/src/index.ts index 58632be858..3b981820e1 100644 --- a/models/server-chunter/src/index.ts +++ b/models/server-chunter/src/index.ts @@ -20,6 +20,7 @@ import chunter from '@hcengineering/chunter' import serverNotification from '@hcengineering/server-notification' import serverCore, { ObjectDDParticipant } from '@hcengineering/server-core' import serverChunter from '@hcengineering/server-chunter' +import notification from '@hcengineering/notification' export { serverChunterId } from '@hcengineering/server-chunter' export function createModel (builder: Builder): void { @@ -44,7 +45,16 @@ export function createModel (builder: Builder): void { trigger: serverChunter.trigger.ChunterTrigger }) - builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: serverChunter.trigger.DMTrigger + builder.mixin(chunter.ids.DMNotification, notification.class.NotificationType, serverNotification.mixin.TypeMatch, { + func: serverChunter.function.IsDirectMessagee }) + + builder.mixin( + chunter.ids.ChannelNotification, + notification.class.NotificationType, + serverNotification.mixin.TypeMatch, + { + func: serverChunter.function.IsChannelMessagee + } + ) } diff --git a/models/server-lead/package.json b/models/server-lead/package.json index 2925cf9b7f..148792b258 100644 --- a/models/server-lead/package.json +++ b/models/server-lead/package.json @@ -31,6 +31,8 @@ "@hcengineering/server-lead": "^0.6.0", "@hcengineering/server-core": "^0.6.1", "@hcengineering/lead": "^0.6.0", + "@hcengineering/model-lead": "^0.6.0", + "@hcengineering/notification": "^0.6.10", "@hcengineering/server-notification": "^0.6.0" } } diff --git a/models/server-lead/src/index.ts b/models/server-lead/src/index.ts index 43f709ba4c..3ac8e011ba 100644 --- a/models/server-lead/src/index.ts +++ b/models/server-lead/src/index.ts @@ -16,8 +16,8 @@ import { Builder } from '@hcengineering/model' import core from '@hcengineering/core' -import lead from '@hcengineering/lead' -import serverCore from '@hcengineering/server-core' +import lead from '@hcengineering/model-lead' +import notification from '@hcengineering/notification' import serverLead from '@hcengineering/server-lead' import serverNotification from '@hcengineering/server-notification' @@ -32,7 +32,12 @@ export function createModel (builder: Builder): void { presenter: serverLead.function.LeadTextPresenter }) - builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: serverLead.trigger.OnLeadUpdate - }) + builder.mixin( + lead.ids.AssigneeNotification, + notification.class.NotificationType, + serverNotification.mixin.TypeMatch, + { + func: serverNotification.function.IsUserInFieldValue + } + ) } diff --git a/models/server-notification/package.json b/models/server-notification/package.json index 5c0e8639e4..941115d3d7 100644 --- a/models/server-notification/package.json +++ b/models/server-notification/package.json @@ -29,6 +29,8 @@ "@hcengineering/model": "^0.6.2", "@hcengineering/model-core": "^0.6.0", "@hcengineering/platform": "^0.6.8", + "@hcengineering/notification": "^0.6.10", + "@hcengineering/model-notification": "^0.6.0", "@hcengineering/server-notification": "^0.6.0", "@hcengineering/server-core": "^0.6.1" } diff --git a/models/server-notification/src/index.ts b/models/server-notification/src/index.ts index 2073649dcf..98966531bc 100644 --- a/models/server-notification/src/index.ts +++ b/models/server-notification/src/index.ts @@ -16,11 +16,18 @@ import { Builder, Mixin } from '@hcengineering/model' -import core from '@hcengineering/core' +import core, { Account, Doc, Ref, Tx } from '@hcengineering/core' import { TClass } from '@hcengineering/model-core' +import { TNotificationType } from '@hcengineering/model-notification' +import notification, { NotificationType } from '@hcengineering/notification' import { Resource } from '@hcengineering/platform' -import serverCore from '@hcengineering/server-core' -import serverNotification, { HTMLPresenter, Presenter, TextPresenter } from '@hcengineering/server-notification' +import serverCore, { TriggerControl } from '@hcengineering/server-core' +import serverNotification, { + HTMLPresenter, + Presenter, + TextPresenter, + TypeMatch +} from '@hcengineering/server-notification' export { serverNotificationId } from '@hcengineering/server-notification' @@ -34,8 +41,15 @@ export class TTextPresenter extends TClass implements TextPresenter { presenter!: Resource } +@Mixin(serverNotification.mixin.TypeMatch, notification.class.NotificationType) +export class TTypeMatch extends TNotificationType implements TypeMatch { + func!: Resource< + (tx: Tx, doc: Doc, user: Ref, type: NotificationType, control: TriggerControl) => Promise + > +} + export function createModel (builder: Builder): void { - builder.createModel(THTMLPresenter, TTextPresenter) + builder.createModel(THTMLPresenter, TTextPresenter, TTypeMatch) builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverNotification.trigger.OnBacklinkCreate @@ -56,4 +70,12 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverNotification.trigger.OnAddCollborator }) + + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverNotification.trigger.OnAttributeCreate + }) + + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverNotification.trigger.OnAttributeUpdate + }) } diff --git a/models/server-recruit/package.json b/models/server-recruit/package.json index c1360dafaf..764bb0414f 100644 --- a/models/server-recruit/package.json +++ b/models/server-recruit/package.json @@ -30,7 +30,8 @@ "@hcengineering/platform": "^0.6.8", "@hcengineering/server-recruit": "^0.6.0", "@hcengineering/server-core": "^0.6.1", - "@hcengineering/recruit": "^0.6.11", + "@hcengineering/model-recruit": "^0.6.0", + "@hcengineering/notification": "^0.6.10", "@hcengineering/server-notification": "^0.6.0" } } diff --git a/models/server-recruit/src/index.ts b/models/server-recruit/src/index.ts index 5cfb1cebea..c4e504de5b 100644 --- a/models/server-recruit/src/index.ts +++ b/models/server-recruit/src/index.ts @@ -16,7 +16,8 @@ import { Builder } from '@hcengineering/model' import core from '@hcengineering/core' -import recruit from '@hcengineering/recruit' +import recruit from '@hcengineering/model-recruit' +import notification from '@hcengineering/notification' import serverCore from '@hcengineering/server-core' import serverNotification from '@hcengineering/server-notification' import serverRecruit from '@hcengineering/server-recruit' @@ -43,4 +44,13 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverRecruit.trigger.OnRecruitUpdate }) + + builder.mixin( + recruit.ids.AssigneeNotification, + notification.class.NotificationType, + serverNotification.mixin.TypeMatch, + { + func: serverNotification.function.IsUserInFieldValue + } + ) } diff --git a/models/server-tracker/package.json b/models/server-tracker/package.json index c19ccade1e..a4ec5f2707 100644 --- a/models/server-tracker/package.json +++ b/models/server-tracker/package.json @@ -29,7 +29,9 @@ "@hcengineering/model": "^0.6.2", "@hcengineering/tracker": "^0.6.6", "@hcengineering/server-core": "^0.6.1", + "@hcengineering/notification": "^0.6.10", "@hcengineering/server-notification": "^0.6.0", + "@hcengineering/model-tracker": "^0.6.0", "@hcengineering/server-tracker": "^0.6.0" } } diff --git a/models/server-tracker/src/index.ts b/models/server-tracker/src/index.ts index 26f2eed700..4496a0d28c 100644 --- a/models/server-tracker/src/index.ts +++ b/models/server-tracker/src/index.ts @@ -15,10 +15,11 @@ import core from '@hcengineering/core' import { Builder } from '@hcengineering/model' +import tracker from '@hcengineering/model-tracker' +import notification from '@hcengineering/notification' import serverCore from '@hcengineering/server-core' import serverNotification from '@hcengineering/server-notification' import serverTracker from '@hcengineering/server-tracker' -import tracker from '@hcengineering/tracker' export { serverTrackerId } from '@hcengineering/server-tracker' @@ -38,4 +39,13 @@ export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverTracker.trigger.OnComponentRemove }) + + builder.mixin( + tracker.ids.AssigneeNotification, + notification.class.NotificationType, + serverNotification.mixin.TypeMatch, + { + func: serverNotification.function.IsUserInFieldValue + } + ) } diff --git a/models/task/src/index.ts b/models/task/src/index.ts index f66c372001..afdef8b63f 100644 --- a/models/task/src/index.ts +++ b/models/task/src/index.ts @@ -322,9 +322,6 @@ export function createModel (builder: Builder): void { }) builder.mixin(task.class.Task, core.class.Class, notification.mixin.TrackedDoc, {}) - builder.mixin(task.class.Task, core.class.Class, notification.mixin.AnotherUserNotifications, { - fields: ['assignee'] - }) builder.createDoc( view.class.ActionCategory, @@ -487,16 +484,16 @@ export function createModel (builder: Builder): void { task.action.ArchiveState ) - builder.createDoc( - notification.class.NotificationType, - core.space.Model, - { - label: task.string.Assigned, - hidden: false, - textTemplate: '{doc} was assigned to you by {sender}', - htmlTemplate: '

{doc} was assigned to you by {sender}

', - subjectTemplate: '{doc} was assigned to you' - }, - task.ids.AssigneedNotification - ) + // builder.createDoc( + // notification.class.NotificationType, + // core.space.Model, + // { + // label: task.string.Assigned, + // hidden: false, + // textTemplate: '{doc} was assigned to you by {sender}', + // htmlTemplate: '

{doc} was assigned to you by {sender}

', + // subjectTemplate: '{doc} was assigned to you' + // }, + // task.ids.AssigneedNotification + // ) } diff --git a/models/tracker/package.json b/models/tracker/package.json index 42863e97a9..69cc0c58af 100644 --- a/models/tracker/package.json +++ b/models/tracker/package.json @@ -37,6 +37,7 @@ "@hcengineering/model-contact": "^0.6.1", "@hcengineering/model-attachment": "^0.6.0", "@hcengineering/notification": "^0.6.10", + "@hcengineering/model-notification": "^0.6.0", "@hcengineering/tracker": "^0.6.6", "@hcengineering/tracker-resources": "^0.6.0", "@hcengineering/model-chunter": "^0.6.0", diff --git a/models/tracker/src/index.ts b/models/tracker/src/index.ts index 5105a7acc1..7f4a5c600e 100644 --- a/models/tracker/src/index.ts +++ b/models/tracker/src/index.ts @@ -80,6 +80,7 @@ import tracker from './plugin' import presentation from '@hcengineering/model-presentation' import { defaultPriorities, issuePriorities } from '@hcengineering/tracker-resources/src/types' +import { generateClassNotificationTypes } from '@hcengineering/model-notification' export { trackerId } from '@hcengineering/tracker' export { trackerOperation } from './migration' @@ -197,6 +198,7 @@ export class TIssue extends TAttachedDoc implements Issue { @Prop(TypeNumber(), tracker.string.Number) @Index(IndexKind.FullText) + @ReadOnly() number!: number @Prop(TypeRef(contact.class.Employee), tracker.string.Assignee) @@ -939,10 +941,6 @@ export function createModel (builder: Builder): void { builder.mixin(tracker.class.Issue, core.class.Class, notification.mixin.TrackedDoc, {}) - builder.mixin(tracker.class.Issue, core.class.Class, notification.mixin.AnotherUserNotifications, { - fields: ['assignee'] - }) - builder.mixin(tracker.class.TypeIssuePriority, core.class.Class, view.mixin.AllValuesFunc, { func: tracker.function.GetAllPriority }) @@ -1799,6 +1797,49 @@ export function createModel (builder: Builder): void { tracker.viewlet.SprintList ) + builder.createDoc( + notification.class.NotificationGroup, + core.space.Model, + { + label: tracker.string.Issues, + icon: tracker.icon.Issues, + objectClass: tracker.class.Issue + }, + tracker.ids.TrackerNotificationGroup + ) + + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + hidden: false, + generated: false, + label: task.string.AssignedToMe, + group: tracker.ids.TrackerNotificationGroup, + field: 'assignee', + txClasses: [core.class.TxCreateDoc, core.class.TxUpdateDoc], + objectClass: tracker.class.Issue, + templates: { + textTemplate: '{doc} was assigned to you by {sender}', + htmlTemplate: '

{doc} was assigned to you by {sender}

', + subjectTemplate: '{doc} was assigned to you' + }, + providers: { + [notification.providers.PlatformNotification]: true, + [notification.providers.EmailNotification]: true + } + }, + tracker.ids.AssigneeNotification + ) + + generateClassNotificationTypes( + builder, + tracker.class.Issue, + tracker.ids.TrackerNotificationGroup, + [], + ['comments', 'status', 'priority'] + ) + createAction( builder, { diff --git a/models/tracker/src/plugin.ts b/models/tracker/src/plugin.ts index 6912edb320..998452b932 100644 --- a/models/tracker/src/plugin.ts +++ b/models/tracker/src/plugin.ts @@ -23,6 +23,7 @@ import type { AnyComponent } from '@hcengineering/ui/src/types' import { Action, ViewAction, Viewlet } from '@hcengineering/view' import { Application } from '@hcengineering/workbench' import { TxViewlet } from '@hcengineering/activity' +import { NotificationGroup, NotificationType } from '@hcengineering/notification' export default mergeIds(trackerId, tracker, { string: { @@ -37,6 +38,7 @@ export default mergeIds(trackerId, tracker, { SearchIssue: '' as IntlString, Parent: '' as IntlString, CreatedOn: '' as IntlString, + ChangeStatus: '' as IntlString, ConfigLabel: '' as IntlString, ConfigDescription: '' as IntlString }, @@ -61,7 +63,9 @@ export default mergeIds(trackerId, tracker, { SprintList: '' as Ref }, ids: { - TxIssueCreated: '' as Ref + TxIssueCreated: '' as Ref, + TrackerNotificationGroup: '' as Ref, + AssigneeNotification: '' as Ref }, completion: { IssueQuery: '' as Resource, diff --git a/plugins/chunter-assets/lang/en.json b/plugins/chunter-assets/lang/en.json index f35c3260ee..d37bf07eba 100644 --- a/plugins/chunter-assets/lang/en.json +++ b/plugins/chunter-assets/lang/en.json @@ -25,6 +25,7 @@ "Reference": "Reference", "Chat": "Chat", "In": "In", + "MentionNotification": "Mentioned", "Replies": "Replies", "LastReply": "Last reply", "RepliesCount": "{replies, plural, =1 {# reply} other {# replies}}", @@ -66,6 +67,8 @@ "CopyLink": "Copy link", "FilterComments": "Comments", "FilterBacklinks": "Backlinks", + "DM": "Direct message", + "DMNotification": "Sent you a message", "ConfigLabel": "Chat", "ConfigDescription": "Extension to perform text communications" } diff --git a/plugins/chunter-assets/lang/ru.json b/plugins/chunter-assets/lang/ru.json index 0b7c5f8859..717c343048 100644 --- a/plugins/chunter-assets/lang/ru.json +++ b/plugins/chunter-assets/lang/ru.json @@ -22,6 +22,7 @@ "Content": "Содержимое", "Comment": "Комментарий", "Message": "Сообщение", + "MentionNotification": "Упомянул", "Reference": "Ссылка", "Chat": "Чат", "In": "в", @@ -66,6 +67,8 @@ "CopyLink": "Копировать ссылку", "FilterComments": "Коментарии", "FilterBacklinks": "Упоминания", + "DM": "Личное сообщение", + "DMNotification": "Отправил сообщение", "ConfigLabel": "Чат", "ConfigDescription": "Расширение для текстовых переписок" } diff --git a/plugins/chunter-resources/src/components/ChannelView.svelte b/plugins/chunter-resources/src/components/ChannelView.svelte index 2d3dc406ac..581738888e 100644 --- a/plugins/chunter-resources/src/components/ChannelView.svelte +++ b/plugins/chunter-resources/src/components/ChannelView.svelte @@ -40,7 +40,7 @@ _class, space, space, - chunter.class.ChunterSpace, + chunterSpace?._class ?? chunter.class.ChunterSpace, 'messages', { content: message, diff --git a/plugins/chunter/package.json b/plugins/chunter/package.json index d1d15d0774..a8dd9bb7f2 100644 --- a/plugins/chunter/package.json +++ b/plugins/chunter/package.json @@ -28,6 +28,7 @@ "dependencies": { "@hcengineering/platform": "^0.6.8", "@hcengineering/ui": "^0.6.6", + "@hcengineering/notification": "^0.6.10", "@hcengineering/contact": "^0.6.14", "@hcengineering/core": "^0.6.23", "@hcengineering/preference": "^0.6.4" diff --git a/plugins/chunter/src/index.ts b/plugins/chunter/src/index.ts index 14847b3561..fd8930c1c7 100644 --- a/plugins/chunter/src/index.ts +++ b/plugins/chunter/src/index.ts @@ -13,8 +13,9 @@ // limitations under the License. // -import type { Account, AttachedDoc, Class, Doc, Ref, RelatedDocument, Space, Timestamp } from '@hcengineering/core' import type { Employee } from '@hcengineering/contact' +import type { Account, AttachedDoc, Class, Doc, Ref, RelatedDocument, Space, Timestamp } from '@hcengineering/core' +import { NotificationType } from '@hcengineering/notification' import type { Asset, Plugin, Resource } from '@hcengineering/platform' import { IntlString, plugin } from '@hcengineering/platform' import type { Preference } from '@hcengineering/preference' @@ -160,6 +161,12 @@ export default plugin(chunterId, { resolver: { Location: '' as Resource<(loc: Location) => Promise> }, + ids: { + DMNotification: '' as Ref, + MentionNotification: '' as Ref, + ThreadNotification: '' as Ref, + ChannelNotification: '' as Ref + }, app: { Chunter: '' as Ref }, diff --git a/plugins/hr-assets/lang/en.json b/plugins/hr-assets/lang/en.json index 0e9dd75437..8b8e503442 100644 --- a/plugins/hr-assets/lang/en.json +++ b/plugins/hr-assets/lang/en.json @@ -51,6 +51,9 @@ "Export": "Export", "Separator": "Separator", "ChooseSeparator": "Choose separator", + "RequestCreated": "Request created", + "RequestUpdated": "Request updated", + "RequestRemoved": "Request removed", "ConfigLabel": "Human resources", "ConfigDescription": "Extension to manage organization structure and employee work calendar" } diff --git a/plugins/hr-assets/lang/ru.json b/plugins/hr-assets/lang/ru.json index 129143025c..8f1b4b81eb 100644 --- a/plugins/hr-assets/lang/ru.json +++ b/plugins/hr-assets/lang/ru.json @@ -51,6 +51,9 @@ "Export": "Экспортировать", "Separator": "Разделитель", "ChooseSeparator": "Выберите разделитель", + "RequestCreated": "Запрос создан", + "RequestUpdated": "Запрос изменен", + "RequestRemoved": "Запрос удален", "ConfigLabel": "Производственный календарь", "ConfigDescription": "Расширение реализующее производственный календарь" } diff --git a/plugins/notification-assets/lang/en.json b/plugins/notification-assets/lang/en.json index 3b170f5fec..daa76c562a 100644 --- a/plugins/notification-assets/lang/en.json +++ b/plugins/notification-assets/lang/en.json @@ -4,10 +4,7 @@ "Notification": "Notification", "Notifications": "Notifications", "NoNotifications": "No notifications", - "MentionNotification": "Mentioned", - "DM": "Direct message", - "DMNotification": "Sent you a message", - "EmailNotification": "by email", + "EmailNotification": "Email", "PlatformNotification": "in platform", "Track": "Track", "DontTrack": "Don't track", @@ -19,6 +16,8 @@ "MarkAsUnread": "Mark as unread", "Archive": "Archive", "Inbox": "Inbox", - "Collaborators": "Collaborators" + "Collaborators": "Collaborators", + "Change": "Change", + "AddedRemoved": "Added/removed" } } diff --git a/plugins/notification-assets/lang/ru.json b/plugins/notification-assets/lang/ru.json index c19d0e4463..fe4cf5c951 100644 --- a/plugins/notification-assets/lang/ru.json +++ b/plugins/notification-assets/lang/ru.json @@ -4,10 +4,7 @@ "Notification": "Увдомление", "Notifications": "Уведомления", "NoNotifications": "Нет уведомлений", - "MentionNotification": "Упомянул", - "DM": "Личное сообщение", - "DMNotification": "Отправил сообщение", - "EmailNotification": "по email", + "EmailNotification": "Email", "PlatformNotification": "в системе", "Track": "Отслеживать", "DontTrack": "Не отслеживать", @@ -19,6 +16,8 @@ "MarkAsUnread": "Отметить непрочитанным", "Archive": "Архивировать", "Inbox": "Входящие", - "Collaborators": "Участники" + "Collaborators": "Участники", + "Change": "Изменено", + "AddedRemoved": "Добавлено/удалено" } } diff --git a/plugins/notification-resources/src/components/BrowserNotificatator.svelte b/plugins/notification-resources/src/components/BrowserNotificatator.svelte index 28dfa0422c..31564a1b1e 100644 --- a/plugins/notification-resources/src/components/BrowserNotificatator.svelte +++ b/plugins/notification-resources/src/components/BrowserNotificatator.svelte @@ -14,13 +14,13 @@ --> + + +
{ + dispatch('click') + }} +> +
+ {#if icon} + + {/if} +
+ + {#if label} +
+ + diff --git a/plugins/notification-resources/src/components/NotificationGroupSetting.svelte b/plugins/notification-resources/src/components/NotificationGroupSetting.svelte new file mode 100644 index 0000000000..387cbe71b4 --- /dev/null +++ b/plugins/notification-resources/src/components/NotificationGroupSetting.svelte @@ -0,0 +1,138 @@ + + + +
+ + {#each types as type} +
+ {#if type.generated} +
+ {#each providers as provider (provider._id)} + {#if type.providers[provider._id] !== undefined} +
+ +
+ {:else} +
+ {/if} + {/each} + {/each} + +
+ + diff --git a/plugins/notification-resources/src/components/NotificationSettings.svelte b/plugins/notification-resources/src/components/NotificationSettings.svelte index 8606af16be..6262ff743e 100644 --- a/plugins/notification-resources/src/components/NotificationSettings.svelte +++ b/plugins/notification-resources/src/components/NotificationSettings.svelte @@ -1,5 +1,5 @@ -
-
-
-
-
-
-
-
- - {#each types as type (type._id)} - -
-
-
+
+
+
+ +
+ {#each groups as gr} + { + group = gr._id + }} + /> + {/each} +
+ +
+ {#if group} + + {/if}
- - diff --git a/plugins/notification-resources/src/plugin.ts b/plugins/notification-resources/src/plugin.ts index f26a14df4b..1847d98f4e 100644 --- a/plugins/notification-resources/src/plugin.ts +++ b/plugins/notification-resources/src/plugin.ts @@ -26,6 +26,8 @@ export default mergeIds(notificationId, notification, { Remove: '' as IntlString, RemoveAll: '' as IntlString, MarkAsRead: '' as IntlString, - MarkAllAsRead: '' as IntlString + MarkAllAsRead: '' as IntlString, + Change: '' as IntlString, + AddedRemoved: '' as IntlString } }) diff --git a/plugins/notification/package.json b/plugins/notification/package.json index 0df5992f81..89bf88a4f6 100644 --- a/plugins/notification/package.json +++ b/plugins/notification/package.json @@ -34,6 +34,7 @@ "dependencies": { "@hcengineering/platform": "^0.6.8", "@hcengineering/core": "^0.6.23", + "@hcengineering/preference": "^0.6.4", "@hcengineering/setting": "^0.6.5", "@hcengineering/ui": "^0.6.6" }, diff --git a/plugins/notification/src/index.ts b/plugins/notification/src/index.ts index 2d0de5def4..a9aefce002 100644 --- a/plugins/notification/src/index.ts +++ b/plugins/notification/src/index.ts @@ -13,12 +13,25 @@ // limitations under the License. // -import { Account, AttachedDoc, Class, Doc, Mixin, Ref, Space, Timestamp, TxCUD } from '@hcengineering/core' +import { + Account, + AnyAttribute, + AttachedDoc, + Class, + Doc, + Mixin, + Ref, + Space, + Timestamp, + Tx, + TxCUD +} from '@hcengineering/core' import type { Asset, IntlString, Plugin, Resource } from '@hcengineering/platform' import { plugin } from '@hcengineering/platform' import { IntegrationType } from '@hcengineering/setting' import { AnyComponent } from '@hcengineering/ui' import { Writable } from './types' +import { Preference } from '@hcengineering/preference' export * from './types' /** @@ -64,9 +77,17 @@ export enum NotificationStatus { /** * @public */ -export interface NotificationType extends Doc { - hidden: boolean +export interface NotificationGroup extends Doc { label: IntlString + icon: Asset + // using for autogenerated settings + objectClass?: Ref> +} + +/** + * @public + */ +export interface NotificationTemplate { textTemplate: string htmlTemplate: string subjectTemplate: string @@ -75,17 +96,40 @@ export interface NotificationType extends Doc { /** * @public */ -export interface NotificationProvider extends Doc { +export interface NotificationType extends Doc { + // For show/hide with attributes + attribute?: Ref + // Is autogenerated + generated: boolean + // allowed to to change setting (probably we should show it, but disable toggle??) + hidden: boolean label: IntlString - default: boolean + group: Ref + txClasses: Ref>[] + objectClass: Ref> + // check parent doc class + attachedToClass?: Ref> + // use for update/mixin txes + field?: string + // allowed providers and default value for it + providers: Record, boolean> + // templates for email (and browser/push?) + templates?: NotificationTemplate } /** * @public */ -export interface NotificationSetting extends Doc { +export interface NotificationProvider extends Doc { + label: IntlString +} + +/** + * @public + */ +export interface NotificationSetting extends Preference { + attachedTo: Ref type: Ref - provider: Ref enabled: boolean } @@ -181,15 +225,16 @@ const notification = plugin(notificationId, { NotificationType: '' as Ref>, NotificationProvider: '' as Ref>, NotificationSetting: '' as Ref>, - DocUpdates: '' as Ref> + DocUpdates: '' as Ref>, + NotificationGroup: '' as Ref> }, ids: { - MentionNotification: '' as Ref, - DMNotification: '' as Ref, + NotificationSettings: '' as Ref + }, + providers: { PlatformNotification: '' as Ref, BrowserNotification: '' as Ref, - EmailNotification: '' as Ref, - NotificationSettings: '' as Ref + EmailNotification: '' as Ref }, integrationType: { MobileApp: '' as Ref diff --git a/plugins/task-assets/lang/en.json b/plugins/task-assets/lang/en.json index f303c4b762..dc4c19441f 100644 --- a/plugins/task-assets/lang/en.json +++ b/plugins/task-assets/lang/en.json @@ -71,7 +71,7 @@ "CantStatusDelete": "Can't delete status", "CantStatusDeleteError": "There are objects in the given state. Move or delete them first.", "Tasks": "Tasks", - "Assigned": "Assigned to me", + "AssignedToMe": "Assigned to me", "TodoItems": "Todos", "Dashboard": "Dashboard", "AllTime": "All time", diff --git a/plugins/task-assets/lang/ru.json b/plugins/task-assets/lang/ru.json index 50991ac0c2..3317387b5a 100644 --- a/plugins/task-assets/lang/ru.json +++ b/plugins/task-assets/lang/ru.json @@ -71,7 +71,7 @@ "CantStatusDelete": "Невозможно удалить статус", "CantStatusDeleteError": "Есть объекты с данным статусом. Сначала переместите или удалите их. ", "Tasks": "Задачи", - "Assigned": "Назначения", + "AssignedToMe": "Назначено на меня", "TodoItems": "Todos", "Dashboard": "Дашборд", "AllTime": "Все время", diff --git a/plugins/task-resources/src/plugin.ts b/plugins/task-resources/src/plugin.ts index 01c1a9fc1f..0607192c78 100644 --- a/plugins/task-resources/src/plugin.ts +++ b/plugins/task-resources/src/plugin.ts @@ -68,7 +68,6 @@ export default mergeIds(taskId, task, { RelatedIssues: '' as IntlString, Tasks: '' as IntlString, - Assigned: '' as IntlString, Task: '' as IntlString, AllTime: '' as IntlString }, diff --git a/plugins/task/src/index.ts b/plugins/task/src/index.ts index 1233c08310..d49ab67d1a 100644 --- a/plugins/task/src/index.ts +++ b/plugins/task/src/index.ts @@ -229,6 +229,7 @@ const task = plugin(taskId, { Projects: '' as IntlString, ManageProjectStatues: '' as IntlString, TodoItems: '' as IntlString, + AssignedToMe: '' as IntlString, Dashboard: '' as IntlString }, class: { diff --git a/plugins/tracker-assets/lang/en.json b/plugins/tracker-assets/lang/en.json index 6573728d27..9b4107ad8a 100644 --- a/plugins/tracker-assets/lang/en.json +++ b/plugins/tracker-assets/lang/en.json @@ -288,6 +288,7 @@ "Saved": "Saved...", "CreatedIssue": "Created issue", "CreatedSubIssue": "Created sub-issue", + "ChangeStatus": "Change status", "ConfigLabel": "Tracker", "ConfigDescription": "Extension to manage work items and do all jobs done." }, diff --git a/plugins/tracker-assets/lang/ru.json b/plugins/tracker-assets/lang/ru.json index a15ef76ba9..de700bfbc3 100644 --- a/plugins/tracker-assets/lang/ru.json +++ b/plugins/tracker-assets/lang/ru.json @@ -288,6 +288,7 @@ "Saved": "Сохранено...", "CreatedIssue": "Создал(а) задачу", "CreatedSubIssue": "Создал(а) подзадачу", + "ChangeStatus": "Изменение статуса", "ConfigLabel": "Трекер", "ConfigDescription": "Расширение по управлению задачами, чтобы все было в срок." }, diff --git a/plugins/workbench-resources/src/components/Workbench.svelte b/plugins/workbench-resources/src/components/Workbench.svelte index ddeca1daf8..fc0822b6c5 100644 --- a/plugins/workbench-resources/src/components/Workbench.svelte +++ b/plugins/workbench-resources/src/components/Workbench.svelte @@ -150,7 +150,8 @@ notificationQuery.query( notification.class.DocUpdates, { - user: account._id + user: account._id, + hidden: false }, (res) => { hasNotification = res.some((p) => p.txes.length > 0) diff --git a/server-plugins/chunter-resources/src/index.ts b/server-plugins/chunter-resources/src/index.ts index 6be2ce97d8..28666bacf6 100644 --- a/server-plugins/chunter-resources/src/index.ts +++ b/server-plugins/chunter-resources/src/index.ts @@ -13,16 +13,10 @@ // limitations under the License. // -import chunter, { - chunterId, - ChunterMessage, - ChunterSpace, - Comment, - Message, - ThreadMessage -} from '@hcengineering/chunter' +import chunter, { chunterId, ChunterSpace, Comment, Message, ThreadMessage } from '@hcengineering/chunter' import { EmployeeAccount } from '@hcengineering/contact' import core, { + Account, Class, concatLink, Doc, @@ -39,11 +33,10 @@ import core, { TxRemoveDoc, TxUpdateDoc } from '@hcengineering/core' -import notification, { Collaborators } from '@hcengineering/notification' +import notification, { Collaborators, NotificationType } from '@hcengineering/notification' import { getMetadata } from '@hcengineering/platform' import serverCore, { TriggerControl } from '@hcengineering/server-core' -import { getEmployeeAccountById } from '@hcengineering/server-notification' -import { createNotificationTxes, getDocCollaborators, getMixinTx } from '@hcengineering/server-notification-resources' +import { getDocCollaborators, getMixinTx } from '@hcengineering/server-notification-resources' import { workbenchId } from '@hcengineering/workbench' /** @@ -288,52 +281,41 @@ export async function ChunterTrigger (tx: Tx, control: TriggerControl): Promise< /** * @public */ -export async function DMTrigger (tx: Tx, control: TriggerControl): Promise { - if (tx._class !== core.class.TxCollectionCUD) return [] - const hierarchy = control.hierarchy - const ctx = tx as TxCollectionCUD - if (!hierarchy.isDerived(ctx.tx.objectClass, chunter.class.Message)) { - return [] - } - const actualTx = TxProcessor.extractTx(tx) - if (actualTx._class !== core.class.TxCreateDoc) { - return [] - } - const doc = TxProcessor.createDoc2Doc(actualTx as TxCreateDoc) - const dms = await control.findAll(chunter.class.DirectMessage, { _id: doc.space }) - if (dms.length === 0) { - return [] - } - const sender = await getEmployeeAccountById(ctx.tx.modifiedBy, control) - if (sender === undefined) return [] - const res: Tx[] = [] - for (const member of dms[0].members) { - const receiver = await getEmployeeAccountById(member, control) - if (receiver === undefined) continue - if (receiver._id === sender._id) continue - const createNotificationTx = await createNotificationTxes( - control, - ctx, - notification.ids.DMNotification, - doc, - sender, - receiver, - doc.content - ) - res.push(...createNotificationTx) - } - return res +export async function IsDirectMessagee ( + tx: Tx, + doc: Doc, + user: Ref, + type: NotificationType, + control: TriggerControl +): Promise { + const space = (await control.findAll(chunter.class.DirectMessage, { _id: doc.space }))[0] + return space !== undefined +} + +/** + * @public + */ +export async function IsChannelMessagee ( + tx: Tx, + doc: Doc, + user: Ref, + type: NotificationType, + control: TriggerControl +): Promise { + const space = (await control.findAll(chunter.class.Channel, { _id: doc.space }))[0] + return space !== undefined } // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export default async () => ({ trigger: { - ChunterTrigger, - DMTrigger + ChunterTrigger }, function: { CommentRemove, ChannelHTMLPresenter: channelHTMLPresenter, - ChannelTextPresenter: channelTextPresenter + ChannelTextPresenter: channelTextPresenter, + IsDirectMessagee, + IsChannelMessagee } }) diff --git a/server-plugins/chunter/src/index.ts b/server-plugins/chunter/src/index.ts index dd6d93f39a..3d829555be 100644 --- a/server-plugins/chunter/src/index.ts +++ b/server-plugins/chunter/src/index.ts @@ -17,7 +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' +import { Presenter, TypeMatchFunc } from '@hcengineering/server-notification' /** * @public @@ -29,8 +29,7 @@ export const serverChunterId = 'server-chunter' as Plugin */ export default plugin(serverChunterId, { trigger: { - ChunterTrigger: '' as Resource, - DMTrigger: '' as Resource + ChunterTrigger: '' as Resource }, function: { CommentRemove: '' as Resource< @@ -45,6 +44,8 @@ export default plugin(serverChunterId, { ) => Promise >, ChannelHTMLPresenter: '' as Resource, - ChannelTextPresenter: '' as Resource + ChannelTextPresenter: '' as Resource, + IsDirectMessagee: '' as TypeMatchFunc, + IsChannelMessagee: '' as TypeMatchFunc } }) diff --git a/server-plugins/hr-resources/src/index.ts b/server-plugins/hr-resources/src/index.ts index c31e600cb5..5d7aeea3d7 100644 --- a/server-plugins/hr-resources/src/index.ts +++ b/server-plugins/hr-resources/src/index.ts @@ -39,7 +39,7 @@ import notification, { NotificationType } from '@hcengineering/notification' import { translate } from '@hcengineering/platform' import { TriggerControl } from '@hcengineering/server-core' import { getEmployee, getEmployeeAccountById } from '@hcengineering/server-notification' -import { getContent } from '@hcengineering/server-notification-resources' +import { getContent, isAllowed } from '@hcengineering/server-notification-resources' async function getOldDepartment ( currentTx: TxMixin | TxUpdateDoc, @@ -209,6 +209,17 @@ async function getEmailNotification ( } } + // should respect employee settings + const accounts = await control.modelDb.findAll(contact.class.EmployeeAccount, { + employee: { $in: Array.from(contacts.values()) as Ref[] } + }) + for (const account of accounts) { + const allowed = await isAllowed(control, account._id, type, notification.providers.EmailNotification) + if (!allowed) { + contacts.delete(account.employee) + } + } + const channels = await control.findAll(contact.class.Channel, { provider: contact.channelProvider.Email, attachedTo: { $in: Array.from(contacts) } diff --git a/server-plugins/lead-resources/src/index.ts b/server-plugins/lead-resources/src/index.ts index af558fe15a..e249a03cc8 100644 --- a/server-plugins/lead-resources/src/index.ts +++ b/server-plugins/lead-resources/src/index.ts @@ -13,21 +13,10 @@ // limitations under the License. // -import core, { - AttachedDoc, - concatLink, - Doc, - Tx, - TxCollectionCUD, - TxCreateDoc, - TxCUD, - TxProcessor, - TxUpdateDoc -} from '@hcengineering/core' -import lead, { Lead, leadId } from '@hcengineering/lead' +import { concatLink, Doc } from '@hcengineering/core' +import { Lead, leadId } from '@hcengineering/lead' import { getMetadata } from '@hcengineering/platform' import serverCore, { TriggerControl } from '@hcengineering/server-core' -import { addAssigneeNotification } from '@hcengineering/server-task-resources' import view from '@hcengineering/view' import { workbenchId } from '@hcengineering/workbench' @@ -50,68 +39,10 @@ export async function leadTextPresenter (doc: Doc): Promise { return `LEAD-${lead.number}` } -/** - * @public - */ -export async function OnLeadUpdate (tx: Tx, control: TriggerControl): Promise { - const actualTx = TxProcessor.extractTx(tx) - - const res: Tx[] = [] - - const cud = actualTx as TxCUD - - if (actualTx._class === core.class.TxCreateDoc) { - await handleLeadCreate(control, cud, res, tx) - } - - if (actualTx._class === core.class.TxUpdateDoc) { - await handleLeadUpdate(control, cud, res, tx) - } - return res -} - -async function handleLeadCreate (control: TriggerControl, cud: TxCUD, res: Tx[], tx: Tx): Promise { - if (control.hierarchy.isDerived(cud.objectClass, lead.class.Lead)) { - const createTx = cud as TxCreateDoc - const leadValue = TxProcessor.createDoc2Doc(createTx) - if (leadValue.assignee != null) { - await addAssigneeNotification( - control, - res, - leadValue, - leadValue.assignee, - tx as TxCollectionCUD - ) - } - } -} - -async function handleLeadUpdate (control: TriggerControl, cud: TxCUD, res: Tx[], tx: Tx): Promise { - if (control.hierarchy.isDerived(cud.objectClass, lead.class.Lead)) { - const updateTx = cud as TxUpdateDoc - if (updateTx.operations.assignee != null) { - const leadValue = (await control.findAll(lead.class.Lead, { _id: updateTx.objectId }, { limit: 1 })).shift() - - if (leadValue?.assignee != null) { - await addAssigneeNotification( - control, - res, - leadValue, - leadValue.assignee, - tx as TxCollectionCUD - ) - } - } - } -} - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export default async () => ({ function: { LeadHTMLPresenter: leadHTMLPresenter, LeadTextPresenter: leadTextPresenter - }, - trigger: { - OnLeadUpdate } }) diff --git a/server-plugins/lead/src/index.ts b/server-plugins/lead/src/index.ts index 012f541c8d..23b43d5a22 100644 --- a/server-plugins/lead/src/index.ts +++ b/server-plugins/lead/src/index.ts @@ -15,7 +15,6 @@ import type { Plugin, Resource } from '@hcengineering/platform' import { plugin } from '@hcengineering/platform' -import { TriggerFunc } from '@hcengineering/server-core' import { Presenter } from '@hcengineering/server-notification' /** @@ -30,8 +29,5 @@ export default plugin(serverLeadId, { function: { LeadHTMLPresenter: '' as Resource, LeadTextPresenter: '' as Resource - }, - trigger: { - OnLeadUpdate: '' as Resource } }) diff --git a/server-plugins/notification-resources/src/index.ts b/server-plugins/notification-resources/src/index.ts index bb5b50955c..c5b33c15df 100644 --- a/server-plugins/notification-resources/src/index.ts +++ b/server-plugins/notification-resources/src/index.ts @@ -22,13 +22,14 @@ import core, { ArrOf, AttachedDoc, Class, + Collection, + Data, Doc, DocumentUpdate, Hierarchy, MixinUpdate, Ref, RefTo, - Space, Tx, TxCUD, TxCollectionCUD, @@ -92,28 +93,9 @@ export async function OnBacklinkCreate (tx: Tx, control: TriggerControl): Promis } ) res.push(collabTx) - res = res.concat( - await createCollabDocInfo( - [receiver._id], - tx as TxCUD, - doc._id, - doc._class, - control, - tx._id as Ref> - ) - ) } + res = res.concat(await createCollabDocInfo([receiver._id], control, tx as TxCUD, doc)) } - const notifyTx = await createNotificationTxes( - control, - ptx, - notification.ids.MentionNotification, - doc, - sender, - receiver, - backlink.message - ) - res = [...res, ...notifyTx] return res } @@ -132,18 +114,22 @@ function checkTx (ptx: TxCollectionCUD, hierarchy: Hierarchy): bo return true } -async function isAllowed ( +/** + * @public + */ +export async function isAllowed ( control: TriggerControl, - receiver: EmployeeAccount, + receiver: Ref, + typeId: Ref, providerId: Ref ): Promise { const setting = ( await control.findAll( notification.class.NotificationSetting, { - provider: providerId, - type: notification.ids.MentionNotification, - space: receiver._id as unknown as Ref + attachedTo: providerId, + type: typeId, + modifiedBy: receiver }, { limit: 1 } ) @@ -151,13 +137,13 @@ async function isAllowed ( if (setting !== undefined) { return setting.enabled } - const provider = ( - await control.modelDb.findAll(notification.class.NotificationProvider, { - _id: providerId + const type = ( + await control.modelDb.findAll(notification.class.NotificationType, { + _id: typeId }) )[0] - if (provider === undefined) return false - return provider.default + if (type === undefined) return false + return type.providers[providerId] ?? false } async function getTextPart (doc: Doc, control: TriggerControl): Promise { @@ -205,10 +191,11 @@ export async function getContent ( const textPart = await getTextPart(doc, control) if (textPart === undefined) return - const text = fillTemplate(notificationType.textTemplate, sender, textPart, data) + if (notificationType.templates === undefined) return + const text = fillTemplate(notificationType.templates.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) + const html = fillTemplate(notificationType.templates.htmlTemplate, sender, htmlPart ?? textPart, data) + const subject = fillTemplate(notificationType.templates.subjectTemplate, sender, textPart, data) return { text, html, @@ -216,36 +203,33 @@ export async function getContent ( } } -/** - * @public - */ -export async function createNotificationTxes ( +async function createEmailNotificationTxes ( control: TriggerControl, - ptx: TxCollectionCUD, + tx: Tx, type: Ref, doc: Doc | undefined, - sender: EmployeeAccount, - receiver: EmployeeAccount, + senderId: Ref, + receiverId: Ref, data: string = '' -): Promise { - const res: Tx[] = [] +): Promise { + const sender = (await control.modelDb.findAll(contact.class.EmployeeAccount, { _id: senderId }))[0] + + if (sender === undefined) return + + const receiver = (await control.modelDb.findAll(contact.class.EmployeeAccount, { _id: receiverId }))[0] + if (receiver === undefined) return const senderName = formatName(sender.name) const content = await getContent(doc, senderName, type, control, data) - 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) - } + if (content !== undefined) { + return await getEmailNotificationTx(tx, senderName, content.text, content.html, content.subject, receiver) } - - return res } async function getEmailNotificationTx ( - ptx: TxCollectionCUD, + tx: Tx, sender: string, text: string, html: string, @@ -259,8 +243,8 @@ async function getEmailNotificationTx ( space: core.space.DerivedTx, objectClass: notification.class.EmailNotification, objectSpace: notification.space.Notifications, - modifiedOn: ptx.modifiedOn, - modifiedBy: ptx.modifiedBy, + modifiedOn: tx.modifiedOn, + modifiedBy: tx.modifiedBy, attributes: { status: 'new', sender, @@ -410,47 +394,152 @@ export async function getDocCollaborators ( return Array.from(collaborators.values()) } +function fieldUpdated (field: string, ops: DocumentUpdate | MixinUpdate): boolean { + if ((ops as any)[field] !== undefined) return true + if ((ops.$pull as any)?.[field] !== undefined) return true + if ((ops.$push as any)?.[field] !== undefined) return true + return false +} + +function isTypeMatched ( + control: TriggerControl, + type: NotificationType, + tx: TxCUD, + extractedTx: TxCUD +): boolean { + const targetClass = control.hierarchy.getBaseClass(type.objectClass) + if (!type.txClasses.includes(extractedTx._class)) return false + if (!control.hierarchy.isDerived(extractedTx.objectClass, targetClass)) return false + if (tx._class === core.class.TxCollectionCUD && type.attachedToClass !== undefined) { + if (!control.hierarchy.isDerived(tx.objectClass, type.attachedToClass)) return false + } + if (type.field !== undefined) { + if (extractedTx._class === core.class.TxUpdateDoc) { + if (!fieldUpdated(type.field, (extractedTx as TxUpdateDoc).operations)) return false + } + if (extractedTx._class === core.class.TxMixin) { + if (!fieldUpdated(type.field, (extractedTx as TxMixin).attributes)) return false + } + } + return true +} + +async function getMatchedTypes (control: TriggerControl, tx: TxCUD): Promise { + const allTypes = await control.modelDb.findAll(notification.class.NotificationType, {}) + const extractedTx = TxProcessor.extractTx(tx) as TxCUD + const filtered: NotificationType[] = [] + for (const type of allTypes) { + if (isTypeMatched(control, type, tx, extractedTx)) { + filtered.push(type) + } + } + return filtered +} + +interface NotifyResult { + allowed: boolean + emails: NotificationType[] +} + +async function isShouldNotify ( + control: TriggerControl, + tx: TxCUD, + object: Doc, + user: Ref +): Promise { + let allowed = false + const emailTypes: NotificationType[] = [] + const types = await getMatchedTypes(control, tx) + for (const type of types) { + if (control.hierarchy.hasMixin(type, serverNotification.mixin.TypeMatch)) { + const mixin = control.hierarchy.as(type, serverNotification.mixin.TypeMatch) + if (mixin.func !== undefined) { + const f = await getResource(mixin.func) + const res = await f(tx, object, user, type, control) + if (!res) continue + } + } + if (await isAllowed(control, user as Ref, type._id, notification.providers.PlatformNotification)) { + allowed = true + } + if (await isAllowed(control, user as Ref, type._id, notification.providers.EmailNotification)) { + emailTypes.push(type) + } + } + return { + allowed, + emails: emailTypes + } +} + +async function getNotificationTxes ( + control: TriggerControl, + object: Doc, + originTx: TxCUD, + target: Ref, + docUpdates: DocUpdates[] +): Promise { + if (originTx.modifiedBy === target) return [] + const res: Tx[] = [] + const allowed = await isShouldNotify(control, originTx, object, target) + if (allowed.allowed) { + const current = docUpdates.find((p) => p.user === target) + if (current === undefined) { + res.push( + control.txFactory.createTxCreateDoc(notification.class.DocUpdates, notification.space.Notifications, { + user: target, + attachedTo: object._id, + attachedToClass: object._class, + hidden: false, + lastTx: originTx._id, + lastTxTime: originTx.modifiedOn, + txes: [[originTx._id, originTx.modifiedOn]] + }) + ) + } else { + res.push( + control.txFactory.createTxUpdateDoc(current._class, current.space, current._id, { + $push: { + txes: [originTx._id, originTx.modifiedOn] + } + }) + ) + res.push( + control.txFactory.createTxUpdateDoc(current._class, current.space, current._id, { + lastTx: originTx._id, + lastTxTime: originTx.modifiedOn, + hidden: false + }) + ) + } + } + for (const type of allowed.emails) { + const emailTx = await createEmailNotificationTxes( + control, + originTx, + type._id, + object, + originTx.modifiedBy as Ref, + target as Ref + ) + if (emailTx !== undefined) { + res.push(emailTx) + } + } + return res +} + async function createCollabDocInfo ( collaborators: Ref[], - tx: TxCUD, - objectId: Ref, - objectClass: Ref>, control: TriggerControl, - txId?: Ref> -): Promise[]> { - const res: TxCUD[] = [] + originTx: TxCUD, + object: Doc +): Promise { + let res: Tx[] = [] const targets = new Set(collaborators) - const docs = await control.findAll(notification.class.DocUpdates, { attachedTo: objectId }) - for (const doc of docs) { - if (tx.modifiedBy === doc.user || !targets.delete(doc.user)) continue - res.push( - control.txFactory.createTxUpdateDoc(doc._class, doc.space, doc._id, { - $push: { - txes: [txId ?? tx._id, tx.modifiedOn] - } - }) - ) - res.push( - control.txFactory.createTxUpdateDoc(doc._class, doc.space, doc._id, { - lastTx: txId ?? tx._id, - lastTxTime: tx.modifiedOn, - hidden: false - }) - ) - } + const docUpdates = await control.findAll(notification.class.DocUpdates, { attachedTo: object._id }) for (const target of targets) { - if (tx.modifiedBy === target) continue - res.push( - control.txFactory.createTxCreateDoc(notification.class.DocUpdates, notification.space.Notifications, { - user: target, - attachedTo: objectId, - attachedToClass: objectClass, - hidden: false, - lastTx: txId ?? tx._id, - lastTxTime: tx.modifiedOn, - txes: [[txId ?? tx._id, tx.modifiedOn]] - }) - ) + res = res.concat(await getNotificationTxes(control, object, originTx, target, docUpdates)) } return res } @@ -481,7 +570,7 @@ export function getMixinTx ( export async function createCollaboratorDoc ( tx: TxCreateDoc, control: TriggerControl, - txId?: Ref> + originTx: TxCUD ): Promise { const res: Tx[] = [] const hierarchy = control.hierarchy @@ -491,7 +580,7 @@ export async function createCollaboratorDoc ( const collaborators = await getDocCollaborators(doc, mixin, control) const mixinTx = getMixinTx(tx, control, collaborators) - const notificationTxes = await createCollabDocInfo(collaborators, tx, doc._id, doc._class, control, txId) + const notificationTxes = await createCollabDocInfo(collaborators, control, originTx, doc) res.push(mixinTx) res.push(...notificationTxes) } @@ -501,15 +590,19 @@ export async function createCollaboratorDoc ( /** * @public */ -export async function collaboratorDocHandler (tx: Tx, control: TriggerControl, txId?: Ref>): Promise { +export async function collaboratorDocHandler ( + tx: TxCUD, + control: TriggerControl, + originTx?: TxCUD +): Promise { switch (tx._class) { case core.class.TxCreateDoc: if (tx.space === core.space.DerivedTx) return [] - return await createCollaboratorDoc(tx as TxCreateDoc, control, txId) + return await createCollaboratorDoc(tx as TxCreateDoc, control, originTx ?? tx) case core.class.TxUpdateDoc: case core.class.TxMixin: if (tx.space === core.space.DerivedTx) return [] - return await updateCollaboratorDoc(tx as TxUpdateDoc, control, txId) + return await updateCollaboratorDoc(tx as TxUpdateDoc, control, originTx ?? tx) case core.class.TxRemoveDoc: return await removeCollaboratorDoc(tx as TxRemoveDoc, control) case core.class.TxCollectionCUD: @@ -521,15 +614,13 @@ export async function collaboratorDocHandler (tx: Tx, control: TriggerControl, t async function collectionCollabDoc (tx: TxCollectionCUD, control: TriggerControl): Promise { const actualTx = TxProcessor.extractTx(tx) - let res = await collaboratorDocHandler(actualTx, control, tx._id) + let res = await collaboratorDocHandler(actualTx as TxCUD, control, tx) if ([core.class.TxCreateDoc, core.class.TxRemoveDoc].includes(actualTx._class)) { const doc = (await control.findAll(tx.objectClass, { _id: tx.objectId }, { limit: 1 }))[0] if (doc !== undefined) { if (control.hierarchy.hasMixin(doc, notification.mixin.Collaborators)) { const collabMixin = control.hierarchy.as(doc, notification.mixin.Collaborators) - res = res.concat( - await createCollabDocInfo(collabMixin.collaborators, tx, tx.objectId, tx.objectClass, control, tx._id) - ) + res = res.concat(await createCollabDocInfo(collabMixin.collaborators, control, tx, doc)) } } } @@ -591,7 +682,7 @@ function isMixinTx (tx: TxUpdateDoc | TxMixin): tx is TxMixin | TxMixin, control: TriggerControl, - txId?: Ref> + originTx: TxCUD ): Promise { const hierarchy = control.hierarchy let res: Tx[] = [] @@ -619,19 +710,12 @@ async function updateCollaboratorDoc ( ) } res = res.concat( - await createCollabDocInfo( - [...collabMixin.collaborators, ...newCollaborators], - tx, - doc._id, - doc._class, - control, - txId - ) + await createCollabDocInfo([...collabMixin.collaborators, ...newCollaborators], control, originTx, doc) ) } else { const collaborators = await getDocCollaborators(doc, mixin, control) res.push(getMixinTx(tx, control, collaborators)) - res = res.concat(await createCollabDocInfo(collaborators, tx, doc._id, doc._class, control, txId)) + res = res.concat(await createCollabDocInfo(collaborators, control, originTx, doc)) } return res @@ -675,6 +759,77 @@ export async function OnAddCollborator (tx: Tx, control: TriggerControl): Promis return result } +/** + * @public + */ +export async function isUserInFieldValue ( + tx: Tx, + doc: Doc, + user: Ref, + type: NotificationType, + control: TriggerControl +): Promise { + if (type.field === undefined) return false + const value = (doc as any)[type.field] + if (value === undefined) return false + if (Array.isArray(value)) { + return value.includes(user) + } else { + return value === user + } +} + +/** + * @public + */ +export async function OnAttributeCreate (tx: Tx, control: TriggerControl): Promise { + if (tx._class !== core.class.TxCreateDoc) return [] + const ctx = tx as TxCreateDoc + if (ctx.objectClass !== core.class.Attribute) return [] + const attribute = TxProcessor.createDoc2Doc(ctx) + const group = (await control.findAll(notification.class.NotificationGroup, { objectClass: attribute.attributeOf }))[0] + if (group === undefined) return [] + const isCollection: boolean = core.class.Collection === attribute.type._class + const objectClass = !isCollection ? attribute.attributeOf : (attribute.type as Collection).of + const txClasses = !isCollection + ? [control.hierarchy.isMixin(attribute.attributeOf) ? core.class.TxMixin : core.class.TxUpdateDoc] + : [core.class.TxCreateDoc, core.class.TxRemoveDoc] + const data: Data = { + group: group._id, + generated: true, + objectClass, + txClasses, + hidden: false, + providers: { + [notification.providers.PlatformNotification]: false + }, + label: attribute.label + } + if (isCollection) { + data.attachedToClass = attribute.attributeOf + } + const id = + `${notification.class.NotificationType}_${attribute.attributeOf}_${attribute.name}` as Ref + const res = control.txFactory.createTxCreateDoc(notification.class.NotificationType, core.space.Model, data, id) + return [res] +} + +/** + * @public + */ +export async function OnAttributeUpdate (tx: Tx, control: TriggerControl): Promise { + if (tx._class !== core.class.TxUpdateDoc) return [] + const ctx = tx as TxUpdateDoc + if (ctx.objectClass !== core.class.Attribute) return [] + if (ctx.operations.hidden === undefined) return [] + const type = (await control.findAll(notification.class.NotificationType, { attribute: ctx.objectId }))[0] + if (type === undefined) return [] + const res = control.txFactory.createTxUpdateDoc(type._class, type.space, type._id, { + hidden: ctx.operations.hidden + }) + return [res] +} + export * from './types' // eslint-disable-next-line @typescript-eslint/explicit-function-return-type @@ -684,6 +839,11 @@ export default async () => ({ CollaboratorDocHandler: collaboratorDocHandler, OnUpdateLastView, UpdateLastView, - OnAddCollborator + OnAddCollborator, + OnAttributeCreate, + OnAttributeUpdate + }, + function: { + IsUserInFieldValue: isUserInFieldValue } }) diff --git a/server-plugins/notification/src/index.ts b/server-plugins/notification/src/index.ts index e8856e1af7..6458bdd695 100644 --- a/server-plugins/notification/src/index.ts +++ b/server-plugins/notification/src/index.ts @@ -15,8 +15,8 @@ // import contact, { Employee, EmployeeAccount } from '@hcengineering/contact' -import { Account, Class, Doc, Mixin, Ref, TxCreateDoc, TxFactory, TxUpdateDoc } from '@hcengineering/core' -import notification, { LastView } from '@hcengineering/notification' +import { Account, Class, Doc, Mixin, Ref, Tx, TxCreateDoc, TxFactory, TxUpdateDoc } from '@hcengineering/core' +import notification, { LastView, NotificationType } from '@hcengineering/notification' import { Plugin, Resource, plugin } from '@hcengineering/platform' import type { TriggerControl, TriggerFunc } from '@hcengineering/server-core' @@ -166,19 +166,39 @@ export interface TextPresenter extends Class { presenter: Resource } +/** + * @public + */ +export type TypeMatchFunc = Resource< +(tx: Tx, doc: Doc, user: Ref, type: NotificationType, control: TriggerControl) => Promise +> + +/** + * @public + */ +export interface TypeMatch extends NotificationType { + func: TypeMatchFunc +} + /** * @public */ export default plugin(serverNotificationId, { mixin: { HTMLPresenter: '' as Ref>, - TextPresenter: '' as Ref> + TextPresenter: '' as Ref>, + TypeMatch: '' as Ref> }, trigger: { OnBacklinkCreate: '' as Resource, UpdateLastView: '' as Resource, OnUpdateLastView: '' as Resource, CollaboratorDocHandler: '' as Resource, - OnAddCollborator: '' as Resource + OnAddCollborator: '' as Resource, + OnAttributeCreate: '' as Resource, + OnAttributeUpdate: '' as Resource + }, + function: { + IsUserInFieldValue: '' as TypeMatchFunc } }) diff --git a/server-plugins/recruit-resources/src/index.ts b/server-plugins/recruit-resources/src/index.ts index d6b9005311..019c64d359 100644 --- a/server-plugins/recruit-resources/src/index.ts +++ b/server-plugins/recruit-resources/src/index.ts @@ -15,11 +15,9 @@ import contact from '@hcengineering/contact' import core, { - AttachedDoc, concatLink, Doc, Tx, - TxCollectionCUD, TxCreateDoc, TxCUD, TxProcessor, @@ -29,7 +27,6 @@ import core, { import { getMetadata } from '@hcengineering/platform' import recruit, { Applicant, recruitId, Vacancy } from '@hcengineering/recruit' import serverCore, { TriggerControl } from '@hcengineering/server-core' -import { addAssigneeNotification } from '@hcengineering/server-task-resources' import { workbenchId } from '@hcengineering/workbench' function getSequenceId (doc: Vacancy | Applicant, control: TriggerControl): string { @@ -96,12 +93,10 @@ export async function OnRecruitUpdate (tx: Tx, control: TriggerControl): Promise if (actualTx._class === core.class.TxCreateDoc) { handleVacancyCreate(control, cud, actualTx, res) - await handleApplicantCreate(control, cud, res, tx) } if (actualTx._class === core.class.TxUpdateDoc) { await handleVacancyUpdate(control, cud, res) - await handleApplicantUpdate(control, cud, res, tx) } if (actualTx._class === core.class.TxRemoveDoc) { await handleVacancyRemove(control, cud, actualTx) @@ -189,43 +184,6 @@ async function handleVacancyRemove (control: TriggerControl, cud: TxCUD, ac } } -async function handleApplicantUpdate (control: TriggerControl, cud: TxCUD, res: Tx[], tx: Tx): Promise { - if (control.hierarchy.isDerived(cud.objectClass, recruit.class.Applicant)) { - const updateTx = cud as TxUpdateDoc - if (updateTx.operations.assignee != null) { - const applicant = ( - await control.findAll(recruit.class.Applicant, { _id: updateTx.objectId }, { limit: 1 }) - ).shift() - - if (applicant?.assignee != null) { - await addAssigneeNotification( - control, - res, - applicant, - applicant.assignee, - tx as TxCollectionCUD - ) - } - } - } -} - -async function handleApplicantCreate (control: TriggerControl, cud: TxCUD, res: Tx[], tx: Tx): Promise { - if (control.hierarchy.isDerived(cud.objectClass, recruit.class.Applicant)) { - const createTx = cud as TxCreateDoc - const applicant = TxProcessor.createDoc2Doc(createTx) - if (applicant.assignee != null) { - await addAssigneeNotification( - control, - res, - applicant, - applicant.assignee, - tx as TxCollectionCUD - ) - } - } -} - function handleVacancyCreate (control: TriggerControl, cud: TxCUD, actualTx: Tx, res: Tx[]): void { if (control.hierarchy.isDerived(cud.objectClass, recruit.class.Vacancy)) { const createTx = actualTx as TxCreateDoc diff --git a/server-plugins/task-resources/src/index.ts b/server-plugins/task-resources/src/index.ts index 985fbfb111..a3d41cc587 100644 --- a/server-plugins/task-resources/src/index.ts +++ b/server-plugins/task-resources/src/index.ts @@ -13,39 +13,6 @@ // limitations under the License. // -import { Employee } from '@hcengineering/contact' -import { AttachedDoc, Doc, Ref, Tx, TxCollectionCUD } from '@hcengineering/core' -import { TriggerControl } from '@hcengineering/server-core' -import { getEmployeeAccount, getEmployeeAccountById } from '@hcengineering/server-notification' -import { createNotificationTxes } from '@hcengineering/server-notification-resources' -import task from '@hcengineering/task' - -/** - * @public - */ -export async function addAssigneeNotification ( - control: TriggerControl, - res: Tx[], - issue: Doc, - assignee: Ref, - ptx: TxCollectionCUD -): Promise { - const sender = await getEmployeeAccountById(ptx.modifiedBy, control) - if (sender === undefined) { - return - } - - const receiver = await getEmployeeAccount(assignee, control) - if (receiver === undefined) { - return - } - if (sender._id === receiver._id) return - - const result = await createNotificationTxes(control, ptx, task.ids.AssigneedNotification, issue, sender, receiver) - - res.push(...result) -} - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export default async () => ({ function: {} diff --git a/server-plugins/task/package.json b/server-plugins/task/package.json index 8ccf446c3e..d152f37c6f 100644 --- a/server-plugins/task/package.json +++ b/server-plugins/task/package.json @@ -29,6 +29,7 @@ "dependencies": { "@hcengineering/server-notification": "^0.6.0", "@hcengineering/platform": "^0.6.8", - "@hcengineering/server-core": "^0.6.1" + "@hcengineering/server-core": "^0.6.1", + "@hcengineering/core": "^0.6.23" } } diff --git a/server-plugins/tracker-resources/src/index.ts b/server-plugins/tracker-resources/src/index.ts index bc331e5b7c..584a4f696c 100644 --- a/server-plugins/tracker-resources/src/index.ts +++ b/server-plugins/tracker-resources/src/index.ts @@ -13,7 +13,6 @@ // limitations under the License. // -import { Employee } from '@hcengineering/contact' import core, { AttachedDoc, concatLink, @@ -32,7 +31,6 @@ import core, { } from '@hcengineering/core' import { getMetadata } from '@hcengineering/platform' import serverCore, { TriggerControl } from '@hcengineering/server-core' -import { addAssigneeNotification } from '@hcengineering/server-task-resources' import tracker, { Component, Issue, IssueParentInfo, TimeSpendReport, trackerId } from '@hcengineering/tracker' import { workbenchId } from '@hcengineering/workbench' @@ -71,19 +69,6 @@ export async function issueTextPresenter (doc: Doc, control: TriggerControl): Pr return issueName } -/** - * @public - */ -export async function addTrackerAssigneeNotification ( - control: TriggerControl, - res: Tx[], - issue: Issue, - assignee: Ref, - ptx: TxCollectionCUD -): Promise { - await addAssigneeNotification(control, res, issue, assignee, ptx) -} - /** * @public */ @@ -137,15 +122,6 @@ export async function OnIssueUpdate (tx: Tx, control: TriggerControl): Promise - ) - } return res } } @@ -279,10 +255,6 @@ async function doIssueUpdate ( return currentIssue } - if (updateTx.operations.assignee != null) { - await addTrackerAssigneeNotification(control, res, await getCurrentIssue(), updateTx.operations.assignee, tx) - } - if (Object.prototype.hasOwnProperty.call(updateTx.operations, 'attachedTo')) { const [newParent] = await control.findAll( tracker.class.Issue, diff --git a/server/middleware/src/spaceSecurity.ts b/server/middleware/src/spaceSecurity.ts index 00acf5caa3..4e9a603bfb 100644 --- a/server/middleware/src/spaceSecurity.ts +++ b/server/middleware/src/spaceSecurity.ts @@ -285,7 +285,7 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar } if (typeof query === 'string') { if (!spaces.includes(query)) { - throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) + return { $in: [] } } } else if (query.$in != null) { query.$in = query.$in.filter((p) => spaces.includes(p))