fix(calendar): classify self-organized imported events as editable via organizerCalendarAddress fallback
This commit is contained in:
committed by
Linus Rath
parent
6dfcb07b9a
commit
e8f01871c2
@@ -132,6 +132,42 @@ describe('isOrganizer', () => {
|
|||||||
const event = makeEvent({ org: orgParticipant });
|
const event = makeEvent({ org: orgParticipant });
|
||||||
expect(isOrganizer(event, [])).toBe(false);
|
expect(isOrganizer(event, [])).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('matches the event-level organizerCalendarAddress when no owner role is set', () => {
|
||||||
|
// Stalwart / imported self-organized events: the user's participant only
|
||||||
|
// carries `attendee`, the organizer lives in organizerCalendarAddress.
|
||||||
|
const event = makeEvent({
|
||||||
|
self: {
|
||||||
|
'@type': 'Participant',
|
||||||
|
name: 'Alice',
|
||||||
|
email: '',
|
||||||
|
roles: { attendee: true },
|
||||||
|
participationStatus: 'accepted',
|
||||||
|
sendTo: { imip: 'mailto:alice@example.com' },
|
||||||
|
kind: 'individual',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
event.organizerCalendarAddress = 'mailto:alice@example.com';
|
||||||
|
expect(isOrganizer(event, ['alice@example.com'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('matches the event-level organizerCalendarAddress case-insensitively', () => {
|
||||||
|
const event = makeEvent({ att1: attendeeParticipant });
|
||||||
|
event.organizerCalendarAddress = 'mailto:Alice@Example.com';
|
||||||
|
expect(isOrganizer(event, ['alice@example.com'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to replyTo when organizerCalendarAddress is absent', () => {
|
||||||
|
const event = makeEvent({ att1: attendeeParticipant });
|
||||||
|
event.replyTo = { imip: 'mailto:alice@example.com' };
|
||||||
|
expect(isOrganizer(event, ['alice@example.com'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false when the event organizer is someone else', () => {
|
||||||
|
const event = makeEvent({ att1: attendeeParticipant });
|
||||||
|
event.organizerCalendarAddress = 'mailto:someoneelse@example.com';
|
||||||
|
expect(isOrganizer(event, ['alice@example.com'])).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getUserParticipantId', () => {
|
describe('getUserParticipantId', () => {
|
||||||
|
|||||||
@@ -35,12 +35,39 @@ function participantMatchesEmail(p: CalendarParticipant, lowerEmails: string[]):
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects the event-level organizer calendar address(es).
|
||||||
|
* Stalwart conveys the organizer via `organizerCalendarAddress` / `replyTo`
|
||||||
|
* rather than a participant `roles.owner` flag, so self-organized events
|
||||||
|
* imported from another server have no owner participant to match against.
|
||||||
|
*/
|
||||||
|
function getEventOrganizerEmails(event: CalendarEvent): string[] {
|
||||||
|
const emails: string[] = [];
|
||||||
|
if (event.organizerCalendarAddress) {
|
||||||
|
emails.push(event.organizerCalendarAddress.replace(/^mailto:/i, '').toLowerCase());
|
||||||
|
}
|
||||||
|
if (event.replyTo) {
|
||||||
|
for (const addr of Object.values(event.replyTo)) {
|
||||||
|
emails.push(addr.replace(/^mailto:/i, '').toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return emails.filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
export function isOrganizer(event: CalendarEvent, userEmails: string[]): boolean {
|
export function isOrganizer(event: CalendarEvent, userEmails: string[]): boolean {
|
||||||
if (!event.participants) return false;
|
if (userEmails.length === 0) return false;
|
||||||
const lower = userEmails.map(e => e.toLowerCase());
|
const lower = userEmails.map(e => e.toLowerCase());
|
||||||
return Object.values(event.participants).some(p =>
|
|
||||||
p.roles?.owner && participantMatchesEmail(p, lower)
|
if (event.participants) {
|
||||||
);
|
const ownerMatch = Object.values(event.participants).some(p =>
|
||||||
|
p.roles?.owner && participantMatchesEmail(p, lower)
|
||||||
|
);
|
||||||
|
if (ownerMatch) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to the event-level organizer address (Stalwart / imported events
|
||||||
|
// mark the organizer here instead of via a participant `owner` role).
|
||||||
|
return getEventOrganizerEmails(event).some(email => lower.includes(email));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUserParticipantId(event: CalendarEvent, userEmails: string[]): string | null {
|
export function getUserParticipantId(event: CalendarEvent, userEmails: string[]): string | null {
|
||||||
|
|||||||
Reference in New Issue
Block a user