mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-24 13:22:23 +02:00
727 lines
22 KiB
TypeScript
727 lines
22 KiB
TypeScript
//
|
|
// Copyright © 2023 Hardcore Engineering Inc.
|
|
//
|
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License. You may
|
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
//
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
import activity, { type ActivityMessage, type ActivityReference, type UserMentionInfo } from '@hcengineering/activity'
|
|
import contact, { type Employee, type Person } from '@hcengineering/contact'
|
|
import core, {
|
|
type AccountUuid,
|
|
type Blob,
|
|
type Class,
|
|
type Data,
|
|
type Doc,
|
|
type Hierarchy,
|
|
type Markup,
|
|
type MeasureContext,
|
|
type ModelDb,
|
|
type PersonId,
|
|
type Ref,
|
|
type Space,
|
|
type Tx,
|
|
type TxCreateDoc,
|
|
type TxCUD,
|
|
TxFactory,
|
|
TxProcessor,
|
|
type TxRemoveDoc,
|
|
type TxUpdateDoc,
|
|
type Type
|
|
} from '@hcengineering/core'
|
|
import notification, {
|
|
getClassCollaborators,
|
|
type MentionInboxNotification,
|
|
type NotificationType
|
|
} from '@hcengineering/notification'
|
|
import { getPerson } from '@hcengineering/server-contact'
|
|
import { type StorageAdapter, type TriggerControl } from '@hcengineering/server-core'
|
|
import {
|
|
getAllowedProviders,
|
|
getCommonNotificationTxes,
|
|
getNotificationProviderControl,
|
|
getReceiversInfo,
|
|
type NotificationProviderControl
|
|
} from '@hcengineering/server-notification-resources'
|
|
import { areEqualJson, extractReferences, jsonToMarkup, markupToJSON } from '@hcengineering/text-core'
|
|
|
|
export function isDocMentioned (doc: Ref<Doc>, content: string): boolean {
|
|
const references = []
|
|
|
|
const node = markupToJSON(content)
|
|
references.push(...extractReferences(node))
|
|
|
|
for (const ref of references) {
|
|
if (ref.objectId === doc) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
export async function getPersonNotificationTxes (
|
|
ctx: MeasureContext,
|
|
reference: Data<ActivityReference>,
|
|
control: TriggerControl,
|
|
senderId: PersonId,
|
|
space: Ref<Space>,
|
|
originTx: TxCUD<Doc>,
|
|
notificationControl: NotificationProviderControl
|
|
): Promise<Tx[]> {
|
|
const { hierarchy } = control
|
|
const res: Tx[] = []
|
|
const receiverPersonRef = reference.attachedTo as Ref<Person>
|
|
const info = (
|
|
await control.findAll<UserMentionInfo>(ctx, activity.class.UserMentionInfo, {
|
|
user: receiverPersonRef,
|
|
attachedTo: reference.attachedDocId
|
|
})
|
|
)[0]
|
|
|
|
res.push(getUpdateMentionInfoTx(control, reference, space, info))
|
|
|
|
if (
|
|
originTx._class === core.class.TxCreateDoc &&
|
|
hierarchy.isDerived(originTx.objectClass, activity.class.ActivityMessage)
|
|
) {
|
|
return res
|
|
}
|
|
|
|
if (info !== undefined && hierarchy.isDerived(originTx.objectClass, activity.class.ActivityMessage)) {
|
|
return res
|
|
}
|
|
|
|
const doc = (await control.findAll(ctx, reference.srcDocClass, { _id: reference.srcDocId }))[0]
|
|
if (doc === undefined) return res
|
|
const docSpace = (await control.findAll<Space>(control.ctx, core.class.Space, { _id: space }, { limit: 1 }))[0]
|
|
if (docSpace === undefined) return res
|
|
|
|
const senderAccount = control.ctx.contextData.socialStringsToUsers.get(senderId)?.accontUuid
|
|
|
|
let collaborators: AccountUuid[] = []
|
|
|
|
if ([contact.mention.Everyone, contact.mention.Here].includes(reference.attachedTo as Ref<Employee>)) {
|
|
collaborators = await getMultipleMentionCollaborators(ctx, reference, control, doc)
|
|
} else {
|
|
const employee = (
|
|
await control.findAll(ctx, contact.mixin.Employee, { _id: reference.attachedTo as Ref<Employee> })
|
|
)[0]
|
|
if (employee?.personUuid != null && employee.personUuid !== senderAccount) {
|
|
collaborators = [employee.personUuid]
|
|
|
|
const collaboratorsTx = getCollaboratorsTxes(control, employee.personUuid, doc)
|
|
|
|
res.push(...collaboratorsTx)
|
|
}
|
|
}
|
|
|
|
const filteredCollaborators = collaborators.filter(
|
|
(c) => c !== senderAccount && checkSpace(c, docSpace, control, res)
|
|
)
|
|
|
|
if (filteredCollaborators.length === 0) return res
|
|
const receivers = await getReceiversInfo(ctx, filteredCollaborators, control)
|
|
if (receivers.length === 0) return []
|
|
const senderPerson = await getPerson(control, senderId)
|
|
const sender = {
|
|
socialId: senderId,
|
|
person: senderPerson
|
|
}
|
|
const type: NotificationType = control.modelDb.findAllSync(notification.class.NotificationType, {
|
|
_id: notification.ids.MentionNotificationType
|
|
})[0]
|
|
for (const receiver of receivers) {
|
|
const data: Omit<Data<MentionInboxNotification>, 'docNotifyContext'> = {
|
|
header: activity.string.MentionedYouIn,
|
|
messageHtml: reference.message,
|
|
mentionedIn: reference.attachedDocId ?? reference.srcDocId,
|
|
mentionedInClass: reference.attachedDocClass ?? reference.srcDocClass,
|
|
objectId: reference.srcDocId,
|
|
objectClass: reference.srcDocClass,
|
|
user: receiver.account,
|
|
isViewed: false,
|
|
archived: false
|
|
}
|
|
|
|
const allowedProviders = getAllowedProviders(control, receiver.socialIds, type, notificationControl)
|
|
const notifyResult = new Map(allowedProviders.map((it) => [it, [type]]))
|
|
|
|
if (notifyResult.has(notification.providers.InboxNotificationProvider)) {
|
|
const txes = await getCommonNotificationTxes(
|
|
control.ctx,
|
|
control,
|
|
doc,
|
|
data,
|
|
receiver,
|
|
sender,
|
|
reference.srcDocId,
|
|
reference.srcDocClass,
|
|
doc.space,
|
|
originTx.modifiedOn,
|
|
notifyResult,
|
|
notification.class.MentionInboxNotification,
|
|
originTx
|
|
)
|
|
res.push(...txes)
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
function getUpdateMentionInfoTx (
|
|
control: TriggerControl,
|
|
reference: Data<ActivityReference>,
|
|
space: Ref<Space>,
|
|
info?: UserMentionInfo
|
|
): Tx {
|
|
if (info === undefined) {
|
|
return control.txFactory.createTxCreateDoc(activity.class.UserMentionInfo, space, {
|
|
attachedTo: reference.attachedDocId ?? reference.srcDocId,
|
|
attachedToClass: reference.attachedDocClass ?? reference.srcDocClass,
|
|
user: reference.attachedTo as Ref<Person>,
|
|
content: reference.message,
|
|
collection: 'mentions'
|
|
})
|
|
}
|
|
|
|
return control.txFactory.createTxUpdateDoc(info._class, info.space, info._id, {
|
|
content: reference.message
|
|
})
|
|
}
|
|
|
|
async function getMultipleMentionCollaborators (
|
|
ctx: MeasureContext,
|
|
reference: Data<ActivityReference>,
|
|
control: TriggerControl,
|
|
doc: Doc
|
|
): Promise<AccountUuid[]> {
|
|
const { hierarchy, modelDb } = control
|
|
const personRef = reference.attachedTo as Ref<Person>
|
|
|
|
const classCollaborators = getClassCollaborators(modelDb, hierarchy, doc._class)
|
|
|
|
if (classCollaborators === undefined) return []
|
|
const collaborators = (
|
|
await control.findAll(ctx, core.class.Collaborator, {
|
|
attachedTo: doc._id
|
|
})
|
|
).map((it) => it.collaborator)
|
|
|
|
if (collaborators.length === 0) return []
|
|
const statuses = Array.from(control.userStatusMap.values())
|
|
|
|
return personRef === contact.mention.Here
|
|
? collaborators.filter((it) => statuses.some((s) => s.online && s.user === it))
|
|
: collaborators
|
|
}
|
|
|
|
function checkSpace (account: AccountUuid, space: Space, control: TriggerControl, res: Tx[]): boolean {
|
|
const isMember = space.members.includes(account)
|
|
|
|
if (space.private) {
|
|
return isMember
|
|
}
|
|
|
|
if (!isMember) {
|
|
res.push(control.txFactory.createTxUpdateDoc(space._class, space.space, space._id, { $push: { members: account } }))
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
function getCollaboratorsTxes (control: TriggerControl, receiver: AccountUuid, object?: Doc): Tx[] {
|
|
const res: Tx[] = []
|
|
|
|
if (object !== undefined) {
|
|
const tx = control.txFactory.createTxCreateDoc(core.class.Collaborator, object.space, {
|
|
attachedTo: object._id,
|
|
collaborator: receiver,
|
|
attachedToClass: object._class,
|
|
collection: 'collaborators'
|
|
})
|
|
|
|
res.push(tx)
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
function isMarkupType (type: Ref<Class<Type<any>>>): boolean {
|
|
return type === core.class.TypeMarkup
|
|
}
|
|
|
|
function isCollaborativeType (type: Ref<Class<Type<any>>>): boolean {
|
|
return type === core.class.TypeCollaborativeDoc
|
|
}
|
|
|
|
async function getCreateReferencesTxes (
|
|
ctx: MeasureContext,
|
|
control: TriggerControl,
|
|
storage: StorageAdapter,
|
|
txFactory: TxFactory,
|
|
createdDoc: Doc,
|
|
srcDocId: Ref<Doc>,
|
|
srcDocClass: Ref<Class<Doc>>,
|
|
srcDocSpace: Ref<Space>,
|
|
originTx: TxCUD<Doc>
|
|
): Promise<Tx[]> {
|
|
const attachedDocId = createdDoc._id
|
|
const attachedDocClass = createdDoc._class
|
|
|
|
const refs: Data<ActivityReference>[] = []
|
|
const attributes = control.hierarchy.getAllAttributes(createdDoc._class)
|
|
|
|
for (const attr of attributes.values()) {
|
|
if (isMarkupType(attr.type._class)) {
|
|
const content: string = (createdDoc as any)[attr.name]?.toString() ?? ''
|
|
const attrReferences = getReferencesData(srcDocId, srcDocClass, attachedDocId, attachedDocClass, content)
|
|
|
|
refs.push(...attrReferences)
|
|
} else if (attr.type._class === core.class.TypeCollaborativeDoc) {
|
|
const blobId = (createdDoc as any)[attr.name] as Ref<Blob>
|
|
if (blobId != null && blobId !== '') {
|
|
try {
|
|
const buffer = await storage.read(ctx, control.workspace, blobId)
|
|
const markup = Buffer.concat(buffer as any).toString()
|
|
const attrReferences = getReferencesData(srcDocId, srcDocClass, attachedDocId, attachedDocClass, markup)
|
|
refs.push(...attrReferences)
|
|
} catch {
|
|
// do nothing, the collaborative doc does not sem to exist yet
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const refSpace: Ref<Space> = control.hierarchy.isDerived(srcDocClass, core.class.Space)
|
|
? (srcDocId as Ref<Space>)
|
|
: srcDocSpace
|
|
|
|
return await getReferencesTxes(ctx, control, txFactory, refs, refSpace, [], [], originTx)
|
|
}
|
|
|
|
async function getUpdateReferencesTxes (
|
|
ctx: MeasureContext,
|
|
control: TriggerControl,
|
|
storage: StorageAdapter,
|
|
txFactory: TxFactory,
|
|
updatedDoc: Doc,
|
|
srcDocId: Ref<Doc>,
|
|
srcDocClass: Ref<Class<Doc>>,
|
|
srcDocSpace: Ref<Space>,
|
|
originTx: TxCUD<Doc>
|
|
): Promise<Tx[]> {
|
|
const attachedDocId = updatedDoc._id
|
|
const attachedDocClass = updatedDoc._class
|
|
|
|
// collect attribute references
|
|
let hasReferenceAttrs = false
|
|
const references: Data<ActivityReference>[] = []
|
|
const attributes = control.hierarchy.getAllAttributes(updatedDoc._class)
|
|
for (const attr of attributes.values()) {
|
|
if (isMarkupType(attr.type._class)) {
|
|
hasReferenceAttrs = true
|
|
const content: string = (updatedDoc as any)[attr.name]?.toString() ?? ''
|
|
const attrReferences = getReferencesData(srcDocId, srcDocClass, attachedDocId, attachedDocClass, content)
|
|
references.push(...attrReferences)
|
|
} else if (attr.type._class === core.class.TypeCollaborativeDoc) {
|
|
hasReferenceAttrs = true
|
|
try {
|
|
const blobId = (updatedDoc as any)[attr.name] as Ref<Blob>
|
|
if (blobId != null) {
|
|
const buffer = await storage.read(ctx, control.workspace, blobId)
|
|
const markup = Buffer.concat(buffer as any).toString()
|
|
const attrReferences = getReferencesData(srcDocId, srcDocClass, attachedDocId, attachedDocClass, markup)
|
|
references.push(...attrReferences)
|
|
}
|
|
} catch {
|
|
// do nothing, the collaborative doc does not sem to exist yet
|
|
}
|
|
}
|
|
}
|
|
|
|
// There is a chance that references are managed manually
|
|
// do not update references if there are no reference sources in the doc
|
|
if (hasReferenceAttrs) {
|
|
const current = await control.findAll(ctx, activity.class.ActivityReference, {
|
|
srcDocId,
|
|
srcDocClass,
|
|
attachedDocId,
|
|
collection: 'references'
|
|
})
|
|
const userMentions = await control.findAll(ctx, activity.class.UserMentionInfo, {
|
|
attachedTo: attachedDocId
|
|
})
|
|
|
|
const refSpace: Ref<Space> = control.hierarchy.isDerived(srcDocClass, core.class.Space)
|
|
? (srcDocId as Ref<Space>)
|
|
: srcDocSpace
|
|
|
|
return await getReferencesTxes(ctx, control, txFactory, references, refSpace, current, userMentions, originTx)
|
|
}
|
|
|
|
return []
|
|
}
|
|
|
|
export function getReferencesData (
|
|
srcDocId: Ref<Doc>,
|
|
srcDocClass: Ref<Class<Doc>>,
|
|
attachedDocId: Ref<Doc> | undefined,
|
|
attachedDocClass: Ref<Class<Doc>> | undefined,
|
|
content: Markup
|
|
): Array<Data<ActivityReference>> {
|
|
const result: Array<Data<ActivityReference>> = []
|
|
const references = []
|
|
|
|
const node = markupToJSON(content)
|
|
references.push(...extractReferences(node))
|
|
|
|
for (const ref of references) {
|
|
if (ref.objectId !== attachedDocId && ref.objectId !== srcDocId) {
|
|
result.push({
|
|
attachedTo: ref.objectId,
|
|
attachedToClass: ref.objectClass,
|
|
collection: 'references',
|
|
srcDocId,
|
|
srcDocClass,
|
|
message: ref.parentNode !== null ? jsonToMarkup(ref.parentNode) : '',
|
|
attachedDocId,
|
|
attachedDocClass
|
|
})
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
async function createReferenceTxes (
|
|
ctx: MeasureContext,
|
|
control: TriggerControl,
|
|
txFactory: TxFactory,
|
|
ref: Data<ActivityReference>,
|
|
space: Ref<Space>,
|
|
originTx: TxCUD<Doc>,
|
|
notificationControl: NotificationProviderControl
|
|
): Promise<Tx[]> {
|
|
if (control.hierarchy.isDerived(ref.attachedToClass, contact.class.Person)) {
|
|
return await getPersonNotificationTxes(ctx, ref, control, txFactory.account, space, originTx, notificationControl)
|
|
}
|
|
|
|
const refTx = control.txFactory.createTxCreateDoc(activity.class.ActivityReference, space, ref)
|
|
const tx = control.txFactory.createTxCollectionCUD(ref.attachedToClass, ref.attachedTo, space, ref.collection, refTx)
|
|
|
|
return [tx]
|
|
}
|
|
|
|
async function getRemoveMentionTxes (
|
|
control: TriggerControl,
|
|
mention: UserMentionInfo,
|
|
originTx: TxCUD<Doc>
|
|
): Promise<Tx[]> {
|
|
const res: Tx[] = []
|
|
res.push(control.txFactory.createTxRemoveDoc(mention._class, mention.space, mention._id))
|
|
|
|
if (control.hierarchy.isDerived(originTx.objectClass, activity.class.ActivityMessage)) {
|
|
const _id = originTx.objectId as Ref<ActivityMessage>
|
|
const person = (
|
|
await control.findAll(control.ctx, contact.mixin.Employee, { _id: mention.user as Ref<Employee> })
|
|
)[0]
|
|
if (person?.personUuid !== undefined) {
|
|
const activityNotification = await control.findAll(control.ctx, notification.class.ActivityInboxNotification, {
|
|
attachedTo: _id,
|
|
user: person.personUuid
|
|
})
|
|
const mentionNotifications = await control.findAll(control.ctx, notification.class.MentionInboxNotification, {
|
|
mentionedIn: _id,
|
|
user: person.personUuid
|
|
})
|
|
res.push(
|
|
...activityNotification
|
|
.filter((it) => it.types?.length === 1 && it.types[0] === notification.ids.MentionNotificationType)
|
|
.map((it) => control.txFactory.createTxRemoveDoc(it._class, it.space, it._id))
|
|
)
|
|
|
|
res.push(...mentionNotifications.map((it) => control.txFactory.createTxRemoveDoc(it._class, it.space, it._id)))
|
|
}
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
async function getReferencesTxes (
|
|
ctx: MeasureContext,
|
|
control: TriggerControl,
|
|
txFactory: TxFactory,
|
|
references: Data<ActivityReference>[],
|
|
space: Ref<Space>,
|
|
current: ActivityReference[],
|
|
mentions: UserMentionInfo[],
|
|
originTx: TxCUD<Doc>
|
|
): Promise<Tx[]> {
|
|
const txes: Tx[] = []
|
|
|
|
for (const c of current) {
|
|
// Find existing and check if we need to update message
|
|
const pos = references.findIndex(
|
|
(b) => b.srcDocId === c.srcDocId && b.srcDocClass === c.srcDocClass && b.attachedTo === c.attachedTo
|
|
)
|
|
if (pos !== -1) {
|
|
// Update existing references when message changed
|
|
const data = references[pos]
|
|
if (c.message !== data.message) {
|
|
const innerTx = txFactory.createTxUpdateDoc(c._class, c.space, c._id, {
|
|
message: data.message
|
|
})
|
|
txes.push(txFactory.createTxCollectionCUD(c.attachedToClass, c.attachedTo, c.space, c.collection, innerTx))
|
|
}
|
|
references.splice(pos, 1)
|
|
} else {
|
|
// Remove not found references
|
|
const innerTx = txFactory.createTxRemoveDoc(c._class, c.space, c._id)
|
|
txes.push(txFactory.createTxCollectionCUD(c.attachedToClass, c.attachedTo, c.space, c.collection, innerTx))
|
|
}
|
|
}
|
|
|
|
const notificationControl = await getNotificationProviderControl(ctx, control)
|
|
|
|
for (const mention of mentions) {
|
|
const refIndex = references.findIndex(
|
|
(r) => mention.user === r.attachedTo && mention.attachedTo === r.attachedDocId
|
|
)
|
|
|
|
const ref = references[refIndex]
|
|
|
|
if (refIndex !== -1) {
|
|
const alreadyProcessed = areEqualJson(JSON.parse(mention.content), JSON.parse(ref.message))
|
|
|
|
if (alreadyProcessed) {
|
|
references.splice(refIndex, 1)
|
|
}
|
|
} else {
|
|
const removeTxes = await getRemoveMentionTxes(control, mention, originTx)
|
|
txes.push(...removeTxes)
|
|
}
|
|
}
|
|
|
|
// Add missing references
|
|
for (const ref of references) {
|
|
txes.push(...(await createReferenceTxes(ctx, control, txFactory, ref, space, originTx, notificationControl)))
|
|
}
|
|
|
|
return txes
|
|
}
|
|
|
|
async function getRemoveActivityReferenceTxes (
|
|
control: TriggerControl,
|
|
txFactory: TxFactory,
|
|
removedDocId: Ref<Doc>
|
|
): Promise<Tx[]> {
|
|
const txes: Tx[] = []
|
|
const refs = await control.findAll(control.ctx, activity.class.ActivityReference, {
|
|
attachedDocId: removedDocId,
|
|
collection: 'references'
|
|
})
|
|
|
|
const mentions = await control.findAll(control.ctx, activity.class.UserMentionInfo, {
|
|
attachedTo: removedDocId
|
|
})
|
|
|
|
const notifications = await control.findAll(control.ctx, notification.class.MentionInboxNotification, {
|
|
mentionedIn: removedDocId
|
|
})
|
|
|
|
for (const notification of notifications) {
|
|
const removeTx = txFactory.createTxRemoveDoc(notification._class, notification.space, notification._id)
|
|
txes.push(removeTx)
|
|
}
|
|
for (const ref of refs) {
|
|
const removeTx = txFactory.createTxRemoveDoc(ref._class, ref.space, ref._id)
|
|
txes.push(txFactory.createTxCollectionCUD(ref.attachedToClass, ref.attachedTo, ref.space, ref.collection, removeTx))
|
|
}
|
|
|
|
for (const mention of mentions) {
|
|
const removeTx = txFactory.createTxRemoveDoc(mention._class, mention.space, mention._id)
|
|
txes.push(
|
|
txFactory.createTxCollectionCUD(
|
|
mention.attachedToClass,
|
|
mention.attachedTo,
|
|
mention.space,
|
|
mention.collection,
|
|
removeTx
|
|
)
|
|
)
|
|
}
|
|
|
|
return txes
|
|
}
|
|
|
|
function guessReferenceObj (
|
|
modelDb: ModelDb,
|
|
hierarchy: Hierarchy,
|
|
tx: TxCUD<Doc>
|
|
): {
|
|
objectId: Ref<Doc>
|
|
objectClass: Ref<Class<Doc>>
|
|
} {
|
|
// Try to guess reference target Tx for TxCollectionCUD txes based on collaborators availability
|
|
if (tx.attachedToClass !== undefined && tx.attachedTo !== undefined) {
|
|
if (hierarchy.isDerived(tx.objectClass, activity.class.ActivityMessage)) {
|
|
return {
|
|
objectId: tx.attachedTo,
|
|
objectClass: tx.attachedToClass
|
|
}
|
|
}
|
|
|
|
const mixin = getClassCollaborators(modelDb, hierarchy, tx.objectClass)
|
|
return mixin !== undefined
|
|
? {
|
|
objectId: tx.objectId,
|
|
objectClass: tx.objectClass
|
|
}
|
|
: {
|
|
objectId: tx.attachedTo,
|
|
objectClass: tx.attachedToClass
|
|
}
|
|
}
|
|
return {
|
|
objectId: tx.objectId,
|
|
objectClass: tx.objectClass
|
|
}
|
|
}
|
|
|
|
async function ActivityReferenceCreate (tx: TxCUD<Doc>, control: TriggerControl): Promise<Tx[]> {
|
|
const ctx = tx as TxCreateDoc<Doc>
|
|
|
|
if (ctx._class !== core.class.TxCreateDoc) return []
|
|
if (control.hierarchy.isDerived(ctx.objectClass, notification.class.InboxNotification)) return []
|
|
if (control.hierarchy.isDerived(ctx.objectClass, activity.class.ActivityReference)) return []
|
|
|
|
const txFactory = new TxFactory(control.txFactory.account)
|
|
|
|
const doc = TxProcessor.createDoc2Doc(ctx)
|
|
const target = guessReferenceObj(control.modelDb, control.hierarchy, tx)
|
|
|
|
const txes: Tx[] = await getCreateReferencesTxes(
|
|
control.ctx,
|
|
control,
|
|
control.storageAdapter,
|
|
txFactory,
|
|
doc,
|
|
target.objectId,
|
|
target.objectClass,
|
|
tx.objectSpace,
|
|
tx
|
|
)
|
|
|
|
if (txes.length !== 0) {
|
|
await control.apply(control.ctx, txes)
|
|
}
|
|
|
|
return []
|
|
}
|
|
|
|
async function ActivityReferenceUpdate (tx: TxCUD<Doc>, control: TriggerControl): Promise<Tx[]> {
|
|
const ctx = tx as TxUpdateDoc<Doc>
|
|
const attributes = control.hierarchy.getAllAttributes(ctx.objectClass)
|
|
|
|
let hasUpdates = false
|
|
|
|
for (const attr of attributes.values()) {
|
|
if (isMarkupType(attr.type._class) || isCollaborativeType(attr.type._class)) {
|
|
if (TxProcessor.txHasUpdate(ctx, attr.name)) {
|
|
hasUpdates = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!hasUpdates) {
|
|
return []
|
|
}
|
|
|
|
const rawDoc = (await control.findAll(control.ctx, ctx.objectClass, { _id: ctx.objectId }))[0]
|
|
|
|
if (rawDoc === undefined) {
|
|
return []
|
|
}
|
|
|
|
const txFactory = new TxFactory(control.txFactory.account)
|
|
const doc = TxProcessor.updateDoc2Doc(rawDoc, ctx)
|
|
const target = guessReferenceObj(control.modelDb, control.hierarchy, tx)
|
|
|
|
const txes: Tx[] = await getUpdateReferencesTxes(
|
|
control.ctx,
|
|
control,
|
|
control.storageAdapter,
|
|
txFactory,
|
|
doc,
|
|
target.objectId,
|
|
target.objectClass,
|
|
tx.objectSpace,
|
|
tx
|
|
)
|
|
|
|
if (txes.length !== 0) {
|
|
await control.apply(control.ctx, txes)
|
|
}
|
|
|
|
return []
|
|
}
|
|
|
|
async function ActivityReferenceRemove (tx: TxCUD<Doc>, control: TriggerControl): Promise<Tx[]> {
|
|
const ctx = tx as TxRemoveDoc<Doc>
|
|
const attributes = control.hierarchy.getAllAttributes(ctx.objectClass)
|
|
|
|
let hasMarkdown = false
|
|
|
|
for (const attr of attributes.values()) {
|
|
if (isMarkupType(attr.type._class) || isCollaborativeType(attr.type._class)) {
|
|
hasMarkdown = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if (hasMarkdown) {
|
|
const txFactory = new TxFactory(control.txFactory.account)
|
|
|
|
const txes: Tx[] = await getRemoveActivityReferenceTxes(control, txFactory, ctx.objectId)
|
|
if (txes.length !== 0) {
|
|
await control.apply(control.ctx, txes)
|
|
}
|
|
}
|
|
|
|
return []
|
|
}
|
|
|
|
/**
|
|
* @public
|
|
*/
|
|
export async function ReferenceTrigger (txes: TxCUD<Doc>[], control: TriggerControl): Promise<Tx[]> {
|
|
const result: Tx[] = []
|
|
|
|
for (const tx of txes) {
|
|
if (control.hierarchy.isDerived(tx.objectClass, activity.class.ActivityReference)) continue
|
|
if (control.hierarchy.isDerived(tx.objectClass, notification.class.InboxNotification)) continue
|
|
if (control.hierarchy.isDerived(tx.objectClass, activity.class.UserMentionInfo)) continue
|
|
|
|
if (tx._class === core.class.TxCreateDoc) {
|
|
result.push(...(await ActivityReferenceCreate(tx, control)))
|
|
}
|
|
if (tx._class === core.class.TxUpdateDoc) {
|
|
result.push(...(await ActivityReferenceUpdate(tx, control)))
|
|
}
|
|
if (tx._class === core.class.TxRemoveDoc) {
|
|
result.push(...(await ActivityReferenceRemove(tx, control)))
|
|
}
|
|
}
|
|
return result
|
|
}
|