2022-01-20 12:27:41 +03:00
|
|
|
//
|
|
|
|
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
2023-03-03 20:02:11 +03:00
|
|
|
// Copyright © 2021, 2022, 2023 Hardcore Engineering Inc.
|
2022-01-20 12:27:41 +03:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
|
2023-11-17 10:35:09 +03:00
|
|
|
import contact, {
|
|
|
|
Channel,
|
|
|
|
Contact,
|
2024-05-08 12:41:44 +03:00
|
|
|
Employee,
|
2023-11-17 10:35:09 +03:00
|
|
|
Organization,
|
|
|
|
Person,
|
2024-02-05 17:36:50 +03:00
|
|
|
PersonAccount,
|
2024-08-19 08:41:31 +03:00
|
|
|
PersonSpace,
|
2023-11-17 10:35:09 +03:00
|
|
|
contactId,
|
2024-02-05 11:47:52 +03:00
|
|
|
formatContactName,
|
|
|
|
formatName,
|
|
|
|
getFirstName,
|
2024-02-05 17:36:50 +03:00
|
|
|
getLastName,
|
2024-08-19 08:41:31 +03:00
|
|
|
getName
|
2023-11-17 10:35:09 +03:00
|
|
|
} from '@hcengineering/contact'
|
2024-05-08 12:41:44 +03:00
|
|
|
import core, {
|
2024-05-17 09:05:05 +03:00
|
|
|
Account,
|
2024-05-08 12:41:44 +03:00
|
|
|
Class,
|
|
|
|
Doc,
|
|
|
|
Hierarchy,
|
|
|
|
Ref,
|
2024-05-17 09:05:05 +03:00
|
|
|
SpaceType,
|
2024-05-08 12:41:44 +03:00
|
|
|
Tx,
|
2024-08-19 08:41:31 +03:00
|
|
|
TxCUD,
|
2024-05-08 12:41:44 +03:00
|
|
|
TxCreateDoc,
|
|
|
|
TxMixin,
|
|
|
|
TxProcessor,
|
|
|
|
TxRemoveDoc,
|
|
|
|
TxUpdateDoc,
|
2024-08-19 08:41:31 +03:00
|
|
|
concatLink
|
2024-05-08 12:41:44 +03:00
|
|
|
} from '@hcengineering/core'
|
2023-04-10 12:15:00 +03:00
|
|
|
import notification, { Collaborators } from '@hcengineering/notification'
|
2022-11-16 18:03:03 +03:00
|
|
|
import { getMetadata } from '@hcengineering/platform'
|
2024-09-03 22:32:22 +03:00
|
|
|
import serverCore, { TriggerControl } from '@hcengineering/server-core'
|
2022-11-16 18:03:03 +03:00
|
|
|
import { workbenchId } from '@hcengineering/workbench'
|
2022-01-20 12:27:41 +03:00
|
|
|
|
2024-05-17 09:05:05 +03:00
|
|
|
export async function OnSpaceTypeMembers (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
|
|
|
const ctx = tx as TxUpdateDoc<SpaceType>
|
|
|
|
const result: Tx[] = []
|
|
|
|
const newMember = ctx.operations.$push?.members as Ref<Account>
|
|
|
|
if (newMember !== undefined) {
|
2024-09-09 11:40:49 +03:00
|
|
|
const spaces = await control.findAll(control.ctx, core.class.Space, { type: ctx.objectId })
|
2024-05-17 09:05:05 +03:00
|
|
|
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<Account>
|
|
|
|
if (ctx.operations.$pull?.members !== undefined) {
|
2024-09-09 11:40:49 +03:00
|
|
|
const spaces = await control.findAll(control.ctx, core.class.Space, { type: ctx.objectId })
|
2024-05-17 09:05:05 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-05-08 12:41:44 +03:00
|
|
|
export async function OnEmployeeCreate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
|
|
|
const mixinTx = tx as TxMixin<Person, Employee>
|
|
|
|
if (mixinTx.attributes.active !== true) return []
|
2024-08-19 08:41:31 +03:00
|
|
|
const acc = control.modelDb.getAccountByPersonId(mixinTx.objectId)
|
|
|
|
if (acc.length === 0) return []
|
2024-09-09 11:40:49 +03:00
|
|
|
const spaces = await control.findAll(control.ctx, core.class.Space, { autoJoin: true })
|
2024-05-08 12:41:44 +03:00
|
|
|
const result: Tx[] = []
|
2024-08-06 12:44:23 +03:00
|
|
|
|
2024-08-19 08:41:31 +03:00
|
|
|
const txes = await createPersonSpace(
|
|
|
|
acc.map((it) => it._id),
|
|
|
|
mixinTx.objectId,
|
|
|
|
control
|
|
|
|
)
|
2024-08-06 12:44:23 +03:00
|
|
|
result.push(...txes)
|
|
|
|
|
2024-05-08 12:41:44 +03:00
|
|
|
for (const space of spaces) {
|
2024-08-19 08:41:31 +03:00
|
|
|
const toAdd = acc.filter((it) => !space.members.includes(it._id))
|
|
|
|
if (toAdd.length === 0) continue
|
|
|
|
for (const a of toAdd) {
|
|
|
|
const pushTx = control.txFactory.createTxUpdateDoc(space._class, space.space, space._id, {
|
|
|
|
$push: {
|
|
|
|
members: a._id
|
|
|
|
}
|
|
|
|
})
|
|
|
|
result.push(pushTx)
|
|
|
|
}
|
2024-05-08 12:41:44 +03:00
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2024-08-06 12:44:23 +03:00
|
|
|
async function createPersonSpace (
|
2024-08-19 08:41:31 +03:00
|
|
|
account: Ref<Account>[],
|
2024-08-06 12:44:23 +03:00
|
|
|
person: Ref<Person>,
|
|
|
|
control: TriggerControl
|
|
|
|
): Promise<TxCUD<PersonSpace>[]> {
|
2024-09-09 11:40:49 +03:00
|
|
|
const personSpace = (await control.findAll(control.ctx, contact.class.PersonSpace, { person }, { limit: 1 })).shift()
|
2024-08-06 12:44:23 +03:00
|
|
|
if (personSpace !== undefined) {
|
2024-08-19 08:41:31 +03:00
|
|
|
const toAdd = account.filter((it) => !personSpace.members.includes(it))
|
|
|
|
if (toAdd.length === 0) return []
|
|
|
|
return toAdd.map((it) =>
|
2024-08-06 12:44:23 +03:00
|
|
|
control.txFactory.createTxUpdateDoc(personSpace._class, personSpace.space, personSpace._id, {
|
|
|
|
$push: {
|
2024-08-19 08:41:31 +03:00
|
|
|
members: it
|
2024-08-06 12:44:23 +03:00
|
|
|
}
|
|
|
|
})
|
2024-08-19 08:41:31 +03:00
|
|
|
)
|
2024-08-06 12:44:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return [
|
|
|
|
control.txFactory.createTxCreateDoc(contact.class.PersonSpace, core.space.Space, {
|
|
|
|
name: 'Personal space',
|
|
|
|
description: '',
|
|
|
|
private: true,
|
|
|
|
archived: false,
|
|
|
|
person,
|
2024-08-19 08:41:31 +03:00
|
|
|
members: account
|
2024-08-06 12:44:23 +03:00
|
|
|
})
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
2024-05-08 12:41:44 +03:00
|
|
|
export async function OnPersonAccountCreate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
|
|
|
const acc = TxProcessor.createDoc2Doc(tx as TxCreateDoc<PersonAccount>)
|
|
|
|
const person = (
|
2024-09-09 11:40:49 +03:00
|
|
|
await control.findAll(
|
|
|
|
control.ctx,
|
|
|
|
contact.mixin.Employee,
|
|
|
|
{ _id: acc.person as Ref<Employee>, active: true },
|
|
|
|
{ limit: 1 }
|
|
|
|
)
|
2024-05-08 12:41:44 +03:00
|
|
|
)[0]
|
|
|
|
if (person === undefined) return []
|
2024-09-09 11:40:49 +03:00
|
|
|
const spaces = await control.findAll(control.ctx, core.class.Space, { autoJoin: true })
|
2024-08-06 12:44:23 +03:00
|
|
|
|
2024-05-08 12:41:44 +03:00
|
|
|
const result: Tx[] = []
|
2024-08-19 08:41:31 +03:00
|
|
|
const txes = await createPersonSpace([acc._id], person._id, control)
|
2024-08-06 12:44:23 +03:00
|
|
|
|
|
|
|
result.push(...txes)
|
|
|
|
|
2024-05-08 12:41:44 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-01-20 12:27:41 +03:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
2023-02-07 07:46:28 +03:00
|
|
|
export async function OnContactDelete (
|
|
|
|
tx: Tx,
|
2024-05-10 09:10:04 +03:00
|
|
|
{ findAll, hierarchy, storageAdapter, workspace, removedMap, txFactory, ctx }: TriggerControl
|
2023-02-07 07:46:28 +03:00
|
|
|
): Promise<Tx[]> {
|
2022-01-20 12:27:41 +03:00
|
|
|
const rmTx = tx as TxRemoveDoc<Contact>
|
|
|
|
|
2023-02-07 07:46:28 +03:00
|
|
|
const removeContact = removedMap.get(rmTx.objectId) as Contact
|
|
|
|
if (removeContact === undefined) {
|
2022-01-20 12:27:41 +03:00
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
2023-02-21 16:45:05 +03:00
|
|
|
const result: Tx[] = []
|
|
|
|
|
2024-09-09 11:40:49 +03:00
|
|
|
const members = await findAll(ctx, contact.class.Member, { contact: removeContact._id })
|
2023-02-21 16:45:05 +03:00
|
|
|
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
|
2022-01-20 12:27:41 +03:00
|
|
|
}
|
|
|
|
|
2023-04-10 12:15:00 +03:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export async function OnChannelUpdate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
|
|
|
const uTx = tx as TxUpdateDoc<Channel>
|
|
|
|
|
|
|
|
const result: Tx[] = []
|
|
|
|
|
|
|
|
if (uTx.operations.$inc?.items !== undefined) {
|
2024-09-09 11:40:49 +03:00
|
|
|
const doc = (await control.findAll(control.ctx, uTx.objectClass, { _id: uTx.objectId }, { limit: 1 }))[0]
|
2023-04-10 12:15:00 +03:00
|
|
|
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 {
|
2023-05-19 05:21:42 +03:00
|
|
|
const res = control.txFactory.createTxMixin<Doc, Collaborators>(
|
2023-04-10 12:15:00 +03:00
|
|
|
doc._id,
|
|
|
|
doc._class,
|
|
|
|
doc.space,
|
|
|
|
notification.mixin.Collaborators,
|
|
|
|
{
|
|
|
|
collaborators: [tx.modifiedBy]
|
|
|
|
}
|
|
|
|
)
|
2023-05-19 05:21:42 +03:00
|
|
|
result.push(res)
|
2023-04-10 12:15:00 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2022-02-22 12:09:13 +03:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
2023-03-15 17:06:03 +03:00
|
|
|
export async function personHTMLPresenter (doc: Doc, control: TriggerControl): Promise<string> {
|
2022-02-22 12:09:13 +03:00
|
|
|
const person = doc as Person
|
2024-06-20 16:13:57 +03:00
|
|
|
const front = control.branding?.front ?? getMetadata(serverCore.metadata.FrontUrl) ?? ''
|
2024-02-02 12:27:27 +03:00
|
|
|
const path = `${workbenchId}/${control.workspace.workspaceUrl}/${contactId}/${doc._id}`
|
2023-02-01 14:27:15 +03:00
|
|
|
const link = concatLink(front, path)
|
2024-06-20 16:13:57 +03:00
|
|
|
return `<a href="${link}">${getName(control.hierarchy, person, control.branding?.lastNameFirst)}</a>`
|
2022-02-22 12:09:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
2023-08-04 21:06:21 +03:00
|
|
|
export function personTextPresenter (doc: Doc, control: TriggerControl): string {
|
2022-02-22 12:09:13 +03:00
|
|
|
const person = doc as Person
|
2024-06-20 16:13:57 +03:00
|
|
|
return `${getName(control.hierarchy, person, control.branding?.lastNameFirst)}`
|
2022-02-22 12:09:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
2023-03-15 17:06:03 +03:00
|
|
|
export async function organizationHTMLPresenter (doc: Doc, control: TriggerControl): Promise<string> {
|
2022-02-22 12:09:13 +03:00
|
|
|
const organization = doc as Organization
|
2024-06-20 16:13:57 +03:00
|
|
|
const front = control.branding?.front ?? getMetadata(serverCore.metadata.FrontUrl) ?? ''
|
2024-02-02 12:27:27 +03:00
|
|
|
const path = `${workbenchId}/${control.workspace.workspaceUrl}/${contactId}/${doc._id}`
|
2023-02-01 14:27:15 +03:00
|
|
|
const link = concatLink(front, path)
|
|
|
|
return `<a href="${link}">${organization.name}</a>`
|
2022-02-22 12:09:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export function organizationTextPresenter (doc: Doc): string {
|
|
|
|
const organization = doc as Organization
|
|
|
|
return `${organization.name}`
|
|
|
|
}
|
|
|
|
|
2023-11-17 10:35:09 +03:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
2023-11-20 13:01:43 +03:00
|
|
|
export function contactNameProvider (hierarchy: Hierarchy, props: Record<string, string>): string {
|
2023-11-17 10:35:09 +03:00
|
|
|
const _class = props._class !== undefined ? (props._class as Ref<Class<Doc>>) : contact.class.Contact
|
2024-06-20 16:13:57 +03:00
|
|
|
return formatContactName(hierarchy, _class, props.name ?? '', props.lastNameFirst)
|
2023-11-17 10:35:09 +03:00
|
|
|
}
|
|
|
|
|
2024-02-05 11:47:52 +03:00
|
|
|
export async function getCurrentEmployeeName (control: TriggerControl, context: Record<string, Doc>): Promise<string> {
|
|
|
|
const account = await control.modelDb.findOne(contact.class.PersonAccount, {
|
|
|
|
_id: control.txFactory.account as Ref<PersonAccount>
|
|
|
|
})
|
|
|
|
if (account === undefined) return ''
|
2024-09-09 11:40:49 +03:00
|
|
|
const employee = (await control.findAll(control.ctx, contact.class.Person, { _id: account.person }))[0]
|
2024-06-20 16:13:57 +03:00
|
|
|
return employee !== undefined ? formatName(employee.name, control.branding?.lastNameFirst) : ''
|
2024-02-05 11:47:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
export async function getCurrentEmployeeEmail (control: TriggerControl, context: Record<string, Doc>): Promise<string> {
|
|
|
|
const account = await control.modelDb.findOne(contact.class.PersonAccount, {
|
|
|
|
_id: control.txFactory.account as Ref<PersonAccount>
|
|
|
|
})
|
|
|
|
if (account === undefined) return ''
|
|
|
|
return account.email
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function getCurrentEmployeePosition (
|
|
|
|
control: TriggerControl,
|
|
|
|
context: Record<string, Doc>
|
|
|
|
): Promise<string | undefined> {
|
|
|
|
const account = await control.modelDb.findOne(contact.class.PersonAccount, {
|
|
|
|
_id: control.txFactory.account as Ref<PersonAccount>
|
|
|
|
})
|
|
|
|
if (account === undefined) return ''
|
2024-09-09 11:40:49 +03:00
|
|
|
const employee = (await control.findAll(control.ctx, contact.class.Person, { _id: account.person }))[0]
|
2024-02-05 11:47:52 +03:00
|
|
|
if (employee !== undefined) {
|
2024-02-05 17:36:50 +03:00
|
|
|
return control.hierarchy.as(employee, contact.mixin.Employee)?.position ?? ''
|
2024-02-05 11:47:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function getContactName (
|
|
|
|
control: TriggerControl,
|
|
|
|
context: Record<string, Doc>
|
|
|
|
): Promise<string | undefined> {
|
|
|
|
const value = context[contact.class.Contact] as Contact
|
|
|
|
if (value === undefined) return
|
|
|
|
if (control.hierarchy.isDerived(value._class, contact.class.Person)) {
|
2024-06-20 16:13:57 +03:00
|
|
|
return getName(control.hierarchy, value, control.branding?.lastNameFirst)
|
2024-02-05 11:47:52 +03:00
|
|
|
} else {
|
|
|
|
return value.name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function getContactLastName (
|
|
|
|
control: TriggerControl,
|
|
|
|
context: Record<string, Doc>
|
|
|
|
): Promise<string | undefined> {
|
|
|
|
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<string, Doc>
|
|
|
|
): Promise<string | undefined> {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-20 12:27:41 +03:00
|
|
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
|
|
export default async () => ({
|
|
|
|
trigger: {
|
2024-05-08 12:41:44 +03:00
|
|
|
OnEmployeeCreate,
|
|
|
|
OnPersonAccountCreate,
|
2023-03-03 20:02:11 +03:00
|
|
|
OnContactDelete,
|
2024-05-17 09:05:05 +03:00
|
|
|
OnChannelUpdate,
|
|
|
|
OnSpaceTypeMembers
|
2022-02-22 12:09:13 +03:00
|
|
|
},
|
|
|
|
function: {
|
|
|
|
PersonHTMLPresenter: personHTMLPresenter,
|
|
|
|
PersonTextPresenter: personTextPresenter,
|
|
|
|
OrganizationHTMLPresenter: organizationHTMLPresenter,
|
2023-11-17 10:35:09 +03:00
|
|
|
OrganizationTextPresenter: organizationTextPresenter,
|
2024-02-05 11:47:52 +03:00
|
|
|
ContactNameProvider: contactNameProvider,
|
|
|
|
GetCurrentEmployeeName: getCurrentEmployeeName,
|
|
|
|
GetCurrentEmployeeEmail: getCurrentEmployeeEmail,
|
|
|
|
GetContactName: getContactName,
|
|
|
|
GetCurrentEmployeePosition: getCurrentEmployeePosition,
|
|
|
|
GetContactFirstName: getContactFirstName,
|
|
|
|
GetContactLastName: getContactLastName
|
2022-01-20 12:27:41 +03:00
|
|
|
}
|
|
|
|
})
|