Calendar event triggers ()

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2024-09-29 21:45:11 +05:00 committed by GitHub
parent 2f0223faad
commit 41a228387a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 193 additions and 12 deletions
models/server-calendar/src
plugins/calendar-resources/src/components
server-plugins
calendar-resources/src
calendar/src
services/calendar/pod-calendar/src

View File

@ -41,6 +41,13 @@ export function createModel (builder: Builder): void {
}
})
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverCalendar.trigger.OnEvent,
txMatch: {
'tx.objectClass': calendar.class.Event
}
})
builder.mixin<Class<Doc>, ObjectDDParticipant>(
core.class.Doc,
core.class.Class,

View File

@ -211,6 +211,7 @@
focusIndex={10007}
kind={'indented'}
showButtons={false}
readonly={readOnly}
placeholder={calendar.string.Description}
bind:content={description}
/>

View File

@ -13,10 +13,11 @@
// limitations under the License.
//
import calendar, { Calendar, Event } from '@hcengineering/calendar'
import { PersonAccount } from '@hcengineering/contact'
import {
import calendar, { Calendar, Event, ExternalCalendar } from '@hcengineering/calendar'
import contactPlugin, { Contact, Person, PersonAccount } from '@hcengineering/contact'
import core, {
Class,
Data,
Doc,
DocumentQuery,
FindOptions,
@ -25,7 +26,10 @@ import {
Ref,
Tx,
TxCreateDoc,
TxProcessor
TxCUD,
TxProcessor,
TxRemoveDoc,
TxUpdateDoc
} from '@hcengineering/core'
import { getResource } from '@hcengineering/platform'
import { TriggerControl } from '@hcengineering/server-core'
@ -98,6 +102,170 @@ export async function OnPersonAccountCreate (tx: Tx, control: TriggerControl): P
return [res]
}
function getCalendar (calendars: Calendar[], person: Ref<PersonAccount>): Ref<Calendar> | undefined {
const filtered = calendars.filter((c) => (c.createdBy ?? c.modifiedBy) === person)
const defaultExternal = filtered.find((c) => (c as ExternalCalendar).default)
if (defaultExternal !== undefined) return defaultExternal._id
return filtered[0]?._id
}
function getEventPerson (current: Event, calendars: Calendar[], control: TriggerControl): Ref<Contact> | undefined {
const calendar = calendars.find((c) => c._id === current.calendar)
if (calendar === undefined) return
const accId = (current.createdBy ?? current.modifiedBy) as Ref<PersonAccount>
const acc = control.modelDb.findAllSync(contactPlugin.class.PersonAccount, { _id: accId })[0]
if (acc === undefined) return
return acc.person
}
async function OnEvent (tx: Tx, control: TriggerControl): Promise<Tx[]> {
const ctx = TxProcessor.extractTx(tx) as TxCUD<Event>
if (ctx._class === core.class.TxCreateDoc) {
return await onEventCreate(ctx as TxCreateDoc<Event>, control)
} else if (ctx._class === core.class.TxUpdateDoc) {
return await onEventUpdate(ctx as TxUpdateDoc<Event>, control)
} else if (ctx._class === core.class.TxRemoveDoc) {
return await onRemoveEvent(ctx as TxRemoveDoc<Event>, control)
}
return []
}
async function onEventUpdate (ctx: TxUpdateDoc<Event>, control: TriggerControl): Promise<Tx[]> {
const ops = ctx.operations
const { visibility, ...otherOps } = ops
if (Object.keys(otherOps).length === 0) return []
const event = (await control.findAll(control.ctx, calendar.class.Event, { _id: ctx.objectId }, { limit: 1 }))[0]
if (event === undefined) return []
if (event.access !== 'owner') return []
const events = await control.findAll(control.ctx, calendar.class.Event, { eventId: event.eventId })
const res: Tx[] = []
const newParticipants = new Set<Ref<Contact>>(event.participants)
const calendars = await control.findAll(control.ctx, calendar.class.Calendar, { hidden: false })
for (const ev of events) {
if (ev._id === event._id) continue
const person = getEventPerson(ev, calendars, control)
if (person === undefined || !event.participants.includes(person)) {
const innerTx = control.txFactory.createTxRemoveDoc(ev._class, ev.space, ev._id)
const outerTx = control.txFactory.createTxCollectionCUD(
ev.attachedToClass,
ev.attachedTo,
ev.space,
ev.collection,
innerTx
)
res.push(outerTx)
} else {
newParticipants.delete(person)
const innerTx = control.txFactory.createTxUpdateDoc(ev._class, ev.space, ev._id, { ...otherOps })
const outerTx = control.txFactory.createTxCollectionCUD(
ev.attachedToClass,
ev.attachedTo,
ev.space,
ev.collection,
innerTx
)
res.push(outerTx)
}
}
if (newParticipants.size === 0) return res
const newPartTxs = await eventForNewParticipants(event, newParticipants, calendars, control)
return res.concat(newPartTxs)
}
async function eventForNewParticipants (
event: Event,
newParticipants: Set<Ref<Person>>,
calendars: Calendar[],
control: TriggerControl
): Promise<Tx[]> {
const res: Tx[] = []
const accounts = await control.findAll(control.ctx, contactPlugin.class.PersonAccount, {
person: { $in: event.participants }
})
const access = 'reader'
const { _class, space, attachedTo, attachedToClass, collection, ...attr } = event
const data = attr as any as Data<Event>
for (const part of newParticipants) {
const acc = accounts.find((a) => a.person === part)
if (acc === undefined) continue
if (acc._id === (event.createdBy ?? event.modifiedBy)) continue
const calendar = getCalendar(calendars, acc._id)
if (calendar === undefined) continue
const innerTx = control.txFactory.createTxCreateDoc(
_class,
space,
{ ...data, calendar, access },
undefined,
undefined,
acc._id
)
const outerTx = control.txFactory.createTxCollectionCUD(
attachedToClass,
attachedTo,
space,
collection,
innerTx,
undefined,
acc._id
)
res.push(outerTx)
}
return res
}
async function onEventCreate (ctx: TxCreateDoc<Event>, control: TriggerControl): Promise<Tx[]> {
const event = TxProcessor.createDoc2Doc(ctx)
if (event.access !== 'owner') return []
const res: Tx[] = []
const { _class, space, attachedTo, attachedToClass, collection, ...attr } = event
const data = attr as any as Data<Event>
const calendars = await control.findAll(control.ctx, calendar.class.Calendar, { hidden: false })
const accounts = await control.findAll(control.ctx, contactPlugin.class.PersonAccount, {
person: { $in: event.participants }
})
const access = 'reader'
for (const part of event.participants) {
const acc = accounts.find((a) => a.person === part)
if (acc === undefined) continue
if (acc._id === (event.createdBy ?? event.modifiedBy)) continue
const calendar = getCalendar(calendars, acc._id)
if (calendar === undefined) continue
const innerTx = control.txFactory.createTxCreateDoc(
_class,
space,
{ ...data, calendar, access },
undefined,
undefined,
acc._id
)
const outerTx = control.txFactory.createTxCollectionCUD(
attachedToClass,
attachedTo,
space,
collection,
innerTx,
undefined,
acc._id
)
res.push(outerTx)
}
return res
}
async function onRemoveEvent (ctx: TxRemoveDoc<Event>, control: TriggerControl): Promise<Tx[]> {
const removed = control.removedMap.get(ctx.objectId) as Event
const res: Tx[] = []
if (removed !== undefined) {
if (removed.access !== 'owner') return []
const current = await control.findAll(control.ctx, calendar.class.Event, { eventId: removed.eventId })
for (const cur of current) {
res.push(control.txFactory.createTxRemoveDoc(cur._class, cur.space, cur._id))
}
}
return res
}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export default async () => ({
function: {
@ -106,6 +274,7 @@ export default async () => ({
FindReminders
},
trigger: {
OnPersonAccountCreate
OnPersonAccountCreate,
OnEvent
}
})

View File

@ -34,6 +34,7 @@ export default plugin(serverCalendarId, {
FindReminders: '' as Resource<ObjectDDParticipantFunc>
},
trigger: {
OnPersonAccountCreate: '' as Resource<TriggerFunc>
OnPersonAccountCreate: '' as Resource<TriggerFunc>,
OnEvent: '' as Resource<TriggerFunc>
}
})

View File

@ -235,7 +235,7 @@ export class WorkspaceClient {
return
}
case core.class.TxUpdateDoc: {
await this.txUpdateEvent(actualTx as TxUpdateDoc<Doc>)
await this.txUpdateEvent(actualTx as TxUpdateDoc<Event>)
return
}
case core.class.TxRemoveDoc: {
@ -249,6 +249,7 @@ export class WorkspaceClient {
const hierarhy = this.client.getHierarchy()
if (hierarhy.isDerived(tx.objectClass, calendar.class.Event)) {
const doc = TxProcessor.createDoc2Doc(tx as TxCreateDoc<Event>)
if (doc.access !== 'owner') return
const client = this.getCalendarClientByCalendar(doc.calendar as Ref<ExternalCalendar>)
if (client === undefined) {
return
@ -296,23 +297,24 @@ export class WorkspaceClient {
}
}
private async txUpdateEvent (tx: TxUpdateDoc<Doc>): Promise<void> {
private async txUpdateEvent (tx: TxUpdateDoc<Event>): Promise<void> {
const hierarhy = this.client.getHierarchy()
if (hierarhy.isDerived(tx.objectClass, calendar.class.Event)) {
if (tx.operations.space !== undefined) {
await this.handleMove(tx as TxUpdateDoc<Event>)
if (tx.operations.calendar !== undefined) {
await this.handleMove(tx)
return
}
const event = await this.client.findOne(calendar.class.Event, { _id: (tx as TxUpdateDoc<Event>).objectId })
const event = await this.client.findOne(calendar.class.Event, { _id: tx.objectId })
if (event === undefined) {
return
}
if (event.access !== 'owner' && event.access !== 'writer') return
const client = this.getCalendarClientByCalendar(event.calendar as Ref<ExternalCalendar>)
if (client === undefined) {
return
}
try {
await client.updateEvent(event, tx as TxUpdateDoc<Event>)
await client.updateEvent(event, tx)
await this.updateSyncTime()
} catch (err) {
console.log(err)
@ -328,6 +330,7 @@ export class WorkspaceClient {
})
const ev = TxProcessor.buildDoc2Doc<Event>(txes.map((tx) => TxProcessor.extractTx(tx)))
if (ev === undefined) return
if (ev.access !== 'owner' && ev.access !== 'writer') return
const client = this.getCalendarClientByCalendar(ev?.calendar as Ref<ExternalCalendar>)
if (client === undefined) {
return