File indexing completed on 2024-05-12 05:10:42

0001 /*
0002    SPDX-FileCopyrightText: 2011 Sérgio Martins <sergio.martins@kdab.com>
0003    SPDX-FileCopyrightText: 2012 Sérgio Martins <iamsergio@gmail.com>
0004 
0005    SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "calendarbase.h"
0009 #include "akonadicalendar_debug.h"
0010 #include "calendarbase_p.h"
0011 #include "calendarutils.h"
0012 #include "incidencechanger.h"
0013 #include <Akonadi/CollectionFetchJob>
0014 
0015 #include <KLocalizedString>
0016 #include <QTimeZone>
0017 
0018 using namespace Akonadi;
0019 using namespace KCalendarCore;
0020 
0021 static QString itemToString(const Akonadi::Item &item)
0022 {
0023     const KCalendarCore::Incidence::Ptr &incidence = CalendarUtils::incidence(item);
0024     QString str;
0025     QTextStream stream(&str);
0026     stream << item.id() << "; summary=" << incidence->summary() << "; uid=" << incidence->uid() << "; type=" << incidence->type()
0027            << "; recurs=" << incidence->recurs() << "; recurrenceId=" << incidence->recurrenceId().toString() << "; dtStart=" << incidence->dtStart().toString()
0028            << "; dtEnd=" << incidence->dateTime(Incidence::RoleEnd).toString() << "; parentCollection=" << item.storageCollectionId()
0029            << item.parentCollection().displayName();
0030 
0031     return str;
0032 }
0033 
0034 CalendarBasePrivate::CalendarBasePrivate(CalendarBase *qq)
0035     : QObject()
0036     , mIncidenceChanger(new IncidenceChanger())
0037     , q(qq)
0038 {
0039     connect(mIncidenceChanger, &IncidenceChanger::createFinished, this, &CalendarBasePrivate::slotCreateFinished);
0040 
0041     connect(mIncidenceChanger, &IncidenceChanger::deleteFinished, this, &CalendarBasePrivate::slotDeleteFinished);
0042 
0043     connect(mIncidenceChanger, &IncidenceChanger::modifyFinished, this, &CalendarBasePrivate::slotModifyFinished);
0044 
0045     mIncidenceChanger->setDestinationPolicy(IncidenceChanger::DestinationPolicyAsk);
0046     mIncidenceChanger->setGroupwareCommunication(false);
0047     mIncidenceChanger->setHistoryEnabled(false);
0048 }
0049 
0050 CalendarBasePrivate::~CalendarBasePrivate()
0051 {
0052     delete mIncidenceChanger;
0053 }
0054 
0055 void CalendarBasePrivate::internalInsert(const Akonadi::Item &item)
0056 {
0057     Q_ASSERT(item.isValid());
0058     Q_ASSERT(item.hasPayload<KCalendarCore::Incidence::Ptr>());
0059     KCalendarCore::Incidence::Ptr incidence = CalendarUtils::incidence(item);
0060 
0061     if (!incidence) {
0062         qCritical() << "Incidence is null. id=" << item.id() << "; hasPayload()=" << item.hasPayload()
0063                     << "; has incidence=" << item.hasPayload<KCalendarCore::Incidence::Ptr>() << "; mime type=" << item.mimeType();
0064         Q_ASSERT(false);
0065         return;
0066     }
0067 
0068     // qCDebug(AKONADICALENDAR_LOG) << "Inserting incidence in calendar. id=" << item.id() << "uid=" << incidence->uid();
0069     const QString uid = incidence->instanceIdentifier();
0070 
0071     if (uid.isEmpty()) {
0072         // This code path should never happen
0073         qCritical() << "Incidence has empty UID. id=" << item.id() << "; summary=" << incidence->summary() << "Please fix it. Ignoring this incidence.";
0074         return;
0075     }
0076 
0077     if (mItemIdByUid.contains(uid) && mItemIdByUid[uid] != item.id()) {
0078         // We only allow duplicate UIDs if they have the same item id, for example
0079         // when using virtual folders.
0080 #if 0
0081         qCWarning(AKONADICALENDAR_LOG) << "Discarding duplicate incidence with instanceIdentifier=" << uid
0082                                        << "and summary " << incidence->summary()
0083                                        << "; recurrenceId() =" << incidence->recurrenceId()
0084                                        << "; new id=" << item.id()
0085                                        << "; existing id=" << mItemIdByUid[uid];
0086 #endif
0087         return;
0088     }
0089 
0090     if (incidence->type() == KCalendarCore::Incidence::TypeEvent && !incidence->dtStart().isValid()) {
0091         // TODO: make the parser discard them would also be a good idea
0092         qCWarning(AKONADICALENDAR_LOG) << "Discarding event with invalid DTSTART. identifier=" << incidence->instanceIdentifier()
0093                                        << "; summary=" << incidence->summary();
0094         return;
0095     }
0096 
0097     Akonadi::Collection collection = item.parentCollection();
0098     if (collection.isValid()) {
0099         // Some items don't have collection set
0100         if (item.storageCollectionId() != collection.id() && item.storageCollectionId() > -1) {
0101             if (mCollections.contains(item.storageCollectionId())) {
0102                 collection = mCollections.value(item.storageCollectionId());
0103                 incidence->setReadOnly(!(collection.rights() & Akonadi::Collection::CanChangeItem));
0104             } else if (!mCollectionJobs.key(item.storageCollectionId())) {
0105                 collection = Akonadi::Collection(item.storageCollectionId());
0106                 auto job = new Akonadi::CollectionFetchJob(collection, Akonadi::CollectionFetchJob::Base, this);
0107                 QObject::connect(job, &KJob::result, this, &CalendarBasePrivate::collectionFetchResult);
0108                 mCollectionJobs.insert(job, collection.id());
0109             }
0110         } else {
0111             mCollections.insert(collection.id(), collection);
0112             incidence->setReadOnly(!(collection.rights() & Akonadi::Collection::CanChangeItem));
0113         }
0114     }
0115 
0116     mItemById.insert(item.id(), item);
0117     mItemIdByUid.insert(uid, item.id());
0118     mItemsByCollection.insert(item.storageCollectionId(), item);
0119 
0120     if (!incidence->hasRecurrenceId()) {
0121         // Insert parent relationships
0122         const QString parentUid = incidence->relatedTo();
0123         if (!parentUid.isEmpty()) {
0124             mParentUidToChildrenUid[parentUid].append(incidence->uid());
0125             mUidToParent.insert(uid, parentUid);
0126         }
0127     }
0128 
0129     incidence->setCustomProperty("VOLATILE", "AKONADI-ID", QString::number(item.id()));
0130     incidence->setCustomProperty("VOLATILE", "COLLECTION-ID", QString::number(item.storageCollectionId()));
0131     // Must be the last one due to re-entrancy
0132     const bool result = q->MemoryCalendar::addIncidence(incidence);
0133     if (!result) {
0134         qCritical() << "Error adding incidence " << itemToString(item);
0135         Q_ASSERT(false);
0136     }
0137 }
0138 
0139 void CalendarBasePrivate::collectionFetchResult(KJob *job)
0140 {
0141     Akonadi::Collection::Id colid = mCollectionJobs.take(job);
0142 
0143     if (job->error()) {
0144         qWarning() << "Error occurred: " << job->errorString();
0145         return;
0146     }
0147 
0148     auto fetchJob = qobject_cast<Akonadi::CollectionFetchJob *>(job);
0149 
0150     const Akonadi::Collection collection = fetchJob->collections().at(0);
0151     if (collection.id() != colid) {
0152         qCritical() << "Fetched the wrong collection,  should fetch: " << colid << "fetched: " << collection.id();
0153     }
0154 
0155     bool isReadOnly = !(collection.rights() & Akonadi::Collection::CanChangeItem);
0156     const auto lst = mItemsByCollection.values(collection.id());
0157     for (const Akonadi::Item &item : lst) {
0158         KCalendarCore::Incidence::Ptr incidence = CalendarUtils::incidence(item);
0159         incidence->setReadOnly(isReadOnly);
0160     }
0161 
0162     mCollections.insert(collection.id(), collection);
0163 
0164     if (mCollectionJobs.isEmpty()) {
0165         Q_EMIT fetchFinished();
0166     }
0167 }
0168 
0169 void CalendarBasePrivate::internalRemove(const Akonadi::Item &item)
0170 {
0171     Q_ASSERT(item.isValid());
0172 
0173     Incidence::Ptr tmp = CalendarUtils::incidence(item);
0174     if (!tmp) {
0175         qCritical() << "CalendarBase::internalRemove1: incidence is null, item.id=" << item.id();
0176         return;
0177     }
0178 
0179     // We want the one stored in the calendar
0180     Incidence::Ptr incidence = q->incidence(tmp->uid(), tmp->recurrenceId());
0181 
0182     // Null incidence means it was deleted via CalendarBase::deleteIncidence(), but then
0183     // the ETMCalendar received the monitor notification and tried to delete it again.
0184     if (incidence) {
0185         q->Calendar::notifyIncidenceAboutToBeDeleted(incidence);
0186 
0187         mItemById.remove(item.id());
0188         // qCDebug(AKONADICALENDAR_LOG) << "Deleting incidence from calendar .id=" << item.id() << "uid=" << incidence->uid();
0189         mItemIdByUid.remove(incidence->instanceIdentifier());
0190 
0191         mItemsByCollection.remove(item.storageCollectionId(), item);
0192 
0193         if (!incidence->hasRecurrenceId()) {
0194             const QString uid = incidence->uid();
0195             const QString parentUid = incidence->relatedTo();
0196             mParentUidToChildrenUid.remove(uid);
0197             if (!parentUid.isEmpty()) {
0198                 mParentUidToChildrenUid[parentUid].removeAll(uid);
0199                 mUidToParent.remove(uid);
0200             }
0201         }
0202 
0203         q->Calendar::setObserversEnabled(false);
0204         // Must be the last one due to re-entrancy
0205         const bool result = q->MemoryCalendar::deleteIncidence(incidence);
0206         q->Calendar::setObserversEnabled(true);
0207         q->Calendar::notifyIncidenceDeleted(incidence);
0208         if (!result) {
0209             qCritical() << "Error removing incidence " << itemToString(item);
0210             Q_ASSERT(false);
0211         }
0212     } else {
0213         qCWarning(AKONADICALENDAR_LOG) << "CalendarBase::internalRemove2: incidence is null, item.id=" << itemToString(item);
0214     }
0215 }
0216 
0217 void CalendarBasePrivate::slotDeleteFinished(int changeId,
0218                                              const QList<Akonadi::Item::Id> &itemIds,
0219                                              IncidenceChanger::ResultCode resultCode,
0220                                              const QString &errorMessage)
0221 {
0222     Q_UNUSED(changeId)
0223     if (resultCode == IncidenceChanger::ResultCodeSuccess) {
0224         for (const Akonadi::Item::Id &id : itemIds) {
0225             if (mItemById.contains(id)) {
0226                 internalRemove(mItemById.value(id));
0227             }
0228         }
0229     }
0230 
0231     Q_EMIT q->deleteFinished(resultCode == IncidenceChanger::ResultCodeSuccess, errorMessage);
0232 }
0233 
0234 void CalendarBasePrivate::slotCreateFinished(int changeId, const Akonadi::Item &item, IncidenceChanger::ResultCode resultCode, const QString &errorMessage)
0235 {
0236     Q_UNUSED(changeId)
0237     Q_UNUSED(item)
0238     if (resultCode == IncidenceChanger::ResultCodeSuccess && !mListensForNewItems) {
0239         Q_ASSERT(item.isValid());
0240         Q_ASSERT(item.hasPayload<KCalendarCore::Incidence::Ptr>());
0241         internalInsert(item);
0242     }
0243 
0244     mLastCreationCancelled = (resultCode == IncidenceChanger::ResultCodeUserCanceled);
0245 
0246     Q_EMIT q->createFinished(resultCode == IncidenceChanger::ResultCodeSuccess, errorMessage);
0247 }
0248 
0249 void CalendarBasePrivate::slotModifyFinished(int changeId, const Akonadi::Item &item, IncidenceChanger::ResultCode resultCode, const QString &errorMessage)
0250 {
0251     Q_UNUSED(changeId)
0252     Q_UNUSED(item)
0253     QString message = errorMessage;
0254     if (resultCode == IncidenceChanger::ResultCodeSuccess) {
0255         KCalendarCore::Incidence::Ptr incidence = CalendarUtils::incidence(item);
0256         Q_ASSERT(incidence);
0257         KCalendarCore::Incidence::Ptr localIncidence = q->incidence(incidence->instanceIdentifier());
0258 
0259         if (localIncidence) {
0260             // update our local one
0261             *(static_cast<KCalendarCore::IncidenceBase *>(localIncidence.data())) = *(incidence.data());
0262         } else {
0263             // This shouldn't happen, unless the incidence gets deleted between event loops
0264             qCWarning(AKONADICALENDAR_LOG) << "CalendarBasePrivate::slotModifyFinished() Incidence was deleted already probably? id=" << item.id();
0265             message = i18n("Could not find incidence to update, it probably was deleted recently.");
0266             resultCode = IncidenceChanger::ResultCodeAlreadyDeleted;
0267         }
0268     }
0269     Q_EMIT q->modifyFinished(resultCode == IncidenceChanger::ResultCodeSuccess, message);
0270 }
0271 
0272 void CalendarBasePrivate::handleUidChange(const Akonadi::Item &oldItem, const Akonadi::Item &newItem, const QString &newIdentifier)
0273 {
0274     Q_ASSERT(oldItem.isValid());
0275     Incidence::Ptr newIncidence = CalendarUtils::incidence(newItem);
0276     Q_ASSERT(newIncidence);
0277     Incidence::Ptr oldIncidence = CalendarUtils::incidence(oldItem);
0278     Q_ASSERT(oldIncidence);
0279 
0280     const QString newUid = newIncidence->uid();
0281     if (mItemIdByUid.contains(newIdentifier)) {
0282         Incidence::Ptr oldIncidence = CalendarUtils::incidence(oldItem);
0283 #if 0
0284         qCWarning(AKONADICALENDAR_LOG) << "New uid shouldn't be known: "  << newIdentifier << "; id="
0285                                        << newItem.id() << "; oldItem.id=" << mItemIdByUid[newIdentifier]
0286                                        << "; new summary= " << newIncidence->summary()
0287                                        << "; new recurrenceId=" << newIncidence->recurrenceId()
0288                                        << "; oldIncidence" << oldIncidence;
0289 #endif
0290         if (oldIncidence) {
0291 #if 0
0292             qCWarning(AKONADICALENDAR_LOG) << "; oldIncidence uid=" << oldIncidence->uid()
0293                                            << "; oldIncidence recurrenceId = " << oldIncidence->recurrenceId()
0294                                            << "; oldIncidence summary = " << oldIncidence->summary();
0295 #endif
0296         }
0297         Q_ASSERT(false);
0298         return;
0299     }
0300 
0301     mItemIdByUid[newIdentifier] = newItem.id();
0302 
0303     // Get the real pointer
0304     oldIncidence = q->MemoryCalendar::incidence(oldIncidence->uid());
0305 
0306     if (!oldIncidence) {
0307         // How can this happen ?
0308         qCWarning(AKONADICALENDAR_LOG) << "Couldn't find old incidence";
0309         Q_ASSERT(false);
0310         return;
0311     }
0312 
0313     if (newIncidence->instanceIdentifier() == oldIncidence->instanceIdentifier()) {
0314 #if 0
0315         qCWarning(AKONADICALENDAR_LOG) << "New uid=" << newIncidence->uid() << "; old uid=" << oldIncidence->uid()
0316                                        << "; new recurrenceId="
0317                                        << newIncidence->recurrenceId()
0318                                        << "; old recurrenceId=" << oldIncidence->recurrenceId()
0319                                        << "; new summary = " << newIncidence->summary()
0320                                        << "; old summary = " << oldIncidence->summary()
0321                                        << "; id = " << newItem.id();
0322 #endif
0323         Q_ASSERT(false); // The reason we're here in the first place
0324         return;
0325     }
0326 
0327     mItemIdByUid.remove(oldIncidence->instanceIdentifier());
0328     const QString oldUid = oldIncidence->uid();
0329 
0330     if (mParentUidToChildrenUid.contains(oldUid)) {
0331         Q_ASSERT(!mParentUidToChildrenUid.contains(newIdentifier));
0332         QStringList children = mParentUidToChildrenUid.value(oldUid);
0333         mParentUidToChildrenUid.insert(newIdentifier, children);
0334         mParentUidToChildrenUid.remove(oldUid);
0335     }
0336 
0337     // Update internal maps of the base class, MemoryCalendar
0338     q->setObserversEnabled(false);
0339     q->MemoryCalendar::deleteIncidence(oldIncidence);
0340     q->MemoryCalendar::addIncidence(newIncidence);
0341 
0342     newIncidence->setUid(oldUid); // We set and unset just to notify observers of a change.
0343     q->setObserversEnabled(true);
0344     newIncidence->setUid(newUid);
0345 }
0346 
0347 void CalendarBasePrivate::handleParentChanged(const KCalendarCore::Incidence::Ptr &newIncidence)
0348 {
0349     Q_ASSERT(newIncidence);
0350 
0351     if (newIncidence->hasRecurrenceId()) { // These ones don't/shouldn't have a parent
0352         return;
0353     }
0354 
0355     const QString originalParentUid = mUidToParent.value(newIncidence->uid());
0356     const QString newParentUid = newIncidence->relatedTo();
0357 
0358     if (originalParentUid == newParentUid) {
0359         return; // nothing changed
0360     }
0361 
0362     if (!originalParentUid.isEmpty()) {
0363         // Remove this child from it's old parent:
0364         Q_ASSERT(mParentUidToChildrenUid.contains(originalParentUid));
0365         mParentUidToChildrenUid[originalParentUid].removeAll(newIncidence->uid());
0366     }
0367 
0368     mUidToParent.remove(newIncidence->uid());
0369 
0370     if (!newParentUid.isEmpty()) {
0371         // Deliver this child to it's new parent:
0372         Q_ASSERT(!mParentUidToChildrenUid[newParentUid].contains(newIncidence->uid()));
0373         mParentUidToChildrenUid[newParentUid].append(newIncidence->uid());
0374         mUidToParent.insert(newIncidence->uid(), newParentUid);
0375     }
0376 }
0377 
0378 CalendarBase::CalendarBase(QObject *parent)
0379     : MemoryCalendar(QTimeZone::systemTimeZone())
0380     , d_ptr(new CalendarBasePrivate(this))
0381 {
0382     setParent(parent);
0383 }
0384 
0385 CalendarBase::CalendarBase(CalendarBasePrivate *const dd, QObject *parent)
0386     : MemoryCalendar(QTimeZone::systemTimeZone())
0387     , d_ptr(dd)
0388 {
0389     setParent(parent);
0390 }
0391 
0392 CalendarBase::~CalendarBase() = default;
0393 
0394 Akonadi::Item CalendarBase::item(Akonadi::Item::Id id) const
0395 {
0396     Q_D(const CalendarBase);
0397     Akonadi::Item i;
0398     auto it = d->mItemById.constFind(id);
0399     if (it != d->mItemById.cend()) {
0400         i = *it;
0401     } else {
0402         qCDebug(AKONADICALENDAR_LOG) << "Can't find any item with id " << id;
0403     }
0404     return i;
0405 }
0406 
0407 Akonadi::Item CalendarBase::item(const QString &uid) const
0408 {
0409     Q_D(const CalendarBase);
0410     Akonadi::Item i;
0411 
0412     if (uid.isEmpty()) {
0413         return i;
0414     }
0415 
0416     auto it = d->mItemIdByUid.constFind(uid);
0417     if (it != d->mItemIdByUid.cend()) {
0418         const Akonadi::Item::Id id = *it;
0419         auto it2 = d->mItemById.constFind(id);
0420         if (it2 == d->mItemById.cend()) {
0421             qCritical() << "Item with id " << id << "(uid=" << uid << ") not found, but in uid map";
0422             Q_ASSERT_X(false, "CalendarBase::item", "not in mItemById");
0423         } else {
0424             i = *it2;
0425         }
0426     } else {
0427         qCDebug(AKONADICALENDAR_LOG) << "Can't find any incidence with uid " << uid;
0428     }
0429     return i;
0430 }
0431 
0432 Item CalendarBase::item(const Incidence::Ptr &incidence) const
0433 {
0434     return incidence ? item(incidence->instanceIdentifier()) : Item();
0435 }
0436 
0437 Akonadi::Item::List CalendarBase::items(Akonadi::Collection::Id id) const
0438 {
0439     Q_D(const CalendarBase);
0440 
0441     Akonadi::Item::List result;
0442     if (id == -1) {
0443         result.reserve(d->mItemsByCollection.size());
0444     }
0445 
0446     auto it = id == -1 ? d->mItemsByCollection.cbegin() : d->mItemsByCollection.constFind(id);
0447     while (it != d->mItemsByCollection.cend() && (id == -1 || it.key() == id)) {
0448         result.push_back(*it);
0449         ++it;
0450     }
0451 
0452     return result;
0453 }
0454 
0455 Akonadi::Item::List CalendarBase::itemList(const KCalendarCore::Incidence::List &incidences) const
0456 {
0457     Akonadi::Item::List items;
0458     items.reserve(incidences.size());
0459 
0460     for (const KCalendarCore::Incidence::Ptr &incidence : incidences) {
0461         if (incidence) {
0462             items << item(incidence->instanceIdentifier());
0463         } else {
0464             items << Akonadi::Item();
0465         }
0466     }
0467 
0468     return items;
0469 }
0470 
0471 KCalendarCore::Incidence::List CalendarBase::childIncidences(Akonadi::Item::Id parentId) const
0472 {
0473     Q_D(const CalendarBase);
0474     KCalendarCore::Incidence::List children;
0475 
0476     if (d->mItemById.contains(parentId)) {
0477         const Akonadi::Item item = d->mItemById.value(parentId);
0478         Q_ASSERT(item.isValid());
0479         KCalendarCore::Incidence::Ptr parent = CalendarUtils::incidence(item);
0480 
0481         if (parent) {
0482             children = childIncidences(parent->uid());
0483         } else {
0484             Q_ASSERT(false);
0485         }
0486     }
0487 
0488     return children;
0489 }
0490 
0491 KCalendarCore::Incidence::List CalendarBase::childIncidences(const QString &parentUid) const
0492 {
0493     Q_D(const CalendarBase);
0494     KCalendarCore::Incidence::List children;
0495     const QStringList uids = d->mParentUidToChildrenUid.value(parentUid);
0496     for (const QString &uid : uids) {
0497         Incidence::Ptr child = incidence(uid);
0498         if (child) {
0499             children.append(child);
0500         } else {
0501             qCWarning(AKONADICALENDAR_LOG) << "Invalid child with uid " << uid;
0502         }
0503     }
0504     return children;
0505 }
0506 
0507 Akonadi::Item::List CalendarBase::childItems(Akonadi::Item::Id parentId) const
0508 {
0509     Q_D(const CalendarBase);
0510     Akonadi::Item::List children;
0511 
0512     if (d->mItemById.contains(parentId)) {
0513         const Akonadi::Item item = d->mItemById.value(parentId);
0514         Q_ASSERT(item.isValid());
0515         KCalendarCore::Incidence::Ptr parent = CalendarUtils::incidence(item);
0516 
0517         if (parent) {
0518             children = childItems(parent->uid());
0519         } else {
0520             Q_ASSERT(false);
0521         }
0522     }
0523 
0524     return children;
0525 }
0526 
0527 Akonadi::Item::List CalendarBase::childItems(const QString &parentUid) const
0528 {
0529     Q_D(const CalendarBase);
0530     Akonadi::Item::List children;
0531     const QStringList uids = d->mParentUidToChildrenUid.value(parentUid);
0532     for (const QString &uid : uids) {
0533         Akonadi::Item child = item(uid);
0534         if (child.isValid() && child.hasPayload<KCalendarCore::Incidence::Ptr>()) {
0535             children.append(child);
0536         } else {
0537             qCWarning(AKONADICALENDAR_LOG) << "Invalid child with uid " << uid;
0538         }
0539     }
0540     return children;
0541 }
0542 
0543 bool CalendarBase::addEvent(const KCalendarCore::Event::Ptr &event)
0544 {
0545     return addIncidence(event);
0546 }
0547 
0548 bool CalendarBase::deleteEvent(const KCalendarCore::Event::Ptr &event)
0549 {
0550     return deleteIncidence(event);
0551 }
0552 
0553 bool CalendarBase::addTodo(const KCalendarCore::Todo::Ptr &todo)
0554 {
0555     return addIncidence(todo);
0556 }
0557 
0558 bool CalendarBase::deleteTodo(const KCalendarCore::Todo::Ptr &todo)
0559 {
0560     return deleteIncidence(todo);
0561 }
0562 
0563 bool CalendarBase::addJournal(const KCalendarCore::Journal::Ptr &journal)
0564 {
0565     return addIncidence(journal);
0566 }
0567 
0568 bool CalendarBase::deleteJournal(const KCalendarCore::Journal::Ptr &journal)
0569 {
0570     return deleteIncidence(journal);
0571 }
0572 
0573 bool CalendarBase::addIncidence(const KCalendarCore::Incidence::Ptr &incidence)
0574 {
0575     // TODO: Parent for dialogs
0576     Q_D(CalendarBase);
0577 
0578     // User canceled on the collection selection dialog
0579     if (batchAdding() && d->mBatchInsertionCancelled) {
0580         return false;
0581     }
0582 
0583     d->mLastCreationCancelled = false;
0584 
0585     Akonadi::Collection collection;
0586 
0587     if (batchAdding() && d->mCollectionForBatchInsertion.isValid()) {
0588         collection = d->mCollectionForBatchInsertion;
0589     }
0590 
0591     if (incidence->hasRecurrenceId() && !collection.isValid()) {
0592         // We are creating an exception, reuse the same collection that the main incidence uses
0593         Item mainItem = item(incidence->uid());
0594         if (mainItem.isValid()) {
0595             collection = Collection(mainItem.storageCollectionId());
0596         }
0597     }
0598 
0599     const int changeId = d->mIncidenceChanger->createIncidence(incidence, collection);
0600 
0601     if (batchAdding()) {
0602         const Akonadi::Collection lastCollection = d->mIncidenceChanger->lastCollectionUsed();
0603         if (changeId != -1 && !lastCollection.isValid()) {
0604             d->mBatchInsertionCancelled = true;
0605         } else if (lastCollection.isValid() && !d->mCollectionForBatchInsertion.isValid()) {
0606             d->mCollectionForBatchInsertion = d->mIncidenceChanger->lastCollectionUsed();
0607         }
0608     }
0609 
0610     return changeId != -1;
0611 }
0612 
0613 bool CalendarBase::deleteIncidence(const KCalendarCore::Incidence::Ptr &incidence)
0614 {
0615     Q_D(CalendarBase);
0616     Q_ASSERT(incidence);
0617     if (!incidence->hasRecurrenceId() && incidence->recurs()) {
0618         deleteIncidenceInstances(incidence);
0619     }
0620     Akonadi::Item item_ = item(incidence->instanceIdentifier());
0621     return -1 != d->mIncidenceChanger->deleteIncidence(item_);
0622 }
0623 
0624 bool CalendarBase::modifyIncidence(const KCalendarCore::Incidence::Ptr &newIncidence)
0625 {
0626     Q_D(CalendarBase);
0627     Q_ASSERT(newIncidence);
0628     Akonadi::Item item_ = item(newIncidence->instanceIdentifier());
0629     item_.setPayload<KCalendarCore::Incidence::Ptr>(newIncidence);
0630     return -1 != d->mIncidenceChanger->modifyIncidence(item_);
0631 }
0632 
0633 IncidenceChanger *CalendarBase::incidenceChanger() const
0634 {
0635     Q_D(const CalendarBase);
0636     return d->mIncidenceChanger;
0637 }
0638 
0639 void CalendarBase::startBatchAdding()
0640 {
0641     KCalendarCore::MemoryCalendar::startBatchAdding();
0642 }
0643 
0644 void CalendarBase::endBatchAdding()
0645 {
0646     Q_D(CalendarBase);
0647     d->mCollectionForBatchInsertion = Akonadi::Collection();
0648     d->mBatchInsertionCancelled = false;
0649     KCalendarCore::MemoryCalendar::endBatchAdding();
0650 }
0651 
0652 #include "moc_calendarbase.cpp"
0653 #include "moc_calendarbase_p.cpp"