data/CalendarModels.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <QDateTime> | ||
| 4 | #include <QString> | ||
| 5 | #include <QStringList> | ||
| 6 | |||
| 7 | #include "DavPushModels.h" | ||
| 8 | |||
| 9 | // DAV paths are only unique within their account. Length-prefix the account | ||
| 10 | // so even unusual account/path separators cannot alias another collection. | ||
| 11 | 1291281 | inline QString calendarSelectionKey(const QString &accountId, const QString &path) { | |
| 12 |
4/4✓ Branch 5 → 6 taken 1291281 times.
✓ Branch 6 → 7 taken 1291281 times.
✓ Branch 7 → 8 taken 1291281 times.
✓ Branch 8 → 9 taken 1291281 times.
|
1291281 | return QString::number(accountId.size()) + QLatin1Char(':') + accountId + path; |
| 13 | } | ||
| 14 | |||
| 15 | // ═══════════════════════════════════════════════════════ | ||
| 16 | // CalDAV data models (Sprint 32 – T-330) | ||
| 17 | // ═══════════════════════════════════════════════════════ | ||
| 18 | |||
| 19 | /// Info about a discovered CalDAV calendar (PROPFIND result) | ||
| 20 | struct CalendarInfo { | ||
| 21 | QString path; ///< DAV href path (unique per server) | ||
| 22 | QString accountId; ///< Owning DAV account id; path is only unique per account | ||
| 23 | QString displayName; ///< Human-readable name, e.g. "Persönlich" | ||
| 24 | QString color; ///< Hex color from APPLE:calendar-color, e.g. "#FF6600" | ||
| 25 | QString ctag; ///< Calendar CTag for sync change detection | ||
| 26 | bool readOnly = false; ///< User-marked as read-only (no write ops) | ||
| 27 | QStringList supportedComponents; ///< Empty = server did not restrict components | ||
| 28 | DavPushOffer push; ///< Sprint 96: WebDAV-Push offer of this collection (discovery only, not stored) | ||
| 29 |
8/8✓ Branch 3 → 4 taken 4298 times.
✓ Branch 3 → 9 taken 20552 times.
✓ Branch 8 → 9 taken 3701 times.
✓ Branch 8 → 10 taken 597 times.
✓ Branch 12 → 13 taken 4298 times.
✓ Branch 12 → 14 taken 20552 times.
✓ Branch 14 → 15 taken 4298 times.
✓ Branch 14 → 16 taken 20552 times.
|
29148 | bool supportsTasks() const { return supportedComponents.isEmpty() || supportedComponents.contains(QStringLiteral("VTODO")); } |
| 30 | }; | ||
| 31 | |||
| 32 | /// Sprint 103 (S103.1.10, D-14): a person named in an event with the | ||
| 33 | /// iCalendar CONTACT property — never an ATTENDEE, so no server sends an | ||
| 34 | /// invitation. `uri` is the ALTREP ("urn:uuid:<UID>" of a contact card). | ||
| 35 | struct EventPerson { | ||
| 36 | 31 | QString uri; | |
| 37 | 30 | QString uid; ///< UID from an urn:uuid ALTREP, else empty | |
| 38 | 30 | QString name; ///< the CONTACT text value | |
| 39 |
4/6✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 30 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 30 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 30 times.
|
31 | bool operator==(const EventPerson &) const = default; |
| 40 | }; | ||
| 41 | |||
| 42 | /// Sprint 103: ATTENDEE / ORGANIZER written by other clients (read-only). | ||
| 43 | struct EventAttendee { | ||
| 44 | QString address; ///< mailto: address without the scheme | ||
| 45 | QString name; ///< CN | ||
| 46 | QString role; ///< ROLE (REQ-PARTICIPANT, …), "ORGANIZER" for the organizer | ||
| 47 | QString partstat; ///< PARTSTAT (ACCEPTED, …) | ||
| 48 | QString cutype; ///< CUTYPE (INDIVIDUAL, ROOM, …) | ||
| 49 | bool organizer = false; | ||
| 50 | bool operator==(const EventAttendee &) const = default; | ||
| 51 | }; | ||
| 52 | |||
| 53 | /// Single calendar event parsed from VEVENT (RFC 5545) | ||
| 54 | struct CalendarEvent { | ||
| 55 | qint64 id = 0; ///< Database primary key | ||
| 56 | QString uid; ///< iCal UID (unique within calendar) | ||
| 57 | QString calendarPath; ///< DAV path of parent calendar | ||
| 58 | QString accountId; ///< Owning DAV account id | ||
| 59 | QString resourceHref; ///< DAV href of the VEVENT resource | ||
| 60 | QString summary; ///< Event title (SUMMARY) | ||
| 61 | QString description; ///< Event description (DESCRIPTION) | ||
| 62 | QString location; ///< Event location (LOCATION) | ||
| 63 | // Invalid server rows remain lossless in storage, but have no occurrences. | ||
| 64 | 78228 | bool isSchedulable() const { return dtStart.isValid(); } | |
| 65 | QDateTime dtStart; ///< Start date/time (DTSTART) | ||
| 66 | QDateTime dtEnd; ///< End date/time (DTEND) | ||
| 67 | bool allDay = false; ///< True if VALUE=DATE (no time component) | ||
| 68 | QString rrule; ///< Recurrence rule string (RRULE), stored as-is | ||
| 69 | QDateTime recurrenceId; ///< RECURRENCE-ID: set for exceptions (moved | ||
| 70 | ///< occurrences); invalid for masters (Q11) | ||
| 71 | QList<QDateTime> exdates; ///< Deleted occurrences (EXDATE, T-79.C1/H3) | ||
| 72 | QString etag; ///< DAV ETag for incremental sync | ||
| 73 | QString color; ///< Inherited from parent calendar | ||
| 74 | QDateTime lastModified; ///< LAST-MODIFIED timestamp | ||
| 75 | // Sprint 93 S93.1.1–S93.1.4 (lossless round trip, zones, full RRULE). | ||
| 76 | // dtStart/dtEnd carry their zone: UTC, local time (floating) or the | ||
| 77 | // resolved IANA zone (see ICalTime.h). | ||
| 78 | QList<QDateTime> rdates; ///< Extra occurrences (RDATE) | ||
| 79 | QString icalRaw; ///< The VEVENT as last read from / written to the | ||
| 80 | ///< server; the serializer patches it (S93.1.1) | ||
| 81 | QString resourceIcal; ///< The whole VCALENDAR resource (VTIMEZONE, | ||
| 82 | ///< calendar properties); filled for write-back | ||
| 83 | bool partialRecurrence = false; ///< Display only: the RRULE could not be | ||
| 84 | ///< expanded completely (S93.1.4) | ||
| 85 | bool occurrencesLimited = false; ///< Display only: the series has more | ||
| 86 | ///< occurrences in the range than the | ||
| 87 | ///< display limit (Sprint 102, DisplayLimit) | ||
| 88 | QString syncState; ///< "" synced, "pending", "conflict" (outbox, S93.1.6) | ||
| 89 | QList<int> reminders; ///< Sprint 93 S93.5.1: display/audio VALARMs as | ||
| 90 | ///< minutes before the start (negative = after), | ||
| 91 | ///< sorted ascending, unique | ||
| 92 | // Sprint 103 (S103.1.10): people named in the event (CONTACT) and the | ||
| 93 | // read-only attendees/organizer. The writer applies `people` only when | ||
| 94 | // `peopleSet` is true (the list was read from the component or edited); | ||
| 95 | // rows loaded without it keep their CONTACT lines untouched. | ||
| 96 | QList<EventPerson> people; | ||
| 97 | bool peopleSet = false; | ||
| 98 | QList<EventAttendee> attendees; | ||
| 99 | }; | ||
| 100 | |||
| 101 | /// A DAV resource whose local state still has to reach the server | ||
| 102 | /// (calendar_outbox, Sprint 93 S93.1.6). The payload is not stored: a put | ||
| 103 | /// pushes the current local rows of `uid`, patched onto the stored resource. | ||
| 104 | struct CalendarOutboxEntry { | ||
| 105 | QString accountId; | ||
| 106 | QString calendarPath; | ||
| 107 | QString uid; | ||
| 108 | QString operation; ///< "put" or "delete" | ||
| 109 | QString href; ///< resource URL ("" = <calendar>/<uid>.ics) | ||
| 110 | QString baseEtag; ///< server ETag the change is based on; "" = create | ||
| 111 | int revision = 0; ///< incremented by every local change | ||
| 112 | int attempts = 0; | ||
| 113 | qint64 nextAttempt = 0; ///< epoch seconds; 0 = now | ||
| 114 | QString lastError; ///< technical detail (log/"Details" only) | ||
| 115 | QString state; ///< "pending" or "conflict" | ||
| 116 | QStringList conflictFields; | ||
| 117 | QString serverIcal; ///< conflict: the server version ("" = deleted there) | ||
| 118 | QString serverEtag; | ||
| 119 | qint64 created = 0; | ||
| 120 | QString componentType = QStringLiteral("VEVENT"); | ||
| 121 | QString dependencyKey; ///< Move: source DELETE waits for the destination PUT | ||
| 122 | }; | ||
| 123 | |||
| 124 | // S106-351: the bound of a stored task sort order (see CalendarTask::sortOrder). | ||
| 125 | inline constexpr qint64 kTaskSortOrderLimit = qint64(1) << 52; | ||
| 126 | |||
| 127 | /// Single TODO/task parsed from VTODO (RFC 5545) | ||
| 128 | struct CalendarTask { | ||
| 129 | qint64 id = 0; ///< Database primary key | ||
| 130 | QString uid; ///< iCal UID | ||
| 131 | QString calendarPath; ///< DAV path of parent calendar | ||
| 132 | QString accountId; ///< Owning DAV account id | ||
| 133 | QString resourceHref; ///< DAV href of the VTODO resource | ||
| 134 | QString summary; ///< Task title (SUMMARY) | ||
| 135 | QString description; ///< Task description (DESCRIPTION) | ||
| 136 | QDateTime dtStart; ///< Start date (DTSTART) | ||
| 137 | QDateTime due; ///< Due date (DUE) | ||
| 138 | bool dueAllDay = false; ///< DUE was encoded as VALUE=DATE | ||
| 139 | int percentComplete = 0; ///< 0–100 (PERCENT-COMPLETE) | ||
| 140 | int priority = 0; ///< 0–9 (PRIORITY, 0 = undefined, 1 = starred/important) | ||
| 141 | QString status; ///< NEEDS-ACTION | COMPLETED | IN-PROCESS | CANCELLED | ||
| 142 | QDateTime completedAt; ///< Completion timestamp (COMPLETED) | ||
| 143 | QDateTime created; ///< Creation timestamp (CREATED) | ||
| 144 | QString organizer; ///< Creator/organizer (ORGANIZER CN or mailto) | ||
| 145 | QString relatedMessageId; ///< Canonical Message-ID of the linked mail | ||
| 146 | QString etag; ///< DAV ETag | ||
| 147 | QDateTime lastModified; ///< LAST-MODIFIED timestamp | ||
| 148 | QString color; ///< Inherited from parent calendar | ||
| 149 | QString calendarDisplayName; ///< Display name of parent calendar | ||
| 150 | bool startAllDay = false; | ||
| 151 | QString icalRaw; | ||
| 152 | QString resourceIcal; | ||
| 153 | QString syncState; | ||
| 154 | QString parentUid; | ||
| 155 | QString rrule; | ||
| 156 | QStringList tags; | ||
| 157 | QList<QDateTime> alarmTimes; | ||
| 158 | // S106-351: X-APPLE-SORT-ORDER is server text; stored values stay within | ||
| 159 | // ±kTaskSortOrderLimit so order arithmetic never overflows. | ||
| 160 | qint64 sortOrder = 0; | ||
| 161 | bool readOnly = false; | ||
| 162 | QString previousStatus; | ||
| 163 | int previousPercent = 0; | ||
| 164 | |||
| 165 | /// Nextcloud Tasks: priority 1 = starred/important | ||
| 166 |
3/4✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 5 taken 1 time.
✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 5 not taken.
|
3 | bool isStarred() const { return priority >= 1 && priority <= 4; } |
| 167 | }; | ||
| 168 | |||
| 169 | 779698 | inline QString taskSelectionKey(const CalendarTask &task) { | |
| 170 |
3/3✓ Branch 4 → 5 taken 779698 times.
✓ Branch 5 → 6 taken 779698 times.
✓ Branch 6 → 7 taken 779698 times.
|
779698 | return calendarSelectionKey(task.accountId, task.calendarPath) + QLatin1Char('\n') + task.uid; |
| 171 | } | ||
| 172 |