some ICS fixes, handle empty calendar list

This commit is contained in:
Stefan Bühler 2024-12-18 15:23:37 +01:00
parent d7d86601f0
commit 6bf9758605

View File

@ -89,7 +89,7 @@ function display_event_date(item) {
return m("span.timerange", display_date_utc(start, utc_start_date), " - ", display_date_utc(end_day, utc_end_day_date));
}
}
if (start_date === end_date) {
return m("span.timerange", display_datetime(start, `${start_date}, ${start_time}`), " - ", display_datetime(end, end_time));
} else {
@ -102,31 +102,43 @@ function display_event_date(item) {
}
}
function escapeICSField(text) {
return text.replace(/\\/g, "\\\\")
.replace(/[,;]/g, "\\$&")
.replace(/\n/g, "\\n");
function format_ICS_text(text) {
return text.replace(/[\\,;]/g, "\\$&") // escape backslash and separators
.replace(/\n/g, "\\n") // escape newline
.replace(/[\x00-\x1f]+/g, " ") // replace control characters with whitespace
;
}
function format_ICS_date(date) {
if (date) {
date = new Date(date);
} else {
date = new Date(); // default to "now"
}
// remove "-" and ":" separators (but keep "T"), and drop microseconds
return date.toISOString().replace(/-|:|\.\d+/g, "");
}
function createICSLink(item, settings) {
const start = new Date(item.calculated.startDate).toISOString().replace(/-|:|\.\d+/g, '');
const end = new Date(item.calculated.endDate).toISOString().replace(/-|:|\.\d+/g, '');
const start = format_ICS_date(item.calculated.startDate);
const end = format_ICS_date(item.calculated.endDate);
const summary = item.base.caption;
const description = item.base.information || "";
const location = item.base.address?.meetingAt || "";
// Escape special characters for ICS fields
const escapedSummary = escapeICSField(summary /* .replace(/['"]/g, "") */);
const escapedDescription = escapeICSField(description /* .replace(/['"]/g, "") */);
const escapedLocation = escapeICSField(location);
const escapedSummary = format_ICS_text(summary /* .replace(/['"]/g, "") */);
const escapedDescription = format_ICS_text(description /* .replace(/['"]/g, "") */);
const escapedLocation = format_ICS_text(location);
const icsContent = `BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Evang Kirchengemeinde Malmsheim//Gottesdienste//EN
PRODID:-//churchtools-next-events//EN
BEGIN:VEVENT
UID:${crypto.randomUUID()}@malmsheim-evangelisch.de
DTSTAMP:${new Date().toISOString().replace(/-|:|\.\d+/g, '')}
UID:appointment-${item.base.id}@${window.location.hostname}
CREATED:${format_ICS_date(item.base.meta?.createdDate)}
LAST-MODIFIED:${format_ICS_date(item.base.meta?.modifiedDate)}
DTSTART:${start}
DTEND:${end}
SUMMARY:${escapedSummary}
@ -273,7 +285,7 @@ var AppointmentLocation = {
if (!location) {
return m("[");
}
return m("p", [
fa_solid_icon(settings, "location-dot"),
" ",
@ -293,7 +305,7 @@ var AppointmentLink = {
if (!url.startsWith("http")) {
url = "https://" + url;
}
return m("p", [
fa_solid_icon(settings, "link"),
" ",
@ -389,24 +401,30 @@ async function load_events(dom_node, defaults) {
credentials: "omit",
mode: "cors",
}
const r_cal_regex = new RegExp(settings.calendar_regex, "i");
const r_event_regex = new RegExp(settings.event_regex, "i");
const all_calendars = (await (await fetch(settings.instance + "/api/calendars", req_config)).json()).data;
const calendars = all_calendars.filter((calendar) => r_cal_regex.exec(calendar.name));
const calendar_ids = calendars.map((calendar) => calendar.id);
const filters = calendar_ids.map((cal_id) => `calendar_ids[]=${cal_id}`);
const from = new Date();
filters.push(`from=${format_date(from)}`);
const to = new Date(from.valueOf() + settings.days * 86400 * 1000);
filters.push(`to=${format_date(to)}`);
const query = filters.join("&");
const all_appointments = (await (await fetch(settings.instance + `/api/calendars/appointments?${query}`, req_config)).json()).data;
const appointments = all_appointments.filter((appointment) => r_event_regex.exec(appointment.base.title));
if (appointments.length > settings.limit) {
appointments.length = settings.limit;
let appointments;
if (calendar_ids.length) {
const filters = calendar_ids.map((cal_id) => `calendar_ids[]=${cal_id}`);
const from = new Date();
filters.push(`from=${format_date(from)}`);
const to = new Date(from.valueOf() + settings.days * 86400 * 1000);
filters.push(`to=${format_date(to)}`);
const query = filters.join("&");
const all_appointments = (await (await fetch(settings.instance + `/api/calendars/appointments?${query}`, req_config)).json()).data;
appointments = all_appointments.filter((appointment) => r_event_regex.exec(appointment.base.title));
if (appointments.length > settings.limit) {
appointments.length = settings.limit;
}
} else {
appointments = [];
}
const Appointments = {