diff --git a/src/akonadi/akonaditaskrepository.cpp b/src/akonadi/akonaditaskrepository.cpp index 964026eb..dfd1a079 100644 --- a/src/akonadi/akonaditaskrepository.cpp +++ b/src/akonadi/akonaditaskrepository.cpp @@ -1,392 +1,396 @@ /* 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) { const auto delegate = task->delegate(); bool sendMail = true; if (task->property("sendMail").isValid()) { sendMail = task->property("sendMail").toBool(); qDebug() << "sendMail:" << sendMail; task->setProperty("sendMail", true); } 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); item.payload()->setLastModified(KDateTime::currentUtcDateTime()); Q_ASSERT(item.isValid()); auto itemOcurrence = m_serializer->createItemFromTask(ocurrence); itemOcurrence.payload()->setLastModified(KDateTime::currentUtcDateTime()); Q_ASSERT(!itemOcurrence.isValid()); auto collection = Akonadi::Collection(task->property("parentCollectionId").value()); if (sendMail && delegate.isValid()) { item.payload()->setRevision(item.payload()->revision() + 1); KPIMIdentities::IdentityManager identityManager; if (identityManager.thatIsMe(delegate.email())) { m_messaging->sendUpdateMessageToOrganizer(item); //TODO: what must be sent to organizier if one occurene is finished? } else { m_messaging->sendUpdateMessageToDelegatee(item); m_messaging->sendDelegationMessage(itemOcurrence); } } 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()); item.payload()->setLastModified(KDateTime::currentUtcDateTime()); qDebug() << "SEQUENCE: " << item.payload()->revision(); if (sendMail && delegate.isValid()) { item.payload()->setRevision(item.payload()->revision() + 1); KPIMIdentities::IdentityManager identityManager; if (identityManager.thatIsMe(delegate.email())) { m_messaging->sendUpdateMessageToOrganizer(item); } else { m_messaging->sendUpdateMessageToDelegatee(item); } } qDebug() << "SEQUENCE: " << item.payload()->revision(); 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) { + if (parent == child) { + qWarning() << "Can't associate task with itself"; + return new CompositeJob; + } 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; }