Fix calendar event visibility (#8510)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2025-04-10 09:46:02 +05:00 committed by GitHub
parent 4cae118f5c
commit afcd637ed2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 55 additions and 14 deletions

View File

@ -28,6 +28,7 @@ import { type Contact } from '@hcengineering/contact'
import { import {
DateRangeMode, DateRangeMode,
IndexKind, IndexKind,
type Account,
type SystemSpace, type SystemSpace,
type Domain, type Domain,
type Markup, type Markup,
@ -135,6 +136,8 @@ export class TEvent extends TAttachedDoc implements Event {
visibility?: Visibility visibility?: Visibility
timeZone?: string timeZone?: string
user!: Ref<Account>
} }
@Model(calendar.class.ReccuringEvent, calendar.class.Event) @Model(calendar.class.ReccuringEvent, calendar.class.Event)

View File

@ -14,7 +14,7 @@
// //
import { type Calendar, calendarId, type Event, type ReccuringEvent } from '@hcengineering/calendar' import { type Calendar, calendarId, type Event, type ReccuringEvent } from '@hcengineering/calendar'
import { groupByArray, type Ref, type Space } from '@hcengineering/core' import core, { groupByArray, toIdMap, type Ref, type Space } from '@hcengineering/core'
import { import {
createDefaultSpace, createDefaultSpace,
tryMigrate, tryMigrate,
@ -122,6 +122,24 @@ async function removeEventDuplicates (client: MigrationClient): Promise<void> {
} }
} }
async function fillUser (client: MigrationClient): Promise<void> {
const calendars = await client.find<Calendar>(DOMAIN_CALENDAR, {})
const events = await client.find<Event>(DOMAIN_EVENT, {
user: { $exists: false }
})
const map = toIdMap(calendars)
for (const event of events) {
const calendar = map.get(event.calendar)
if (calendar !== undefined) {
await client.update(
DOMAIN_CALENDAR,
{ _id: calendar._id },
{ user: event.createdBy !== core.account.System ? event.createdBy : calendar.createdBy }
)
}
}
}
async function migrateTimezone (client: MigrationClient): Promise<void> { async function migrateTimezone (client: MigrationClient): Promise<void> {
await client.update( await client.update(
DOMAIN_CALENDAR, DOMAIN_CALENDAR,
@ -171,6 +189,11 @@ export const calendarOperation: MigrateOperation = {
state: 'remove-duplicates', state: 'remove-duplicates',
mode: 'upgrade', mode: 'upgrade',
func: removeEventDuplicates func: removeEventDuplicates
},
{
state: 'fill-user',
mode: 'upgrade',
func: fillUser
} }
]) ])
}, },

View File

@ -264,6 +264,7 @@
modifiedOn: Date.now(), modifiedOn: Date.now(),
date: e.detail.date.getTime(), date: e.detail.date.getTime(),
space: calendar.space.Calendar, space: calendar.space.Calendar,
user: me._id,
dueDate: new Date(e.detail.date).setMinutes(new Date(e.detail.date).getMinutes() + 30) dueDate: new Date(e.detail.date).setMinutes(new Date(e.detail.date).getMinutes() + 30)
} }
raw.push(temp) raw.push(temp)

View File

@ -102,6 +102,7 @@
if (startDate != null) date = startDate if (startDate != null) date = startDate
if (date === undefined) return if (date === undefined) return
if (title === '') return if (title === '') return
const user = me._id
const _id = generateId<Event>() const _id = generateId<Event>()
if (rules.length > 0) { if (rules.length > 0) {
await client.addCollection( await client.addCollection(
@ -128,7 +129,8 @@
allDay, allDay,
access: 'owner', access: 'owner',
originalStartTime: allDay ? saveUTC(date) : date, originalStartTime: allDay ? saveUTC(date) : date,
timeZone timeZone,
user
}, },
_id as Ref<ReccuringEvent> _id as Ref<ReccuringEvent>
) )
@ -153,7 +155,8 @@
location, location,
allDay, allDay,
timeZone, timeZone,
access: 'owner' access: 'owner',
user
}, },
_id _id
) )

View File

@ -55,7 +55,8 @@
title: _title, title: _title,
allDay: false, allDay: false,
reminders: [0], reminders: [0],
access: 'owner' access: 'owner',
user: getCurrentAccount()._id
}) })
} }
</script> </script>

View File

@ -104,7 +104,8 @@ async function deleteRecHandler (res: any, object: ReccuringInstance): Promise<v
exdate: object.exdate, exdate: object.exdate,
visibility: object.visibility, visibility: object.visibility,
access: object.access, access: object.access,
timeZone: object.timeZone timeZone: object.timeZone,
user: object.user
}, },
object._id object._id
) )

View File

@ -29,7 +29,7 @@ export function hidePrivateEvents (events: Event[], calendars: IdMap<Calendar>,
const me = getCurrentAccount()._id const me = getCurrentAccount()._id
const res: Event[] = [] const res: Event[] = []
for (const event of events) { for (const event of events) {
if ((event.createdBy ?? event.modifiedBy) === me && allowMe) { if (event.user === me && allowMe) {
res.push(event) res.push(event)
} else { } else {
if (event.visibility !== undefined) { if (event.visibility !== undefined) {
@ -49,14 +49,14 @@ export function hidePrivateEvents (events: Event[], calendars: IdMap<Calendar>,
export function isReadOnly (value: Event): boolean { export function isReadOnly (value: Event): boolean {
const me = getCurrentAccount()._id const me = getCurrentAccount()._id
if (value.createdBy !== me) return true if (value.user !== me) return true
if (['owner', 'writer'].includes(value.access)) return false if (['owner', 'writer'].includes(value.access)) return false
return true return true
} }
export function isVisible (value: Event, calendars: IdMap<Calendar>): boolean { export function isVisible (value: Event, calendars: IdMap<Calendar>): boolean {
const me = getCurrentAccount()._id const me = getCurrentAccount()._id
if (value.createdBy === me) return true if (value.user === me) return true
if (value.visibility === 'freeBusy') { if (value.visibility === 'freeBusy') {
return false return false
} else if (value.visibility === 'public') { } else if (value.visibility === 'public') {
@ -158,6 +158,7 @@ export async function updateReccuringInstance (
exdate: object.exdate, exdate: object.exdate,
rdate: object.rdate, rdate: object.rdate,
timeZone: object.timeZone, timeZone: object.timeZone,
user: object.user,
...ops ...ops
}, },
object._id object._id

View File

@ -12,7 +12,7 @@
// limitations under the License. // limitations under the License.
import { Contact } from '@hcengineering/contact' import { Contact } from '@hcengineering/contact'
import type { AttachedDoc, Class, Doc, Markup, Mixin, Ref, SystemSpace, Timestamp } from '@hcengineering/core' import type { Account, AttachedDoc, Class, Doc, Markup, Mixin, Ref, SystemSpace, Timestamp } from '@hcengineering/core'
import { NotificationType } from '@hcengineering/notification' import { NotificationType } from '@hcengineering/notification'
import type { Asset, IntlString, Metadata, Plugin } from '@hcengineering/platform' import type { Asset, IntlString, Metadata, Plugin } from '@hcengineering/platform'
import { plugin } from '@hcengineering/platform' import { plugin } from '@hcengineering/platform'
@ -108,6 +108,8 @@ export interface Event extends AttachedDoc {
access: 'freeBusyReader' | 'reader' | 'writer' | 'owner' access: 'freeBusyReader' | 'reader' | 'writer' | 'owner'
timeZone?: string timeZone?: string
user: Ref<Account>
} }
/** /**

View File

@ -98,7 +98,8 @@
allDay: false, allDay: false,
access: 'owner', access: 'owner',
visibility: todo.visibility === 'public' ? 'public' : 'freeBusy', visibility: todo.visibility === 'public' ? 'public' : 'freeBusy',
reminders: [] reminders: [],
user: acc._id
}) })
Analytics.handleEvent(TimeEvents.ToDoScheduled, { id }) Analytics.handleEvent(TimeEvents.ToDoScheduled, { id })
} }
@ -153,7 +154,8 @@
attachedToClass: time.class.ToDo, attachedToClass: time.class.ToDo,
collection: 'workslots', collection: 'workslots',
modifiedOn: Date.now(), modifiedOn: Date.now(),
modifiedBy: acc._id modifiedBy: acc._id,
user: acc._id
}) })
slots = slots slots = slots
} }

View File

@ -67,7 +67,8 @@
allDay: false, allDay: false,
access: 'owner', access: 'owner',
visibility: doc.visibility === 'public' ? 'public' : 'freeBusy', visibility: doc.visibility === 'public' ? 'public' : 'freeBusy',
reminders: [] reminders: [],
user: currentUser._id
}) })
Analytics.handleEvent(TimeEvents.ToDoScheduled, { id: doc._id }) Analytics.handleEvent(TimeEvents.ToDoScheduled, { id: doc._id })
} }

View File

@ -133,7 +133,8 @@
participants: [me.person], participants: [me.person],
modifiedOn: Date.now(), modifiedOn: Date.now(),
date: e.detail.date.getTime(), date: e.detail.date.getTime(),
dueDate: new Date(e.detail.date).setMinutes(new Date(e.detail.date).getMinutes() + 30) dueDate: new Date(e.detail.date).setMinutes(new Date(e.detail.date).getMinutes() + 30),
user: me._id
} }
raw.push(ev) raw.push(ev)
} }

View File

@ -73,6 +73,7 @@
title: todo.title, title: todo.title,
allDay: false, allDay: false,
access: 'owner', access: 'owner',
user: currentUser._id,
visibility: todo.visibility === 'public' ? 'public' : 'freeBusy', visibility: todo.visibility === 'public' ? 'public' : 'freeBusy',
reminders: [] reminders: []
}) })

View File

@ -721,7 +721,8 @@ export class CalendarClient {
eventId: event.id ?? '', eventId: event.id ?? '',
calendar: _calendar, calendar: _calendar,
access: this.getAccess(event, accessRole), access: this.getAccess(event, accessRole),
timeZone: event.start?.timeZone ?? event.end?.timeZone ?? 'Etc/GMT' timeZone: event.start?.timeZone ?? event.end?.timeZone ?? 'Etc/GMT',
user: this.user.userId
} }
if (participants[1].length > 0) { if (participants[1].length > 0) {
res.externalParticipants = participants[1] res.externalParticipants = participants[1]