diff --git a/src/akonadi/akonadimessaging.cpp b/src/akonadi/akonadimessaging.cpp index ee9d767f..98936cf8 100644 --- a/src/akonadi/akonadimessaging.cpp +++ b/src/akonadi/akonadimessaging.cpp @@ -1,55 +1,81 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akonadimessaging.h" #include #include using namespace Akonadi; Messaging::Messaging() : m_itip(new ITIPHandler) { m_itip->setShowDialogsOnError(true); } Messaging::~Messaging() { delete m_itip; } void Messaging::sendDelegationMessage(Item item) { auto todo = item.payload(); Q_ASSERT(todo); QWidget *window = 0; if (!QApplication::topLevelWidgets().isEmpty()) { window = QApplication::topLevelWidgets().first(); } m_itip->sendiTIPMessage(KCalCore::iTIPRequest, todo, window); } + +void Messaging::sendDeleteMessageToOrganizer(Akonadi::Item item) +{ + auto todo = item.payload(); + Q_ASSERT(todo); + + QWidget *window = 0; + if (!QApplication::topLevelWidgets().isEmpty()) { + window = QApplication::topLevelWidgets().first(); + } + todo->attendees().first()->setStatus(KCalCore::Attendee::Declined); + m_itip->sendiTIPMessage(KCalCore::iTIPReply, todo, window); +} + +void Messaging::sendDeleteMessageToDelegatee(Akonadi::Item item) +{ + auto todo = item.payload(); + Q_ASSERT(todo); + + QWidget *window = 0; + if (!QApplication::topLevelWidgets().isEmpty()) { + window = QApplication::topLevelWidgets().first(); + } + + m_itip->sendiTIPMessage(KCalCore::iTIPCancel, todo, window); +} \ No newline at end of file diff --git a/src/akonadi/akonadimessaging.h b/src/akonadi/akonadimessaging.h index eb3fba13..315253f3 100644 --- a/src/akonadi/akonadimessaging.h +++ b/src/akonadi/akonadimessaging.h @@ -1,47 +1,49 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_MESSAGING_H #define AKONADI_MESSAGING_H #include "akonadi/akonadimessaginginterface.h" namespace Akonadi { class ITIPHandler; class Messaging : public MessagingInterface { public: Messaging(); ~Messaging(); void sendDelegationMessage(Akonadi::Item item) Q_DECL_OVERRIDE; + void sendDeleteMessageToOrganizer(Akonadi::Item item) Q_DECL_OVERRIDE; + void sendDeleteMessageToDelegatee(Akonadi::Item item) Q_DECL_OVERRIDE; private: ITIPHandler *m_itip; }; } #endif // AKONADI_MESSAGING_H diff --git a/src/akonadi/akonadimessaginginterface.h b/src/akonadi/akonadimessaginginterface.h index 4ec7ef15..820a2022 100644 --- a/src/akonadi/akonadimessaginginterface.h +++ b/src/akonadi/akonadimessaginginterface.h @@ -1,42 +1,44 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_MESSAGINGINTERFACE_H #define AKONADI_MESSAGINGINTERFACE_H #include namespace Akonadi { class MessagingInterface { public: MessagingInterface(); virtual ~MessagingInterface(); virtual void sendDelegationMessage(Akonadi::Item item) = 0; + virtual void sendDeleteMessageToOrganizer(Akonadi::Item item) = 0; + virtual void sendDeleteMessageToDelegatee(Akonadi::Item item) = 0; }; } #endif // AKONADI_MESSAGINGINTERFACE_H diff --git a/src/akonadi/akonaditaskrepository.cpp b/src/akonadi/akonaditaskrepository.cpp index 310b94af..093f7f2b 100644 --- a/src/akonadi/akonaditaskrepository.cpp +++ b/src/akonadi/akonaditaskrepository.cpp @@ -1,344 +1,354 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akonaditaskrepository.h" #include #include #include #include #include "akonadicollectionfetchjobinterface.h" #include "akonadiitemfetchjobinterface.h" #include "akonadimessaging.h" #include "akonadiserializer.h" #include "akonadistorage.h" #include "akonadistoragesettings.h" #include "utils/compositejob.h" using namespace Akonadi; using namespace Utils; TaskRepository::TaskRepository(QObject *parent) : QObject(parent), m_storage(new Storage), m_serializer(new Serializer), m_messaging(new Messaging), m_ownInterfaces(true) { } TaskRepository::TaskRepository(StorageInterface *storage, SerializerInterface *serializer, MessagingInterface *messaging) : m_storage(storage), m_serializer(serializer), m_messaging(messaging), m_ownInterfaces(false) { } TaskRepository::~TaskRepository() { if (m_ownInterfaces) { delete m_storage; delete m_serializer; delete m_messaging; } } bool TaskRepository::isDefaultSource(Domain::DataSource::Ptr source) const { auto settingsCollection = StorageSettings::instance().defaultTaskCollection(); auto sourceCollection = m_serializer->createCollectionFromDataSource(source); return settingsCollection == sourceCollection; } void TaskRepository::setDefaultSource(Domain::DataSource::Ptr source) { auto collection = m_serializer->createCollectionFromDataSource(source); StorageSettings::instance().setDefaultTaskCollection(collection); } KJob *TaskRepository::createItem(const Item &item) { const Akonadi::Collection defaultCollection = m_storage->defaultTaskCollection(); if (defaultCollection.isValid()) { return m_storage->createItem(item, defaultCollection); } else { auto job = new CompositeJob(); CollectionFetchJobInterface *fetchCollectionJob = m_storage->fetchCollections(Akonadi::Collection::root(), StorageInterface::Recursive, StorageInterface::Tasks); job->install(fetchCollectionJob->kjob(), [fetchCollectionJob, item, job, this] { if (fetchCollectionJob->kjob()->error() != KJob::NoError) return; Q_ASSERT(fetchCollectionJob->collections().size() > 0); const Akonadi::Collection::List collections = fetchCollectionJob->collections(); Akonadi::Collection col = *std::find_if(collections.constBegin(), collections.constEnd(), [] (const Akonadi::Collection &c) { return c.rights() == Akonadi::Collection::AllRights; }); Q_ASSERT(col.isValid()); auto createJob = m_storage->createItem(item, col); job->addSubjob(createJob); createJob->start(); }); return job; } } KJob *TaskRepository::create(Domain::Task::Ptr task) { auto item = m_serializer->createItemFromTask(task); Q_ASSERT(!item.isValid()); return createItem(item); } KJob *TaskRepository::createInProject(Domain::Task::Ptr task, Domain::Project::Ptr project) { Item taskItem = m_serializer->createItemFromTask(task); Q_ASSERT(!taskItem.isValid()); Item projectItem = m_serializer->createItemFromProject(project); Q_ASSERT(projectItem.isValid()); Q_ASSERT(projectItem.parentCollection().isValid()); m_serializer->updateItemProject(taskItem, project); return m_storage->createItem(taskItem, projectItem.parentCollection()); } KJob *TaskRepository::createInContext(Domain::Task::Ptr task, Domain::Context::Ptr context) { Item item = m_serializer->createItemFromTask(task); Q_ASSERT(!item.isValid()); Tag tag = m_serializer->createTagFromContext(context); Q_ASSERT(tag .isValid()); item.setTag(tag); return createItem(item); } KJob *TaskRepository::createInTag(Domain::Task::Ptr task, Domain::Tag::Ptr tag) { Item item = m_serializer->createItemFromTask(task); Q_ASSERT(!item.isValid()); Tag akonadiTag = m_serializer->createAkonadiTagFromTag(tag); Q_ASSERT(akonadiTag .isValid()); item.setTag(akonadiTag); return createItem(item); } KJob *TaskRepository::update(Domain::Task::Ptr task) { if (task->recurrence() && task->status() == Domain::Task::Complete) { /* one ocurrence is completed * * copy current task with: * * status = completed * * recurrence deleted * * parentTask = recurrence Task * * the recurrence task: * * set next start/due date */ Domain::Task::Ptr ocurrence(new Domain::Task); *ocurrence = *task; ocurrence->setRecurrence(Domain::Recurrence::Ptr()); ocurrence->setStatus(Domain::Task::Complete); ocurrence->setProperty("relatedUid", task->property("todoUid")); if (!task->startDate().isValid()) { qWarning() << "A recurring todo must always have a valid start date"; task->setStartDate(task->dueDate().isValid()?task->dueDate():QDateTime::currentDateTime()); } task->setStatus(Domain::Task::None); auto tempItem = m_serializer->createItemFromTask(task); Q_ASSERT(tempItem.isValid()); const KCalCore::Todo::Ptr todo = tempItem.payload(); QDateTime nextStartDate = todo->recurrence()->getNextDateTime(KDateTime(task->startDate())).dateTime(); if (nextStartDate.isValid()) { if (task->dueDate().isValid()) { QDateTime nextDueDate = nextStartDate.addDays(task->startDate().daysTo(task->dueDate())); task->setDueDate(nextDueDate); } task->setStartDate(nextStartDate); } else { task->setStatus(Domain::Task::Complete); } if (task->recurrence()->count() > 1) { task->recurrence()->setCount(task->recurrence()->count()-1); } else if (task->recurrence()->count() == 1) { task->setStatus(Domain::Task::Complete); } auto item = m_serializer->createItemFromTask(task); Q_ASSERT(item.isValid()); auto itemOcurrence = m_serializer->createItemFromTask(ocurrence); Q_ASSERT(!itemOcurrence.isValid()); auto collection = Akonadi::Collection(task->property("parentCollectionId").value()); auto job = new CompositeJob(); job->install(m_storage->createItem(itemOcurrence, collection),[] {}); job->install(m_storage->updateItem(item), [] {}); return job; } else { auto item = m_serializer->createItemFromTask(task); Q_ASSERT(item.isValid()); return m_storage->updateItem(item); } } KJob *TaskRepository::remove(Domain::Task::Ptr task) { + auto delegate = task->delegate(); auto item = m_serializer->createItemFromTask(task); Q_ASSERT(item.isValid()); + if (delegate.isValid()) { + KPIMIdentities::IdentityManager identityManager; + if (identityManager.thatIsMe(delegate.email())) { + m_messaging->sendDeleteMessageToOrganizer(item); + } else { + m_messaging->sendDeleteMessageToDelegatee(item); + } + } + auto compositeJob = new CompositeJob(); ItemFetchJobInterface *fetchItemJob = m_storage->fetchItem(item); compositeJob->install(fetchItemJob->kjob(), [fetchItemJob, compositeJob, this] { if (fetchItemJob->kjob()->error() != KJob::NoError) return; Q_ASSERT(fetchItemJob->items().size() == 1); auto item = fetchItemJob->items().first(); ItemFetchJobInterface *fetchCollectionItemsJob = m_storage->fetchItems(item.parentCollection()); compositeJob->install(fetchCollectionItemsJob->kjob(), [fetchCollectionItemsJob, item, compositeJob, this] { if (fetchCollectionItemsJob->kjob()->error() != KJob::NoError) return; Item::List childItems = m_serializer->filterDescendantItems(fetchCollectionItemsJob->items(), item); childItems << item; auto removeJob = m_storage->removeItems(childItems); compositeJob->addSubjob(removeJob); removeJob->start(); }); }); return compositeJob; } KJob *TaskRepository::associate(Domain::Task::Ptr parent, Domain::Task::Ptr child) { auto childItem = m_serializer->createItemFromTask(child); auto job = new CompositeJob(); ItemFetchJobInterface *fetchItemJob = m_storage->fetchItem(childItem); job->install(fetchItemJob->kjob(), [fetchItemJob, parent, job, this] { if (fetchItemJob->kjob()->error() != KJob::NoError) return; Q_ASSERT(fetchItemJob->items().size() == 1); auto childItem = fetchItemJob->items().first(); m_serializer->updateItemParent(childItem, parent); // Check collections to know if we need to move child auto parentItem = m_serializer->createItemFromTask(parent); ItemFetchJobInterface *fetchParentItemJob = m_storage->fetchItem(parentItem); job->install(fetchParentItemJob->kjob(), [fetchParentItemJob, childItem, job, this] { if (fetchParentItemJob->kjob()->error() != KJob::NoError) return; Q_ASSERT(fetchParentItemJob->items().size() == 1); auto parentItem = fetchParentItemJob->items().first(); const int itemCollectionId = childItem.parentCollection().id(); const int parentCollectionId = parentItem.parentCollection().id(); if (itemCollectionId != parentCollectionId) { ItemFetchJobInterface *fetchChildrenItemJob = m_storage->fetchItems(childItem.parentCollection()); job->install(fetchChildrenItemJob->kjob(), [fetchChildrenItemJob, childItem, parentItem, job, this] { if (fetchChildrenItemJob->kjob()->error() != KJob::NoError) return; Item::List childItems = m_serializer->filterDescendantItems(fetchChildrenItemJob->items(), childItem); auto transaction = m_storage->createTransaction(); m_storage->updateItem(childItem, transaction); childItems.push_front(childItem); m_storage->moveItems(childItems, parentItem.parentCollection(), transaction); job->addSubjob(transaction); transaction->start(); }); } else { auto updateJob = m_storage->updateItem(childItem); job->addSubjob(updateJob); updateJob->start(); } }); }); return job; } KJob *TaskRepository::dissociate(Domain::Task::Ptr child) { auto job = new CompositeJob(); auto childItem = m_serializer->createItemFromTask(child); ItemFetchJobInterface *fetchItemJob = m_storage->fetchItem(childItem); job->install(fetchItemJob->kjob(), [fetchItemJob, job, this] { if (fetchItemJob->kjob()->error() != KJob::NoError) return; Q_ASSERT(fetchItemJob->items().size() == 1); auto childItem = fetchItemJob->items().first(); m_serializer->removeItemParent(childItem); auto updateJob = m_storage->updateItem(childItem); job->addSubjob(updateJob); updateJob->start(); }); return job; } KJob *TaskRepository::delegate(Domain::Task::Ptr task, Domain::Task::Delegate delegate) { auto originalDelegate = task->delegate(); task->blockSignals(true); task->setDelegate(delegate); if (task->property("organizer").toString().isEmpty()) { KPIMIdentities::IdentityManager identityManager; task->setProperty("organizer", identityManager.defaultIdentity().fullEmailAddr()); } auto item = m_serializer->createItemFromTask(task); task->setDelegate(originalDelegate); task->blockSignals(false); m_messaging->sendDelegationMessage(item); return 0; }