22 #include "scheduler.h"
27 using namespace KCalCore;
30 #include <KLocalizedString>
31 #include <KMessageBox>
33 using namespace KCalUtils;
36 struct KCalUtils::Scheduler::Private
39 Private() : mFreeBusyCache(0)
53 Scheduler::~Scheduler()
61 d->mFreeBusyCache = c;
66 return d->mFreeBusyCache;
72 kDebug() <<
"method=" << ScheduleMessage::methodName(method);
76 return acceptPublish(incidence, status, method);
78 return acceptRequest(incidence, status, email);
80 return acceptAdd(incidence, status);
82 return acceptCancel(incidence, status, email);
84 return acceptDeclineCounter(incidence, status);
86 return acceptReply(incidence, status, method);
88 return acceptRefresh(incidence, status);
90 return acceptCounter(incidence, status);
94 deleteTransaction(incidence);
98 bool Scheduler::deleteTransaction(
const IncidenceBase::Ptr &)
106 if (newIncBase->type() == IncidenceBase::TypeFreeBusy) {
107 return acceptFreeBusy(newIncBase, method);
112 kDebug() <<
"status=" << Stringify::scheduleMessageStatus(status);
114 Incidence::Ptr newInc = newIncBase.staticCast<
Incidence>() ;
115 Incidence::Ptr calInc = mCalendar->incidence(newIncBase->uid());
117 case ScheduleMessage::Unknown:
118 case ScheduleMessage::PublishNew:
119 case ScheduleMessage::PublishUpdate:
120 if (calInc && newInc) {
121 if ((newInc->revision() > calInc->revision()) ||
122 (newInc->revision() == calInc->revision() &&
123 newInc->lastModified() > calInc->lastModified())) {
124 const QString oldUid = calInc->uid();
126 if (calInc->type() != newInc->type()) {
127 kError() <<
"assigning different incidence types";
132 calInc->setSchedulingID(newInc->uid(), oldUid);
138 case ScheduleMessage::Obsolete:
144 deleteTransaction(newIncBase);
148 bool Scheduler::acceptRequest(
const IncidenceBase::Ptr &incidence,
152 Incidence::Ptr inc = incidence.staticCast<
Incidence>() ;
154 kWarning() <<
"Accept what?";
157 if (inc->type() == IncidenceBase::TypeFreeBusy) {
162 const Incidence::List existingIncidences = mCalendar->incidencesFromSchedulingID(inc->uid());
163 kDebug() <<
"status=" << Stringify::scheduleMessageStatus(status)
164 <<
": found " << existingIncidences.count()
165 <<
" incidences with schedulingID " << inc->schedulingID()
166 <<
"; uid was = " << inc->uid();
168 if (existingIncidences.isEmpty()) {
171 kDebug() <<
"incidence not found; calendar = " << mCalendar.
data()
172 <<
"; incidence count = " << mCalendar->incidences().count();
174 Incidence::List::ConstIterator incit = existingIncidences.begin();
175 for (; incit != existingIncidences.end() ; ++incit) {
176 Incidence::Ptr existingIncidence = *incit;
177 kDebug() <<
"Considering this found event ("
178 << (existingIncidence->isReadOnly() ?
"readonly" :
"readwrite")
179 <<
") :" << mFormat->
toString(existingIncidence);
181 if (existingIncidence->isReadOnly()) {
184 if (existingIncidence->revision() <= inc->revision()) {
186 bool isUpdate =
true;
192 kDebug() <<
"looking in " << existingIncidence->uid() <<
"'s attendees";
197 Attendee::List::ConstIterator ait;
198 for (ait = attendees.
begin(); ait != attendees.
end(); ++ait) {
199 if ((*ait)->email() == email && (*ait)->status() == Attendee::NeedsAction) {
202 kDebug() <<
"ignoring " << existingIncidence->uid()
203 <<
" since I'm still NeedsAction there";
209 if (existingIncidence->revision() == inc->revision() &&
210 existingIncidence->lastModified() > inc->lastModified()) {
212 kDebug() <<
"This isn't an update - the found incidence was modified more recently";
213 deleteTransaction(existingIncidence);
216 kDebug() <<
"replacing existing incidence " << existingIncidence->uid();
218 const QString oldUid = existingIncidence->uid();
219 if (existingIncidence->type() != inc->type()) {
220 kError() <<
"assigning different incidence types";
223 IncidenceBase *existingIncidenceBase = existingIncidence.data();
225 *existingIncidenceBase = *incBase;
226 existingIncidence->setSchedulingID(inc->uid(), oldUid);
228 deleteTransaction(incidence);
233 kDebug() <<
"This isn't an update - the found incidence has a bigger revision number";
234 deleteTransaction(incidence);
240 inc->setSchedulingID(inc->uid(), CalFormat::createUniqueId());
242 if (existingIncidences.count() == 0 && inc->revision() > 0) {
243 KMessageBox::information(
246 "<para>You accepted an invitation update, but an earlier version of the "
247 "item could not be found in your calendar.</para>"
248 "<para>This may have occurred because:<list>"
249 "<item>the organizer did not include you in the original invitation</item>"
250 "<item>you did not accept the original invitation yet</item>"
251 "<item>you deleted the original invitation from your calendar</item>"
252 "<item>you no longer have access to the calendar containing the invitation</item>"
254 "<para>This is not a problem, but we thought you should know.</para>"),
255 i18nc(
"@title",
"Cannot find invitation to be updated"),
QLatin1String(
"AcceptCantFindIncidence"));
257 kDebug() <<
"Storing new incidence with scheduling uid=" << inc->schedulingID()
258 <<
" and uid=" << inc->uid();
259 const bool result = mCalendar->addIncidence(inc);
261 deleteTransaction(incidence);
265 bool Scheduler::acceptAdd(
const IncidenceBase::Ptr &incidence,
268 deleteTransaction(incidence);
272 bool Scheduler::acceptCancel(
const IncidenceBase::Ptr &incidence,
276 Incidence::Ptr inc = incidence.staticCast<
Incidence>();
281 if (inc->type() == IncidenceBase::TypeFreeBusy) {
286 const Incidence::List existingIncidences = mCalendar->incidencesFromSchedulingID(inc->uid());
287 kDebug() <<
"Scheduler::acceptCancel="
288 << Stringify::scheduleMessageStatus(status)
289 <<
": found " << existingIncidences.count()
290 <<
" incidences with schedulingID " << inc->schedulingID();
293 Incidence::List::ConstIterator incit = existingIncidences.begin();
294 for (; incit != existingIncidences.end() ; ++incit) {
295 Incidence::Ptr i = *incit;
296 kDebug() <<
"Considering this found event ("
297 << (i->isReadOnly() ?
"readonly" :
"readwrite")
301 if (i->isReadOnly()) {
311 kDebug() <<
"looking in " << i->uid() <<
"'s attendees";
318 Attendee::List::ConstIterator ait;
319 for (ait = attendees.
begin(); ait != attendees.
end(); ++ait) {
320 if ((*ait)->email() == attendee &&
321 (*ait)->status() == Attendee::NeedsAction) {
324 kDebug() <<
"ignoring " << i->uid()
325 <<
" since I'm still NeedsAction there";
332 kDebug() <<
"removing existing incidence " << i->uid();
333 if (i->type() == IncidenceBase::TypeEvent) {
334 Event::Ptr event = mCalendar->event(i->uid());
335 ret = (
event && mCalendar->deleteEvent(event));
336 }
else if (i->type() == IncidenceBase::TypeTodo) {
337 Todo::Ptr todo = mCalendar->todo(i->uid());
338 ret = (todo && mCalendar->deleteTodo(todo));
340 deleteTransaction(incidence);
346 if (existingIncidences.count() > 0 && inc->revision() > 0) {
350 "The event or task could not be removed from your calendar. "
351 "Maybe it has already been deleted or is not owned by you. "
352 "Or it might belong to a read-only or disabled calendar."));
354 deleteTransaction(incidence);
358 bool Scheduler::acceptDeclineCounter(
const IncidenceBase::Ptr &incidence,
362 deleteTransaction(incidence);
370 if (incidence->type() == IncidenceBase::TypeFreeBusy) {
371 return acceptFreeBusy(incidence, method);
374 Event::Ptr ev = mCalendar->event(incidence->uid());
375 Todo::Ptr to = mCalendar->todo(incidence->uid());
379 const Incidence::List list = mCalendar->incidences();
380 for (Incidence::List::ConstIterator it=list.constBegin(), end=list.constEnd();
382 if ((*it)->schedulingID() == incidence->uid()) {
392 kDebug() <<
"match found!";
397 attendeesEv = ev->attendees();
400 attendeesEv = to->attendees();
402 Attendee::List::ConstIterator inIt;
403 Attendee::List::ConstIterator evIt;
409 if (attIn->email().toLower() == attEv->email().toLower()) {
411 kDebug() <<
"update attendee";
412 attEv->setStatus(attIn->status());
413 attEv->setDelegate(attIn->delegate());
414 attEv->setDelegator(attIn->delegator());
419 if (!found && attIn->status() != Attendee::Declined) {
420 attendeesNew.
append(attIn);
424 bool attendeeAdded =
false;
425 for (Attendee::List::ConstIterator it = attendeesNew.
constBegin();
426 it != attendeesNew.
constEnd(); ++it) {
429 i18nc(
"@info",
"%1 wants to attend %2 but was not invited.",
431 (ev ? ev->summary() : to->summary()));
432 if (!attNew->delegator().isEmpty()) {
433 msg = i18nc(
"@info",
"%1 wants to attend %2 on behalf of %3.",
435 (ev ? ev->summary() : to->summary()), attNew->delegator());
437 if (KMessageBox::questionYesNo(
438 0, msg, i18nc(
"@title",
"Uninvited attendee"),
439 KGuiItem(i18nc(
"@option",
"Accept Attendance")),
440 KGuiItem(i18nc(
"@option",
"Reject Attendance"))) != KMessageBox::Yes) {
441 Incidence::Ptr cancel = incidence.dynamicCast<
Incidence>();
445 "The organizer rejected your attendance at this meeting."));
455 attNew->status(), attNew->role(), attNew->uid()));
457 a->setDelegate(attNew->delegate());
458 a->setDelegator(attNew->delegator());
465 attendeeAdded =
true;
470 bool sendMail =
false;
472 if (KMessageBox::questionYesNo(
475 "An attendee was added to the incidence. "
476 "Do you want to email the attendees an update message?"),
477 i18nc(
"@title",
"Attendee Added"),
478 KGuiItem(i18nc(
"@option",
"Send Messages")),
479 KGuiItem(i18nc(
"@option",
"Do Not Send"))) == KMessageBox::Yes) {
485 ev->setRevision(ev->revision() + 1);
491 to->setRevision(to->revision() + 1);
512 if (update && (to->percentComplete() != update->percentComplete())) {
513 to->setPercentComplete(update->percentComplete());
518 kError() <<
"No incidence for scheduling.";
522 deleteTransaction(incidence);
531 deleteTransaction(incidence);
538 deleteTransaction(incidence);
542 bool Scheduler::acceptFreeBusy(
const IncidenceBase::Ptr &incidence,
iTIPMethod method)
544 if (!d->mFreeBusyCache) {
545 kError() <<
"Scheduler: no FreeBusyCache.";
554 if (method == iTIPPublish) {
555 from = freebusy->organizer();
557 if ((method == iTIPReply) && (freebusy->attendeeCount() == 1)) {
559 from->setName(attendee->name());
560 from->setEmail(attendee->email());
563 if (!d->mFreeBusyCache->saveFreeBusy(freebusy, from)) {
567 deleteTransaction(incidence);
virtual QString freeBusyDir()=0
Returns the directory where the free-busy information is stored.
void append(const T &value)
const_iterator constEnd() const
virtual bool performTransaction(const KCalCore::IncidenceBase::Ptr &incidence, KCalCore::iTIPMethod method)=0
Performs iTIP transaction on incidence.
void setFreeBusyCache(KCalCore::FreeBusyCache *)
Sets the free/busy cache used to store free/busy information.
This file is part of the API for handling calendar data and provides static functions for formatting ...
KCalCore::FreeBusyCache * freeBusyCache() const
Returns the free/busy cache.
This class provides an encapsulation of iTIP transactions (RFC 2446).
bool acceptTransaction(const KCalCore::IncidenceBase::Ptr &incidence, KCalCore::iTIPMethod method, KCalCore::ScheduleMessage::Status status, const QString &email=QString())
Accepts the transaction.
const_iterator constBegin() const
QSharedPointer< X > dynamicCast() const
QSharedPointer< X > staticCast() const