// // Copyright © 2020, 2021 Anticrm Platform Contributors. // Copyright © 2021, 2022, 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 contact, { Channel, Contact, Employee, Organization, Person, PersonAccount, contactId, formatContactName, formatName, getFirstName, getLastName, getName } from '@hcengineering/contact' import core, { Account, Class, Doc, Hierarchy, Ref, SpaceType, Tx, TxCreateDoc, TxMixin, TxProcessor, TxRemoveDoc, TxUpdateDoc, concatLink } from '@hcengineering/core' import notification, { Collaborators } from '@hcengineering/notification' import { getMetadata } from '@hcengineering/platform' import serverCore, { TriggerControl, removeAllObjects } from '@hcengineering/server-core' import { workbenchId } from '@hcengineering/workbench' export async function OnSpaceTypeMembers (tx: Tx, control: TriggerControl): Promise { const ctx = tx as TxUpdateDoc const result: Tx[] = [] const newMember = ctx.operations.$push?.members as Ref if (newMember !== undefined) { const spaces = await control.findAll(core.class.Space, { type: ctx.objectId }) for (const space of spaces) { if (space.members.includes(newMember)) continue const pushTx = control.txFactory.createTxUpdateDoc(space._class, space.space, space._id, { $push: { members: newMember } }) result.push(pushTx) } } const oldMember = ctx.operations.$pull?.members as Ref if (ctx.operations.$pull?.members !== undefined) { const spaces = await control.findAll(core.class.Space, { type: ctx.objectId }) for (const space of spaces) { if (!space.members.includes(oldMember)) continue const pullTx = control.txFactory.createTxUpdateDoc(space._class, space.space, space._id, { $pull: { members: oldMember } }) result.push(pullTx) } } return result } export async function OnEmployeeCreate (tx: Tx, control: TriggerControl): Promise { const mixinTx = tx as TxMixin if (mixinTx.attributes.active !== true) return [] const acc = await control.modelDb.findOne(contact.class.PersonAccount, { person: mixinTx.objectId }) if (acc === undefined) return [] const spaces = await control.findAll(core.class.Space, { autoJoin: true }) const result: Tx[] = [] for (const space of spaces) { if (space.members.includes(acc._id)) continue const pushTx = control.txFactory.createTxUpdateDoc(space._class, space.space, space._id, { $push: { members: acc._id } }) result.push(pushTx) } return result } export async function OnPersonAccountCreate (tx: Tx, control: TriggerControl): Promise { const acc = TxProcessor.createDoc2Doc(tx as TxCreateDoc) const person = ( await control.findAll(contact.mixin.Employee, { _id: acc.person as Ref, active: true }, { limit: 1 }) )[0] if (person === undefined) return [] const spaces = await control.findAll(core.class.Space, { autoJoin: true }) const result: Tx[] = [] for (const space of spaces) { if (space.members.includes(acc._id)) continue const pushTx = control.txFactory.createTxUpdateDoc(space._class, space.space, space._id, { $push: { members: acc._id } }) result.push(pushTx) } return result } /** * @public */ export async function OnContactDelete ( tx: Tx, { findAll, hierarchy, storageAdapter, workspace, removedMap, txFactory, ctx }: TriggerControl ): Promise { const rmTx = tx as TxRemoveDoc const removeContact = removedMap.get(rmTx.objectId) as Contact if (removeContact === undefined) { return [] } if (removeContact.avatar == null) { return [] } await removeAllObjects(ctx, storageAdapter, workspace, removeContact.avatar) const result: Tx[] = [] const members = await findAll(contact.class.Member, { contact: removeContact._id }) for (const member of members) { const removeTx = txFactory.createTxRemoveDoc(member._class, member.space, member._id) const tx = txFactory.createTxCollectionCUD( member.attachedToClass, member.attachedTo, member.space, member.collection, removeTx ) result.push(tx) } return result } /** * @public */ export async function OnChannelUpdate (tx: Tx, control: TriggerControl): Promise { const uTx = tx as TxUpdateDoc const result: Tx[] = [] if (uTx.operations.$inc?.items !== undefined) { const doc = (await control.findAll(uTx.objectClass, { _id: uTx.objectId }, { limit: 1 }))[0] if (doc !== undefined) { if (control.hierarchy.hasMixin(doc, notification.mixin.Collaborators)) { const collab = control.hierarchy.as(doc, notification.mixin.Collaborators) as Doc as Collaborators if (collab.collaborators.includes(tx.modifiedBy)) { result.push( control.txFactory.createTxMixin(doc._id, doc._class, doc.space, notification.mixin.Collaborators, { $push: { collaborators: tx.modifiedBy } }) ) } } else { const res = control.txFactory.createTxMixin( doc._id, doc._class, doc.space, notification.mixin.Collaborators, { collaborators: [tx.modifiedBy] } ) result.push(res) } } } return result } /** * @public */ export async function personHTMLPresenter (doc: Doc, control: TriggerControl): Promise { const person = doc as Person const front = control.branding?.front ?? getMetadata(serverCore.metadata.FrontUrl) ?? '' const path = `${workbenchId}/${control.workspace.workspaceUrl}/${contactId}/${doc._id}` const link = concatLink(front, path) return `${getName(control.hierarchy, person, control.branding?.lastNameFirst)}` } /** * @public */ export function personTextPresenter (doc: Doc, control: TriggerControl): string { const person = doc as Person return `${getName(control.hierarchy, person, control.branding?.lastNameFirst)}` } /** * @public */ export async function organizationHTMLPresenter (doc: Doc, control: TriggerControl): Promise { const organization = doc as Organization const front = control.branding?.front ?? getMetadata(serverCore.metadata.FrontUrl) ?? '' const path = `${workbenchId}/${control.workspace.workspaceUrl}/${contactId}/${doc._id}` const link = concatLink(front, path) return `${organization.name}` } /** * @public */ export function organizationTextPresenter (doc: Doc): string { const organization = doc as Organization return `${organization.name}` } /** * @public */ export function contactNameProvider (hierarchy: Hierarchy, props: Record): string { const _class = props._class !== undefined ? (props._class as Ref>) : contact.class.Contact return formatContactName(hierarchy, _class, props.name ?? '', props.lastNameFirst) } export async function getCurrentEmployeeName (control: TriggerControl, context: Record): Promise { const account = await control.modelDb.findOne(contact.class.PersonAccount, { _id: control.txFactory.account as Ref }) if (account === undefined) return '' const employee = (await control.findAll(contact.class.Person, { _id: account.person }))[0] return employee !== undefined ? formatName(employee.name, control.branding?.lastNameFirst) : '' } export async function getCurrentEmployeeEmail (control: TriggerControl, context: Record): Promise { const account = await control.modelDb.findOne(contact.class.PersonAccount, { _id: control.txFactory.account as Ref }) if (account === undefined) return '' return account.email } export async function getCurrentEmployeePosition ( control: TriggerControl, context: Record ): Promise { const account = await control.modelDb.findOne(contact.class.PersonAccount, { _id: control.txFactory.account as Ref }) if (account === undefined) return '' const employee = (await control.findAll(contact.class.Person, { _id: account.person }))[0] if (employee !== undefined) { return control.hierarchy.as(employee, contact.mixin.Employee)?.position ?? '' } } export async function getContactName ( control: TriggerControl, context: Record ): Promise { const value = context[contact.class.Contact] as Contact if (value === undefined) return if (control.hierarchy.isDerived(value._class, contact.class.Person)) { return getName(control.hierarchy, value, control.branding?.lastNameFirst) } else { return value.name } } export async function getContactLastName ( control: TriggerControl, context: Record ): Promise { const value = context[contact.class.Contact] as Contact if (value === undefined) return if (control.hierarchy.isDerived(value._class, contact.class.Person)) { return getLastName(value.name) } else { return '' } } export async function getContactFirstName ( control: TriggerControl, context: Record ): Promise { const value = context[contact.class.Contact] as Contact if (value === undefined) return if (control.hierarchy.isDerived(value._class, contact.class.Person)) { return getFirstName(value.name) } else { return value.name } } // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export default async () => ({ trigger: { OnEmployeeCreate, OnPersonAccountCreate, OnContactDelete, OnChannelUpdate, OnSpaceTypeMembers }, function: { PersonHTMLPresenter: personHTMLPresenter, PersonTextPresenter: personTextPresenter, OrganizationHTMLPresenter: organizationHTMLPresenter, OrganizationTextPresenter: organizationTextPresenter, ContactNameProvider: contactNameProvider, GetCurrentEmployeeName: getCurrentEmployeeName, GetCurrentEmployeeEmail: getCurrentEmployeeEmail, GetContactName: getContactName, GetCurrentEmployeePosition: getCurrentEmployeePosition, GetContactFirstName: getContactFirstName, GetContactLastName: getContactLastName } })