Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions app/views/faq/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1925,19 +1925,23 @@ In order to use calendar support, the **user** must be the email address of an a

### Do you support tasks and reminders (CalDAV VTODO)

Yes, as of \[INSERT DATE] we have added CalDAV VTODO support for tasks and reminders. This uses the same server as our calendar support: `caldav.forwardemail.net`.
Yes, as of October 14, 2025 we have added CalDAV VTODO support for tasks and reminders. This uses the same server as our calendar support: `caldav.forwardemail.net`.

Our CalDAV server supports both calendar events (VEVENT) and tasks (VTODO) components, with automatic separation into appropriate calendar collections:
Our CalDAV server supports both calendar events (VEVENT) and tasks (VTODO) components using **unified calendars**. This means each calendar can contain both events and tasks, providing maximum flexibility and compatibility across all CalDAV clients.

* **Calendar events** go into your main "Calendar" collection
* **Tasks/reminders** go into a separate "Reminders" collection
**How calendars and lists work:**

* **Each calendar supports both events and tasks** - You can add events, tasks, or both to any calendar
* **Apple Reminders lists** - Each list you create in Apple Reminders becomes a separate calendar on the server
* **Multiple calendars** - You can create as many calendars as you need, each with its own name, color, and organization
* **Cross-client sync** - Tasks and events sync seamlessly between all compatible clients

**Supported task clients:**

* **macOS Reminders** - Full native support for task creation, editing, completion, and sync
* **iOS Reminders** - Full native support across all iOS devices
* **Tasks.org (Android)** - Popular open-source task manager with CalDAV sync
* **Thunderbird with Lightning** - Task support in desktop email client
* **Thunderbird** - Task and calendar support in desktop email client
* **Any CalDAV-compatible task manager** - Standard VTODO component support

**Task features supported:**
Expand All @@ -1949,6 +1953,8 @@ Our CalDAV server supports both calendar events (VEVENT) and tasks (VTODO) compo
* Recurring tasks
* Task descriptions and notes
* Multi-device synchronization
* Subtasks with RELATED-TO property
* Task reminders with VALARM

The login credentials are the same as for calendar support:

Expand All @@ -1957,7 +1963,12 @@ The login credentials are the same as for calendar support:
| Username | `[email protected]` | Email address of an alias that exists for the domain at <a href="/my-account/domains" target="_blank" rel="noopener noreferrer">My Account <i class="fa fa-angle-right"></i> Domains</a>. |
| Password | `************************` | Alias-specific generated password. |

**Important:** Tasks and calendar events are kept in separate collections to ensure proper client compatibility, especially with Apple devices that expect dedicated task calendars.
**Important notes:**

* **Each Reminders list is a separate calendar** - When you create a new list in Apple Reminders, it creates a new calendar on the CalDAV server
* **Thunderbird users** - You'll need to manually subscribe to each calendar/list you want to sync, or use the calendar home URL: `https://caldav.forwardemail.net/dav/[email protected]/`
* **Apple users** - Calendar discovery happens automatically, so all your calendars and lists will appear in Calendar.app and Reminders.app
* **Unified calendars** - All calendars support both events and tasks, giving you flexibility in how you organize your data

### Do you support contacts (CardDAV)

Expand Down
22 changes: 5 additions & 17 deletions caldav-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -1434,21 +1434,9 @@ class CalDAV extends API {
//
if (calendar) return calendar; // safeguard

// Determine supported components based on calendar name/type
// Default: support both events and tasks
let has_vevent = true;
let has_vtodo = true;

// Task/reminder-only calendars (only if specifically named)
if (
name === 'DEFAULT_TASK_CALENDAR_NAME' ||
I18N_SET_REMINDERS.has(name) ||
I18N_SET_TASKS.has(name)
) {
has_vevent = false; // Tasks only
has_vtodo = true;
}

// All calendars support both events and tasks (unified calendar approach)
// This ensures compatibility across all CalDAV clients (Apple, Thunderbird, etc.)
// and aligns with the default calendar creation behavior (see ensureDefaultCalendars)
return Calendars.create({
// db virtual helper
instance: this,
Expand All @@ -1467,8 +1455,8 @@ class CalDAV extends API {
url: config.urls.web,
readonly: false,
synctoken: `${config.urls.web}/ns/sync-token/1`,
has_vevent,
has_vtodo
has_vevent: true, // Support both events and tasks
has_vtodo: true
});
}

Expand Down
54 changes: 53 additions & 1 deletion test/caldav/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1405,7 +1405,50 @@ test('single ICS file with both VEVENT and VTODO components', async (t) => {
// Use the unified calendar (first calendar)
const unifiedCal = calendars[0];

// Upload the file with both components
// First, explicitly verify the unified calendar supports both VEVENT and VTODO
// by successfully uploading a pure VEVENT file
const eventOnlyIcs = await fsp.readFile(
path.join(__dirname, 'data', '1.ics'),
'utf8'
);
const eventOnlyUrl = new URL('test-vevent-support.ics', unifiedCal.url).href;
const eventOnlyResponse = await createObject({
url: eventOnlyUrl,
data: eventOnlyIcs,
headers: {
'content-type': 'text/calendar; charset=utf-8',
...t.context.authHeaders
}
});
t.true(
eventOnlyResponse.ok,
'Unified calendar should accept VEVENT (has_vevent=true)'
);

// Then verify it supports VTODO by uploading a pure VTODO file
const todoOnlyIcs = await fsp.readFile(
path.join(__dirname, 'data', 'vtodo-1.ics'),
'utf8'
);
const todoOnlyUrl = new URL('test-vtodo-support.ics', unifiedCal.url).href;
const todoOnlyResponse = await createObject({
url: todoOnlyUrl,
data: todoOnlyIcs,
headers: {
'content-type': 'text/calendar; charset=utf-8',
...t.context.authHeaders
}
});
t.true(
todoOnlyResponse.ok,
'Unified calendar should accept VTODO (has_vtodo=true)'
);

// Clean up the test files
await deleteObject({ url: eventOnlyUrl, headers: t.context.authHeaders });
await deleteObject({ url: todoOnlyUrl, headers: t.context.authHeaders });

// Now upload the file with both components
const objectUrl = new URL('mixed-components.ics', unifiedCal.url).href;
const response = await createObject({
url: objectUrl,
Expand Down Expand Up @@ -1445,6 +1488,15 @@ test('single ICS file with both VEVENT and VTODO components', async (t) => {
'Should preserve VTODO summary'
);

// This test validates multiple aspects of mixed component support:
// 1. EXPLICITLY verifies has_vevent=true by uploading a pure VEVENT file (must succeed)
// 2. EXPLICITLY verifies has_vtodo=true by uploading a pure VTODO file (must succeed)
// 3. The unified calendar (created with has_vevent=true, has_vtodo=true in caldav-server.js:334-335)
// successfully accepts ICS files containing both VEVENT and VTODO components
// 4. Both components are preserved in the stored ICS data
// 5. Per calendar-events.js pre-validate hook (lines 160-167), when both components exist,
// the CalendarEvent.componentType field is set to 'VEVENT' for backward compatibility

// Clean up
await deleteObject({ url: objectUrl, headers: t.context.authHeaders });
});
Expand Down
Loading