feat(caldav): enhance eventToICS function to include join links and organizer details
All checks were successful
Build & Push Docker Image / build (push) Successful in 6m24s

This commit is contained in:
2026-03-03 12:13:36 +01:00
parent ddc0c684ec
commit f3ef490012

View File

@@ -89,7 +89,12 @@ function getICSProp(ics, key) {
return v.trim();
}
function eventToICS(event) {
function eventToICS(event, base, user) {
// Determine the most useful join URL
const joinUrl = event.federated_join_url
|| (event.room_uid ? `${base}/join/${event.room_uid}` : null);
const roomUrl = event.room_uid ? `${base}/rooms/${event.room_uid}` : null;
const lines = [
'BEGIN:VCALENDAR',
'VERSION:2.0',
@@ -103,14 +108,40 @@ function eventToICS(event) {
`DTSTAMP:${toICSDate(event.updated_at || event.created_at)}`,
`LAST-MODIFIED:${toICSDate(event.updated_at || event.created_at)}`,
];
if (event.description) {
lines.push(`DESCRIPTION:${escapeICS(event.description)}`);
// LOCATION: show join link so calendar apps display "where" the meeting is
if (joinUrl) {
lines.push(`LOCATION:${escapeICS(joinUrl)}`);
lines.push(`URL:${joinUrl}`);
} else if (roomUrl) {
lines.push(`LOCATION:${escapeICS(roomUrl)}`);
lines.push(`URL:${roomUrl}`);
}
// DESCRIPTION: combine user description + join link hint
const descParts = [];
if (event.description) descParts.push(event.description);
if (joinUrl) {
descParts.push(`Join meeting: ${joinUrl}`);
}
if (roomUrl && roomUrl !== joinUrl) {
descParts.push(`Room page: ${roomUrl}`);
}
if (descParts.length > 0) {
lines.push(`DESCRIPTION:${escapeICS(descParts.join('\n'))}`);
}
// ORGANIZER
if (user) {
const cn = user.display_name || user.name || user.email;
lines.push(`ORGANIZER;CN=${escapeICS(cn)}:mailto:${user.email}`);
}
if (event.room_uid) {
lines.push(`X-REDLIGHT-ROOM-UID:${event.room_uid}`);
}
if (event.federated_join_url) {
lines.push(`X-REDLIGHT-JOIN-URL:${escapeICS(event.federated_join_url)}`);
if (joinUrl) {
lines.push(`X-REDLIGHT-JOIN-URL:${escapeICS(joinUrl)}`);
}
lines.push('END:VEVENT', 'END:VCALENDAR');
return lines.map(foldICSLine).join('\r\n');
@@ -340,7 +371,7 @@ router.all('/:username/calendar/', caldavAuth, async (req, res) => {
responses.push(
propResponse(`${calendarHref}${ev.uid}.ics`, {
'd:getetag': escapeXml(etag(ev)),
'c:calendar-data': escapeXml(ics),
'c:calendar-data': escapeXml(eventToICS(ev, baseUrl(req), req.caldavUser)),
}),
);
}
@@ -368,7 +399,7 @@ router.all('/:username/calendar/', caldavAuth, async (req, res) => {
const responses = events.map(ev =>
propResponse(`${calendarHref}${ev.uid}.ics`, {
'd:getetag': escapeXml(etag(ev)),
'c:calendar-data': escapeXml(eventToICS(ev)),
'c:calendar-data': escapeXml(eventToICS(ev, baseUrl(req), req.caldavUser)),
}),
);
setDAVHeaders(res);
@@ -391,7 +422,7 @@ router.get('/:username/calendar/:filename', caldavAuth, async (req, res) => {
setDAVHeaders(res);
res.set('ETag', etag(ev));
res.set('Content-Type', 'text/calendar; charset=utf-8');
res.send(eventToICS(ev));
res.send(eventToICS(ev, baseUrl(req), req.caldavUser));
});
// ── PUT /{username}/calendar/{uid}.ics — create or update ─────────────────