Skip to content

Commit

Permalink
[buteo-sync-plugin-caldav] Ensure VTODO are properly imported, compar…
Browse files Browse the repository at this point in the history
…ed and exported.
  • Loading branch information
dcaliste committed Sep 16, 2019
1 parent ecc2a76 commit 4db2e6f
Showing 1 changed file with 99 additions and 86 deletions.
185 changes: 99 additions & 86 deletions src/incidencehandler.cpp
Expand Up @@ -444,21 +444,29 @@ void IncidenceHandler::prepareImportedIncidence(KCalCore::Incidence::Ptr inciden
LOG_WARNING("unable to handle imported non-event incidence; skipping");
return;
}
KCalCore::Event::Ptr event = incidence.staticCast<KCalCore::Event>();

if (event->allDay()) {
KDateTime dtStart = event->dtStart();
KDateTime dtEnd = event->dtEnd();
switch (incidence->type()) {
case KCalCore::IncidenceBase::TypeEvent: {
KCalCore::Event::Ptr event = incidence.staticCast<KCalCore::Event>();

// calendar processing requires all-day events to have a dtEnd
if (!dtEnd.isValid()) {
LOG_DEBUG("Adding DTEND to" << incidence->uid() << "as" << dtStart.toString());
event->setCustomProperty("buteo", PROP_DTEND_ADDED_USING_DTSTART, PROP_DTEND_ADDED_USING_DTSTART);
event->setDtEnd(dtStart);
}
if (event->allDay()) {
KDateTime dtStart = event->dtStart();
KDateTime dtEnd = event->dtEnd();

// calendar processing requires all-day events to have a dtEnd
if (!dtEnd.isValid()) {
LOG_DEBUG("Adding DTEND to" << incidence->uid() << "as" << dtStart.toString());
event->setCustomProperty("buteo", PROP_DTEND_ADDED_USING_DTSTART, PROP_DTEND_ADDED_USING_DTSTART);
event->setDtEnd(dtStart);
}

// setting dtStart/End changes the allDay value, so ensure it is still set to true
event->setAllDay(true);
// setting dtStart/End changes the allDay value, so ensure it is still set to true
event->setAllDay(true);
}
break;
}
default:
break;
}
}

Expand Down Expand Up @@ -521,81 +529,59 @@ QString IncidenceHandler::toIcs(KCalCore::Incidence::Ptr incidence,

KCalCore::Incidence::Ptr IncidenceHandler::incidenceToExport(KCalCore::Incidence::Ptr sourceIncidence, const KCalCore::Incidence::List &instances)
{
if (sourceIncidence->type() != KCalCore::IncidenceBase::TypeEvent) {
LOG_DEBUG("Incidence not an event; cannot create exportable version");
return sourceIncidence;
}

KCalCore::Incidence::Ptr incidence = QSharedPointer<KCalCore::Incidence>(sourceIncidence->clone());
KCalCore::Event::Ptr event = incidence.staticCast<KCalCore::Event>();

// check to see if the UID is of the special form: NBUID:NotebookUid:EventUid. If so, trim it.
if (event->uid().startsWith(QStringLiteral("NBUID:"))) {
QString oldUid = event->uid();
if (incidence->uid().startsWith(QStringLiteral("NBUID:"))) {
QString oldUid = incidence->uid();
QString trimmedUid = oldUid.mid(oldUid.indexOf(':', 6)+1); // remove NBUID:NotebookUid:
event->setUid(trimmedUid); // leaving just the EventUid.
}

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.
LOG_DEBUG("Removing DTEND from" << incidence->uid());
event->setDtEnd(KDateTime());
} else {
KDateTime dt;
if (event->hasEndDate()) {
// Event::dtEnd() is inclusive, but DTEND in iCalendar format is exclusive.
dt = KDateTime(event->dtEnd().addDays(1).date(), KDateTime::Spec::ClockTime());
LOG_DEBUG("Adding +1 day to DTEND to make exclusive DTEND for" << incidence->uid() << ":" << dt.toString());
} else {
// No DTEND exists in event, but it's all day. Set to DTSTART+1 to make exclusive DTEND.
dt = KDateTime(event->dtStart().addDays(1).date(), KDateTime::Spec::ClockTime());
LOG_DEBUG("Setting DTEND to DTSTART+1 for" << incidence->uid() << ":" << dt.toString());
}
dt.setDateOnly(true);
LOG_DEBUG("Stripping time from date-only DTEND:" << dt.toString());
event->setDtEnd(dt);
}
}

if (event->dtStart().isDateOnly()) {
KDateTime dt = KDateTime(event->dtStart().date(), KDateTime::Spec::ClockTime());
dt.setDateOnly(true);
event->setDtStart(dt);
LOG_DEBUG("Stripping time from date-only DTSTART:" << dt.toString());
}

// setting dtStart/End changes the allDay value, so ensure it is still set to true if needed.
if (eventIsAllDay) {
event->setAllDay(true);
incidence->setUid(trimmedUid); // leaving just the EventUid.
}

// remove any (obsolete) markers that tell us that the time was added by us
event->removeCustomProperty("buteo", "dtstart-date_only");
event->removeCustomProperty("buteo", "dtend-date_only");
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.
event->removeCustomProperty("buteo", "uri");
event->removeCustomProperty("buteo", "etag");
const QStringList &comments(event->comments());
incidence->removeCustomProperty("buteo", "uri");
incidence->removeCustomProperty("buteo", "etag");
const QStringList &comments(incidence->comments());
Q_FOREACH (const QString &comment, comments) {
if (comment.startsWith("buteo:caldav:uri:") ||
comment.startsWith("buteo:caldav:detached-and-synced") ||
comment.startsWith("buteo:caldav:etag:")) {
LOG_DEBUG("Discarding buteo-prefixed comment:" << comment);
event->removeComment(comment);
incidence->removeComment(comment);
}
}

// The default storage implementation applies the organizer as an attendee by default.
// Undo this as it turns the incidence into a scheduled event requiring acceptance/rejection/etc.
const KCalCore::Person::Ptr organizer = incidence->organizer();
if (organizer) {
Q_FOREACH (const KCalCore::Attendee::Ptr &attendee, incidence->attendees()) {
if (attendee->email() == organizer->email() && attendee->fullName() == organizer->fullName()) {
LOG_DEBUG("Discarding organizer as attendee" << attendee->fullName());
incidence->deleteAttendee(attendee);
} else {
LOG_DEBUG("Not discarding attendee:" << attendee->fullName() << attendee->email() << ": not organizer:" << organizer->fullName() << organizer->email());
}
}
}

// remove EXDATE values from the recurring incidence which correspond to the persistent occurrences (instances)
if (incidence->recurs()) {
Q_FOREACH (KCalCore::Incidence::Ptr instance, instances) {
KCalCore::DateTimeList exDateTimes = incidence->recurrence()->exDateTimes();
exDateTimes.removeAll(instance->recurrenceId());
incidence->recurrence()->setExDateTimes(exDateTimes);
LOG_DEBUG("Discarding exdate:" << instance->recurrenceId().toString());
}
}

// Icalformat from kcalcore converts second-type duration into day-type duration
// whenever possible. We do the same to have consistent comparisons.
KCalCore::Alarm::List alarms = event->alarms();
KCalCore::Alarm::List alarms = incidence->alarms();
Q_FOREACH (KCalCore::Alarm::Ptr alarm, alarms) {
if (!alarm->hasTime()) {
KCalCore::Duration offset(0);
Expand All @@ -613,29 +599,56 @@ KCalCore::Incidence::Ptr IncidenceHandler::incidenceToExport(KCalCore::Incidence
}
}

// The default storage implementation applies the organizer as an attendee by default.
// Undo this as it turns the incidence into a scheduled event requiring acceptance/rejection/etc.
const KCalCore::Person::Ptr organizer = event->organizer();
if (organizer) {
Q_FOREACH (const KCalCore::Attendee::Ptr &attendee, event->attendees()) {
if (attendee->email() == organizer->email() && attendee->fullName() == organizer->fullName()) {
LOG_DEBUG("Discarding organizer as attendee" << attendee->fullName());
event->deleteAttendee(attendee);
switch (incidence->type()) {
case KCalCore::IncidenceBase::TypeEvent: {
KCalCore::Event::Ptr event = incidence.staticCast<KCalCore::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.
LOG_DEBUG("Removing DTEND from" << incidence->uid());
event->setDtEnd(KDateTime());
} else {
LOG_DEBUG("Not discarding attendee:" << attendee->fullName() << attendee->email() << ": not organizer:" << organizer->fullName() << organizer->email());
KDateTime dt;
if (event->hasEndDate()) {
// Event::dtEnd() is inclusive, but DTEND in iCalendar format is exclusive.
dt = KDateTime(event->dtEnd().addDays(1).date(), KDateTime::Spec::ClockTime());
LOG_DEBUG("Adding +1 day to DTEND to make exclusive DTEND for" << incidence->uid() << ":" << dt.toString());
} else {
// No DTEND exists in event, but it's all day. Set to DTSTART+1 to make exclusive DTEND.
dt = KDateTime(event->dtStart().addDays(1).date(), KDateTime::Spec::ClockTime());
LOG_DEBUG("Setting DTEND to DTSTART+1 for" << incidence->uid() << ":" << dt.toString());
}
dt.setDateOnly(true);
LOG_DEBUG("Stripping time from date-only DTEND:" << dt.toString());
event->setDtEnd(dt);
}
}
}

// remove EXDATE values from the recurring incidence which correspond to the persistent occurrences (instances)
if (incidence->recurs()) {
Q_FOREACH (KCalCore::Incidence::Ptr instance, instances) {
KCalCore::DateTimeList exDateTimes = incidence->recurrence()->exDateTimes();
exDateTimes.removeAll(instance->recurrenceId());
incidence->recurrence()->setExDateTimes(exDateTimes);
LOG_DEBUG("Discarding exdate:" << instance->recurrenceId().toString());
if (event->dtStart().isDateOnly()) {
KDateTime dt = KDateTime(event->dtStart().date(), KDateTime::Spec::ClockTime());
dt.setDateOnly(true);
event->setDtStart(dt);
LOG_DEBUG("Stripping time from date-only DTSTART:" << dt.toString());
}

// setting dtStart/End changes the allDay value, so ensure it is still set to true if needed.
if (eventIsAllDay) {
event->setAllDay(true);
}
break;
}
case KCalCore::IncidenceBase::TypeTodo:
break;
default:
LOG_DEBUG("Incidence type not supported; cannot create proper exportable version");
break;
}

return event;
return incidence;
}

0 comments on commit 4db2e6f

Please sign in to comment.