/
incidencehandler.cpp
148 lines (128 loc) · 6.35 KB
/
incidencehandler.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
* This file is part of buteo-sync-plugin-caldav package
*
* Copyright (C) 2014 Jolla Ltd. and/or its subsidiary(-ies).
*
* Contributors: Bea Lam <bea.lam@jollamobile.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "incidencehandler.h"
#include <QDebug>
#include "logging.h"
#include <KCalendarCore/MemoryCalendar>
#include <KCalendarCore/ICalFormat>
#define PROP_DTEND_ADDED_USING_DTSTART "dtend-added-as-dtstart"
IncidenceHandler::IncidenceHandler()
{
}
IncidenceHandler::~IncidenceHandler()
{
}
// A given incidence has been added or modified locally.
// To upsync the change, we need to construct the .ics data to upload to server.
// Since the incidence may be an occurrence or recurring series incidence,
// we cannot simply convert the incidence to iCal data, but instead we have to
// upsync an .ics containing the whole recurring series.
QString IncidenceHandler::toIcs(const KCalendarCore::Incidence::Ptr incidence,
const KCalendarCore::Incidence::List instances)
{
// create an in-memory calendar
// add to it the required incidences (ie, check if has recurrenceId -> load parent and all instances; etc)
// for each of those, we need to do the IncidenceToExport() modifications first
// then, export from that calendar to .ics file.
KCalendarCore::MemoryCalendar::Ptr memoryCalendar(new KCalendarCore::MemoryCalendar(QTimeZone::utc()));
KCalendarCore::Incidence::Ptr exportableIncidence = IncidenceHandler::incidenceToExport(incidence, instances);
// store the base recurring event into the in-memory calendar
if (!memoryCalendar->addIncidence(exportableIncidence)) {
qCWarning(lcCalDav) << "Unable to add base series event to in-memory calendar for incidence:"
<< incidence->uid() << ":" << incidence->recurrenceId().toString();
return QString();
}
// now create the persistent occurrences in the in-memory calendar
for (KCalendarCore::Incidence::Ptr instance : instances) {
KCalendarCore::Incidence::Ptr exportableOccurrence = IncidenceHandler::incidenceToExport(instance);
if (!memoryCalendar->addIncidence(exportableOccurrence)) {
qCWarning(lcCalDav) << "Unable to add this incidence to in-memory calendar for export:"
<< instance->uid() << instance->recurrenceId().toString();
return QString();
}
}
KCalendarCore::ICalFormat icalFormat;
return icalFormat.toString(memoryCalendar, QString(), false);
}
KCalendarCore::Incidence::Ptr IncidenceHandler::incidenceToExport(KCalendarCore::Incidence::Ptr sourceIncidence, const KCalendarCore::Incidence::List &instances)
{
KCalendarCore::Incidence::Ptr incidence = QSharedPointer<KCalendarCore::Incidence>(sourceIncidence->clone());
// check to see if the UID is of the special form: NBUID:NotebookUid:EventUid. If so, trim it.
if (incidence->uid().startsWith(QStringLiteral("NBUID:"))) {
QString oldUid = incidence->uid();
QString trimmedUid = oldUid.mid(oldUid.indexOf(':', 6)+1); // remove NBUID:NotebookUid:
incidence->setUid(trimmedUid); // leaving just the EventUid.
}
// remove any (obsolete) markers that tell us that the time was added by us
incidence->removeCustomProperty("buteo", "dtstart-date_only");
incidence->removeCustomProperty("buteo", "dtend-date_only");
// remove any URI or ETAG data we insert into the event for sync purposes.
incidence->removeCustomProperty("buteo", "uri");
incidence->removeCustomProperty("buteo", "etag");
const QStringList &comments(incidence->comments());
for (const QString &comment : comments) {
if ((comment.startsWith("buteo:caldav:uri:") ||
comment.startsWith("buteo:caldav:detached-and-synced") ||
comment.startsWith("buteo:caldav:etag:"))
&& incidence->removeComment(comment)) {
qCDebug(lcCalDav) << "Discarding buteo-prefixed comment:" << comment;
}
}
// remove EXDATE values from the recurring incidence which correspond to the persistent occurrences (instances)
if (incidence->recurs()) {
for (KCalendarCore::Incidence::Ptr instance : instances) {
KCalendarCore::DateTimeList exDateTimes = incidence->recurrence()->exDateTimes();
exDateTimes.removeAll(instance->recurrenceId());
incidence->recurrence()->setExDateTimes(exDateTimes);
qCDebug(lcCalDav) << "Discarding exdate:" << instance->recurrenceId().toString();
}
}
switch (incidence->type()) {
case KCalendarCore::IncidenceBase::TypeEvent: {
KCalendarCore::Event::Ptr event = incidence.staticCast<KCalendarCore::Event>();
bool eventIsAllDay = event->allDay();
if (eventIsAllDay) {
bool sendWithoutDtEnd = !event->customProperty("buteo", PROP_DTEND_ADDED_USING_DTSTART).isEmpty()
&& (event->dtStart() == event->dtEnd());
event->removeCustomProperty("buteo", PROP_DTEND_ADDED_USING_DTSTART);
if (sendWithoutDtEnd) {
// A single-day all-day event was received without a DTEND, and it is still a single-day
// all-day event, so remove the DTEND before upsyncing.
qCDebug(lcCalDav) << "Removing DTEND from" << incidence->uid();
event->setDtEnd(QDateTime());
}
}
// setting dtStart/End changes the allDay value, so ensure it is still set to true if needed.
if (eventIsAllDay) {
event->setAllDay(true);
}
break;
}
case KCalendarCore::IncidenceBase::TypeTodo:
break;
default:
qCDebug(lcCalDav) << "Incidence type not supported; cannot create proper exportable version";
break;
}
return incidence;
}