1 /*
2 SPDX-FileCopyrightText: 2001, 2004 Cornelius Schumacher <schumacher@kde.org>
3 SPDX-FileCopyrightText: 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
4 SPDX-FileCopyrightText: 2012-2013 Sérgio Martins <iamsergio@gmail.com>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 #include "scheduler_p.h"
9 #include "calendarbase_p.h"
10
11 #include <KCalUtils/Stringify>
12
13 #include <KCalendarCore/FreeBusyCache>
14 #include <KCalendarCore/ICalFormat>
15
16 #include "akonadicalendar_debug.h"
17 #include <KLocalizedString>
18 #include <KMessageBox>
19 #include <QTimeZone>
20
21 using namespace KCalendarCore;
22 using namespace Akonadi;
23
24 class Akonadi::SchedulerPrivate
25 {
26 public:
SchedulerPrivate(Scheduler * qq)27 explicit SchedulerPrivate(Scheduler *qq)
28 : q(qq)
29 {
30 }
31
32 FreeBusyCache *mFreeBusyCache = nullptr;
33 bool mShowDialogs = true;
34 Scheduler *const q;
35 };
36
Scheduler(QObject * parent)37 Scheduler::Scheduler(QObject *parent)
38 : QObject(parent)
39 , d(new SchedulerPrivate(this))
40 {
41 mFormat = new ICalFormat();
42 mFormat->setTimeZone(QTimeZone::systemTimeZone());
43 }
44
~Scheduler()45 Scheduler::~Scheduler()
46 {
47 delete mFormat;
48 }
49
setShowDialogs(bool enable)50 void Scheduler::setShowDialogs(bool enable)
51 {
52 d->mShowDialogs = enable;
53 }
54
setFreeBusyCache(FreeBusyCache * c)55 void Scheduler::setFreeBusyCache(FreeBusyCache *c)
56 {
57 d->mFreeBusyCache = c;
58 }
59
freeBusyCache() const60 FreeBusyCache *Scheduler::freeBusyCache() const
61 {
62 return d->mFreeBusyCache;
63 }
64
acceptTransaction(const IncidenceBase::Ptr & incidence,const Akonadi::CalendarBase::Ptr & calendar,iTIPMethod method,ScheduleMessage::Status status,const QString & email)65 void Scheduler::acceptTransaction(const IncidenceBase::Ptr &incidence,
66 const Akonadi::CalendarBase::Ptr &calendar,
67 iTIPMethod method,
68 ScheduleMessage::Status status,
69 const QString &email)
70 {
71 Q_ASSERT(incidence);
72 Q_ASSERT(calendar);
73 qCDebug(AKONADICALENDAR_LOG) << "method=" << ScheduleMessage::methodName(method);
74 connectCalendar(calendar);
75 switch (method) {
76 case iTIPPublish:
77 acceptPublish(incidence, calendar, status, method);
78 break;
79 case iTIPRequest:
80 acceptRequest(incidence, calendar, status, email);
81 break;
82 case iTIPAdd:
83 acceptAdd(incidence, status);
84 break;
85 case iTIPCancel:
86 acceptCancel(incidence, calendar, status, email);
87 break;
88 case iTIPDeclineCounter:
89 acceptDeclineCounter(incidence, status);
90 break;
91 case iTIPReply:
92 acceptReply(incidence, calendar, status, method);
93 break;
94 case iTIPRefresh:
95 acceptRefresh(incidence, status);
96 break;
97 case iTIPCounter:
98 acceptCounter(incidence, status);
99 break;
100 default:
101 qCWarning(AKONADICALENDAR_LOG) << "Unhandled method: " << method;
102 }
103 }
104
acceptPublish(const IncidenceBase::Ptr & newIncBase,const Akonadi::CalendarBase::Ptr & calendar,ScheduleMessage::Status status,iTIPMethod method)105 void Scheduler::acceptPublish(const IncidenceBase::Ptr &newIncBase,
106 const Akonadi::CalendarBase::Ptr &calendar,
107 ScheduleMessage::Status status,
108 iTIPMethod method)
109 {
110 if (newIncBase->type() == IncidenceBase::TypeFreeBusy) {
111 acceptFreeBusy(newIncBase, method);
112 return;
113 }
114
115 QString errorString;
116 Result result = ResultSuccess;
117
118 qCDebug(AKONADICALENDAR_LOG) << "status=" << KCalUtils::Stringify::scheduleMessageStatus(status);
119
120 Incidence::Ptr newInc = newIncBase.staticCast<Incidence>();
121 Incidence::Ptr calInc = calendar->incidence(newIncBase->uid());
122 switch (status) {
123 case ScheduleMessage::Unknown:
124 case ScheduleMessage::PublishNew:
125 case ScheduleMessage::PublishUpdate:
126 if (calInc && newInc) {
127 if ((newInc->revision() > calInc->revision()) || (newInc->revision() == calInc->revision() && newInc->lastModified() > calInc->lastModified())) {
128 const QString oldUid = calInc->uid();
129
130 if (calInc->type() != newInc->type()) {
131 result = ResultAssigningDifferentTypes;
132 errorString = i18n("Error: Assigning different incidence types.");
133 qCritical() << errorString;
134 } else {
135 newInc->setSchedulingID(newInc->uid(), oldUid);
136 const bool success = calendar->modifyIncidence(newInc);
137
138 if (!success) {
139 Q_EMIT transactionFinished(ResultModifyingError, QStringLiteral("Error modifying incidence"));
140 } else {
141 // signal will be emitted in the handleModifyFinished() slot
142 }
143
144 return;
145 }
146 }
147 }
148 break;
149 case ScheduleMessage::Obsolete:
150 break;
151 default:
152 break;
153 }
154
155 Q_EMIT transactionFinished(result, errorString);
156 }
157
acceptRequest(const IncidenceBase::Ptr & incidenceBase,const Akonadi::CalendarBase::Ptr & calendar,ScheduleMessage::Status status,const QString & email)158 void Scheduler::acceptRequest(const IncidenceBase::Ptr &incidenceBase,
159 const Akonadi::CalendarBase::Ptr &calendar,
160 ScheduleMessage::Status status,
161 const QString &email)
162 {
163 Incidence::Ptr incidence = incidenceBase.staticCast<Incidence>();
164
165 if (incidence->type() == IncidenceBase::TypeFreeBusy) {
166 // reply to this request is handled in korganizer's incomingdialog
167 Q_EMIT transactionFinished(ResultSuccess, QString());
168 return;
169 }
170
171 const QString schedulingUid = incidence->uid();
172 QString errorString;
173 Result result = ResultSuccess;
174
175 const Incidence::List existingIncidences = calendar->incidencesFromSchedulingID(schedulingUid);
176 qCDebug(AKONADICALENDAR_LOG) << "status=" << KCalUtils::Stringify::scheduleMessageStatus(status) << ": found " << existingIncidences.count()
177 << " incidences with schedulingID " << incidence->schedulingID() << "; uid was = " << schedulingUid;
178
179 if (existingIncidences.isEmpty()) {
180 // Perfectly normal if the incidence doesn't exist. This is probably
181 // a new invitation.
182 qCDebug(AKONADICALENDAR_LOG) << "incidence not found; calendar = " << calendar.data() << "; incidence count = " << calendar->incidences().count();
183 }
184
185 for (const KCalendarCore::Incidence::Ptr &existingIncidence : existingIncidences) {
186 qCDebug(AKONADICALENDAR_LOG) << "Considering this found event (" << (existingIncidence->isReadOnly() ? "readonly" : "readwrite")
187 << ") :" << mFormat->toString(existingIncidence);
188 // If it's readonly, we can't possible update it.
189 if (existingIncidence->isReadOnly()) {
190 continue;
191 }
192
193 const QString existingUid = existingIncidence->uid();
194 const int existingRevision = existingIncidence->revision();
195
196 if (existingRevision <= incidence->revision()) {
197 // The new incidence might be an update for the found one
198 bool isUpdate = true;
199 // Code for new invitations:
200 // If you think we could check the value of "status" to be RequestNew: we can't.
201 // It comes from a similar check inside libical, where the event is compared to
202 // other events in the calendar. But if we have another version of the event around
203 // (e.g. shared folder for a group), the status could be RequestNew, Obsolete or Updated.
204 qCDebug(AKONADICALENDAR_LOG) << "looking in " << existingUid << "'s attendees";
205 // This is supposed to be a new request, not an update - however we want to update
206 // the existing one to handle the "clicking more than once on the invitation" case.
207 // So check the attendee status of the attendee.
208 const Attendee::List attendees = existingIncidence->attendees();
209 Attendee::List::ConstIterator ait;
210 for (ait = attendees.begin(); ait != attendees.end(); ++ait) {
211 if ((*ait).email() == email && (*ait).status() == Attendee::NeedsAction) {
212 // This incidence wasn't created by me - it's probably in a shared folder
213 // and meant for someone else, ignore it.
214 qCDebug(AKONADICALENDAR_LOG) << "ignoring " << existingUid << " since I'm still NeedsAction there";
215 isUpdate = false;
216 break;
217 }
218 }
219 if (isUpdate) {
220 if (existingRevision == incidence->revision() && existingIncidence->lastModified() > incidence->lastModified()) {
221 // This isn't an update - the found incidence was modified more recently
222 errorString = i18n(
223 "This isn't an update. "
224 "The found incidence was modified more recently.");
225 // QT5 port
226 #if 0
227 qCWarning(AKONADICALENDAR_LOG) << errorString
228 << "; revision=" << existingIncidence->revision()
229 << "; existing->lastModified=" << existingIncidence->lastModified()
230 << "; update->lastModified=" << incidence->lastModified();
231 #endif
232 Q_EMIT transactionFinished(ResultOutatedUpdate, errorString);
233 return;
234 }
235 qCDebug(AKONADICALENDAR_LOG) << "replacing existing incidence " << existingUid;
236 if (existingIncidence->type() != incidence->type()) {
237 qCritical() << "assigning different incidence types";
238 result = ResultAssigningDifferentTypes;
239 errorString = i18n("Error: Assigning different incidence types.");
240 Q_EMIT transactionFinished(result, errorString);
241 } else {
242 incidence->setSchedulingID(schedulingUid, existingUid);
243
244 if (incidence->hasRecurrenceId()) {
245 Incidence::Ptr existingInstance = calendar->incidence(incidence->instanceIdentifier());
246 if (!existingInstance) {
247 // The organizer created an exception, lets create it in our calendar, we don't have it yet
248 const bool success = calendar->addIncidence(incidence);
249
250 if (!success) {
251 Q_EMIT transactionFinished(ResultCreatingError, QStringLiteral("Error creating incidence"));
252 } else {
253 // Signal emitted in the result slot of addFinished()
254 }
255
256 return;
257 }
258 }
259
260 const bool success = calendar->modifyIncidence(incidence);
261
262 if (!success) {
263 Q_EMIT transactionFinished(ResultModifyingError, i18n("Error modifying incidence"));
264 } else {
265 // handleModifyFinished() will Q_EMIT the final signal.
266 }
267 }
268 return;
269 }
270 } else {
271 errorString = i18n(
272 "This isn't an update. "
273 "The found incidence was modified more recently.");
274 qCWarning(AKONADICALENDAR_LOG) << errorString;
275 // This isn't an update - the found incidence has a bigger revision number
276 qCDebug(AKONADICALENDAR_LOG) << "This isn't an update - the found incidence has a bigger revision number";
277 Q_EMIT transactionFinished(ResultOutatedUpdate, errorString);
278 return;
279 }
280 }
281
282 // Move the uid to be the schedulingID and make a unique UID
283 incidence->setSchedulingID(schedulingUid, CalFormat::createUniqueId());
284 // notify the user in case this is an update and we didn't find the to-be-updated incidence
285 if (d->mShowDialogs && existingIncidences.isEmpty() && incidence->revision() > 0) {
286 KMessageBox::information(nullptr,
287 xi18nc("@info",
288 "<para>You added an invitation update, but an earlier version of the "
289 "item could not be found in your calendar.</para>"
290 "<para>This may have occurred because:<list>"
291 "<item>the organizer did not include you in the original invitation</item>"
292 "<item>you did not accept the original invitation yet</item>"
293 "<item>you deleted the original invitation from your calendar</item>"
294 "<item>you no longer have access to the calendar containing the invitation</item>"
295 "</list></para>"
296 "<para>This is not a problem, but we thought you should know.</para>"),
297 i18nc("@title", "Cannot find invitation to be updated"),
298 QStringLiteral("AcceptCantFindIncidence"));
299 }
300 qCDebug(AKONADICALENDAR_LOG) << "Storing new incidence with scheduling uid=" << schedulingUid << " and uid=" << incidence->uid();
301
302 const bool success = calendar->addIncidence(incidence);
303 if (!success) {
304 Q_EMIT transactionFinished(ResultCreatingError, i18n("Error adding incidence"));
305 } else {
306 // The slot will Q_EMIT the result
307 }
308 }
309
acceptAdd(const IncidenceBase::Ptr &,ScheduleMessage::Status)310 void Scheduler::acceptAdd(const IncidenceBase::Ptr &, ScheduleMessage::Status)
311 {
312 Q_EMIT transactionFinished(ResultSuccess, QString());
313 }
314
acceptCancel(const IncidenceBase::Ptr & incidenceBase,const Akonadi::CalendarBase::Ptr & calendar,ScheduleMessage::Status status,const QString & attendeeEmail)315 void Scheduler::acceptCancel(const IncidenceBase::Ptr &incidenceBase,
316 const Akonadi::CalendarBase::Ptr &calendar,
317 ScheduleMessage::Status status,
318 const QString &attendeeEmail)
319 {
320 Incidence::Ptr incidence = incidenceBase.staticCast<Incidence>();
321
322 if (incidence->type() == IncidenceBase::TypeFreeBusy) {
323 // reply to this request is handled in korganizer's incomingdialog
324 Q_EMIT transactionFinished(ResultSuccess, QString());
325 return;
326 }
327
328 if (incidence->type() == IncidenceBase::TypeJournal) {
329 Q_EMIT transactionFinished(ResultUnsupported, QStringLiteral("Unsupported incidence type"));
330 return;
331 }
332
333 const Incidence::List existingIncidences = calendar->incidencesFromSchedulingID(incidence->uid());
334 qCDebug(AKONADICALENDAR_LOG) << "Scheduler::acceptCancel=" << KCalUtils::Stringify::scheduleMessageStatus(status) << ": found "
335 << existingIncidences.count() << " incidences with schedulingID " << incidence->schedulingID();
336
337 Result result = ResultIncidenceToDeleteNotFound;
338 const QString errorString = i18n("Could not find incidence to delete.");
339 for (const KCalendarCore::Incidence::Ptr &existingIncidence : existingIncidences) {
340 qCDebug(AKONADICALENDAR_LOG) << "Considering this found event (" << (existingIncidence->isReadOnly() ? "readonly" : "readwrite")
341 << ") :" << mFormat->toString(existingIncidence);
342
343 // If it's readonly, we can't possible remove it.
344 if (existingIncidence->isReadOnly()) {
345 continue;
346 }
347
348 const QString existingUid = existingIncidence->uid();
349
350 // Code for new invitations:
351 // We cannot check the value of "status" to be RequestNew because
352 // "status" comes from a similar check inside libical, where the event
353 // is compared to other events in the calendar. But if we have another
354 // version of the event around (e.g. shared folder for a group), the
355 // status could be RequestNew, Obsolete or Updated.
356 qCDebug(AKONADICALENDAR_LOG) << "looking in " << existingUid << "'s attendees";
357
358 // This is supposed to be a new request, not an update - however we want
359 // to update the existing one to handle the "clicking more than once
360 // on the invitation" case. So check the attendee status of the attendee.
361 bool isMine = true;
362 const Attendee::List attendees = existingIncidence->attendees();
363 for (const KCalendarCore::Attendee &attendee : attendees) {
364 if (attendee.email() == attendeeEmail && attendee.status() == Attendee::NeedsAction) {
365 // This incidence wasn't created by me - it's probably in a shared
366 // folder and meant for someone else, ignore it.
367 qCDebug(AKONADICALENDAR_LOG) << "ignoring " << existingUid << " since I'm still NeedsAction there";
368 isMine = false;
369 break;
370 }
371 }
372
373 if (!isMine) {
374 continue;
375 }
376
377 qCDebug(AKONADICALENDAR_LOG) << "removing existing incidence " << existingUid;
378 if (incidence->hasRecurrenceId()) {
379 Incidence::Ptr existingInstance = calendar->incidence(incidence->instanceIdentifier());
380
381 if (existingInstance) {
382 existingInstance->setStatus(Incidence::StatusCanceled);
383 result = calendar->modifyIncidence(existingInstance) ? ResultSuccess : ResultModifyingError;
384 } else {
385 incidence->setSchedulingID(incidence->uid(), existingIncidence->uid());
386 incidence->setStatus(Incidence::StatusCanceled);
387 result = calendar->addIncidence(incidence) ? ResultSuccess : ResultCreatingError;
388 }
389
390 if (result != ResultSuccess) {
391 Q_EMIT transactionFinished(result, i18n("Error recording exception"));
392 }
393 } else {
394 result = calendar->deleteIncidence(existingIncidence) ? ResultSuccess : ResultErrorDelete;
395 if (result != ResultSuccess) {
396 Q_EMIT transactionFinished(result, errorString);
397 }
398 }
399
400 // The success case will be handled in handleDeleteFinished()
401 return;
402 }
403
404 // in case we didn't find the to-be-removed incidencez
405 if (d->mShowDialogs && !existingIncidences.isEmpty() && incidence->revision() > 0) {
406 KMessageBox::error(nullptr,
407 i18nc("@info",
408 "The event or task could not be removed from your calendar. "
409 "Maybe it has already been deleted or is not owned by you. "
410 "Or it might belong to a read-only or disabled calendar."));
411 }
412 Q_EMIT transactionFinished(result, errorString);
413 }
414
acceptDeclineCounter(const IncidenceBase::Ptr &,ScheduleMessage::Status)415 void Scheduler::acceptDeclineCounter(const IncidenceBase::Ptr &, ScheduleMessage::Status)
416 {
417 // Not sure why KCalUtils::Scheduler returned false here
418 Q_EMIT transactionFinished(ResultGenericError, i18n("Generic Error"));
419 }
420
acceptReply(const IncidenceBase::Ptr & incidenceBase,const Akonadi::CalendarBase::Ptr & calendar,ScheduleMessage::Status status,iTIPMethod method)421 void Scheduler::acceptReply(const IncidenceBase::Ptr &incidenceBase,
422 const Akonadi::CalendarBase::Ptr &calendar,
423 ScheduleMessage::Status status,
424 iTIPMethod method)
425 {
426 Q_UNUSED(status)
427 if (incidenceBase->type() == IncidenceBase::TypeFreeBusy) {
428 acceptFreeBusy(incidenceBase, method);
429 return;
430 }
431
432 Result result = ResultGenericError;
433 QString errorString = i18n("Generic Error");
434
435 Incidence::Ptr incidence = calendar->incidence(incidenceBase->uid());
436
437 // try harder to find the correct incidence
438 if (!incidence) {
439 const Incidence::List list = calendar->incidences();
440 for (Incidence::List::ConstIterator it = list.constBegin(), end = list.constEnd(); it != end; ++it) {
441 if ((*it)->schedulingID() == incidenceBase->uid()) {
442 incidence = (*it).dynamicCast<Incidence>();
443 break;
444 }
445 }
446 }
447
448 if (incidence) {
449 // get matching attendee in calendar
450 qCDebug(AKONADICALENDAR_LOG) << "match found!";
451 const Attendee::List attendeesIn = incidenceBase->attendees();
452 Attendee::List attendeesNew;
453 Attendee::List attendeesEv = incidence->attendees();
454 for (const auto &attIn : attendeesIn) {
455 bool found = false;
456 for (auto &attEv : attendeesEv) {
457 if (attIn.email().toLower() == attEv.email().toLower()) {
458 // update attendee-info
459 qCDebug(AKONADICALENDAR_LOG) << "update attendee";
460 attEv.setStatus(attIn.status());
461 attEv.setDelegate(attIn.delegate());
462 attEv.setDelegator(attIn.delegator());
463 result = ResultSuccess;
464 errorString.clear();
465 found = true;
466 }
467 }
468 if (!found && attIn.status() != Attendee::Declined) {
469 attendeesNew.append(attIn);
470 }
471 }
472 incidence->setAttendees(attendeesEv);
473
474 bool attendeeAdded = false;
475 for (const auto &attNew : std::as_const(attendeesNew)) {
476 QString msg = i18nc("@info", "%1 wants to attend %2 but was not invited.", attNew.fullName(), incidence->summary());
477 if (!attNew.delegator().isEmpty()) {
478 msg = i18nc("@info", "%1 wants to attend %2 on behalf of %3.", attNew.fullName(), incidence->summary(), attNew.delegator());
479 }
480 if (KMessageBox::questionYesNo(nullptr,
481 msg,
482 i18nc("@title", "Uninvited attendee"),
483 KGuiItem(i18nc("@option", "Accept Attendance")),
484 KGuiItem(i18nc("@option", "Reject Attendance")))
485 != KMessageBox::Yes) {
486 Incidence::Ptr cancel = incidence;
487 cancel->addComment(i18nc("@info", "The organizer rejected your attendance at this meeting."));
488 performTransaction(incidenceBase, iTIPCancel, attNew.fullName());
489 continue;
490 }
491
492 Attendee a(attNew.name(), attNew.email(), attNew.RSVP(), attNew.status(), attNew.role(), attNew.uid());
493
494 a.setDelegate(attNew.delegate());
495 a.setDelegator(attNew.delegator());
496 incidence->addAttendee(a);
497
498 result = ResultSuccess;
499 errorString.clear();
500 attendeeAdded = true;
501 }
502
503 // send update about new participants
504 if (attendeeAdded) {
505 bool sendMail = false;
506 if (KMessageBox::questionYesNo(nullptr,
507 i18nc("@info",
508 "An attendee was added to the incidence. "
509 "Do you want to email the attendees an update message?"),
510 i18nc("@title", "Attendee Added"),
511 KGuiItem(i18nc("@option", "Send Messages")),
512 KGuiItem(i18nc("@option", "Do Not Send")))
513 == KMessageBox::Yes) {
514 sendMail = true;
515 }
516
517 incidence->setRevision(incidence->revision() + 1);
518 if (sendMail) {
519 performTransaction(incidence, iTIPRequest);
520 }
521 }
522
523 if (incidence->type() == Incidence::TypeTodo) {
524 // for VTODO a REPLY can be used to update the completion status of
525 // a to-do. see RFC2446 3.4.3
526 Todo::Ptr update = incidenceBase.dynamicCast<Todo>();
527 Todo::Ptr calendarTodo = incidence.staticCast<Todo>();
528 Q_ASSERT(update);
529 if (update && (calendarTodo->percentComplete() != update->percentComplete())) {
530 calendarTodo->setPercentComplete(update->percentComplete());
531 calendarTodo->updated();
532 const bool success = calendar->modifyIncidence(calendarTodo);
533 if (!success) {
534 Q_EMIT transactionFinished(ResultModifyingError, i18n("Error modifying incidence"));
535 } else {
536 // success will be emitted in the handleModifyFinished() slot
537 }
538 return;
539 }
540 }
541
542 if (result == ResultSuccess) {
543 // We set at least one of the attendees, so the incidence changed
544 // Note: This should not result in a sequence number bump
545 incidence->updated();
546 const bool success = calendar->modifyIncidence(incidence);
547
548 if (!success) {
549 Q_EMIT transactionFinished(ResultModifyingError, i18n("Error modifying incidence"));
550 } else {
551 // success will be emitted in the handleModifyFinished() slot
552 }
553
554 return;
555 }
556 } else {
557 result = ResultSuccess;
558 errorString = i18n("No incidence for scheduling.");
559 qCritical() << errorString;
560 }
561 Q_EMIT transactionFinished(result, errorString);
562 }
563
acceptRefresh(const IncidenceBase::Ptr &,ScheduleMessage::Status)564 void Scheduler::acceptRefresh(const IncidenceBase::Ptr &, ScheduleMessage::Status)
565 {
566 // handled in korganizer's IncomingDialog
567 // Not sure why it returns false here
568 Q_EMIT transactionFinished(ResultGenericError, i18n("Generic Error"));
569 }
570
acceptCounter(const IncidenceBase::Ptr &,ScheduleMessage::Status)571 void Scheduler::acceptCounter(const IncidenceBase::Ptr &, ScheduleMessage::Status)
572 {
573 // Not sure why it returns false here
574 Q_EMIT transactionFinished(ResultGenericError, i18n("Generic Error"));
575 }
576
acceptFreeBusy(const IncidenceBase::Ptr & incidence,iTIPMethod method)577 void Scheduler::acceptFreeBusy(const IncidenceBase::Ptr &incidence, iTIPMethod method)
578 {
579 if (!d->mFreeBusyCache) {
580 qCritical() << "Scheduler: no FreeBusyCache.";
581 Q_EMIT transactionFinished(ResultNoFreeBusyCache, i18n("No Free Busy Cache"));
582 return;
583 }
584
585 FreeBusy::Ptr freebusy = incidence.staticCast<FreeBusy>();
586
587 qCDebug(AKONADICALENDAR_LOG) << "freeBusyDirName:" << freeBusyDir();
588
589 Person from;
590 if (method == iTIPPublish) {
591 from = freebusy->organizer();
592 }
593 if ((method == iTIPReply) && (freebusy->attendeeCount() == 1)) {
594 Attendee attendee = freebusy->attendees().at(0);
595 from.setName(attendee.name());
596 from.setEmail(attendee.email());
597 }
598
599 if (!d->mFreeBusyCache->saveFreeBusy(freebusy, from)) {
600 Q_EMIT transactionFinished(ResultErrorSavingFreeBusy, i18n("Error saving freebusy object"));
601 } else {
602 Q_EMIT transactionFinished(ResultNoFreeBusyCache, QString());
603 }
604 }
605
handleCreateFinished(bool success,const QString & errorMessage)606 void Scheduler::handleCreateFinished(bool success, const QString &errorMessage)
607 {
608 auto calendar = qobject_cast<Akonadi::CalendarBase *>(sender());
609 const bool cancelled = calendar && calendar->d_ptr->mLastCreationCancelled;
610
611 Q_EMIT transactionFinished(success ? ResultSuccess : (cancelled ? ResultUserCancelled : ResultCreatingError), errorMessage);
612 }
613
handleModifyFinished(bool success,const QString & errorMessage)614 void Scheduler::handleModifyFinished(bool success, const QString &errorMessage)
615 {
616 qCDebug(AKONADICALENDAR_LOG) << "Modification finished. Success=" << success << errorMessage;
617 Q_EMIT transactionFinished(success ? ResultSuccess : ResultModifyingError, errorMessage);
618 }
619
handleDeleteFinished(bool success,const QString & errorMessage)620 void Scheduler::handleDeleteFinished(bool success, const QString &errorMessage)
621 {
622 Q_EMIT transactionFinished(success ? ResultSuccess : ResultDeletingError, errorMessage);
623 }
624
connectCalendar(const Akonadi::CalendarBase::Ptr & calendar)625 void Scheduler::connectCalendar(const Akonadi::CalendarBase::Ptr &calendar)
626 {
627 connect(calendar.data(), &CalendarBase::createFinished, this, &Scheduler::handleCreateFinished, Qt::UniqueConnection);
628 connect(calendar.data(), &CalendarBase::modifyFinished, this, &Scheduler::handleModifyFinished, Qt::UniqueConnection);
629 connect(calendar.data(), &CalendarBase::deleteFinished, this, &Scheduler::handleDeleteFinished, Qt::UniqueConnection);
630 }
631