File indexing completed on 2025-03-09 04:56:45

0001 /*
0002  * SPDX-FileCopyrightText: 2014 Kevin Ottens <ervin@kde.org>
0003  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004  */
0005 
0006 
0007 #include "akonaditaskrepository.h"
0008 
0009 #include <KLocalizedString>
0010 
0011 
0012 #include "akonadicollectionfetchjobinterface.h"
0013 #include "akonadiitemfetchjobinterface.h"
0014 
0015 #include "utils/compositejob.h"
0016 
0017 using namespace Akonadi;
0018 using namespace Utils;
0019 
0020 TaskRepository::TaskRepository(const StorageInterface::Ptr &storage,
0021                                const SerializerInterface::Ptr &serializer)
0022     : m_storage(storage),
0023       m_serializer(serializer)
0024 {
0025 }
0026 
0027 KJob *TaskRepository::createItem(const Item &item)
0028 {
0029     const Akonadi::Collection defaultCollection = m_storage->defaultCollection();
0030     if (defaultCollection.isValid()) {
0031         return m_storage->createItem(item, defaultCollection);
0032     } else {
0033         auto job = new CompositeJob();
0034         CollectionFetchJobInterface *fetchCollectionJob = m_storage->fetchCollections(Akonadi::Collection::root(),
0035                                                                                       StorageInterface::Recursive,
0036                                                                                       this);
0037         job->install(fetchCollectionJob->kjob(), [fetchCollectionJob, item, job, this] {
0038             if (fetchCollectionJob->kjob()->error() != KJob::NoError)
0039                 return;
0040 
0041             Q_ASSERT(fetchCollectionJob->collections().size() > 0);
0042             const Akonadi::Collection::List collections = fetchCollectionJob->collections();
0043             auto it = std::find_if(collections.constBegin(), collections.constEnd(),
0044                                    [] (const Akonadi::Collection &c) {
0045                 return (c.rights() & Akonadi::Collection::CanCreateItem)
0046                     && (c.rights() & Akonadi::Collection::CanChangeItem)
0047                     && (c.rights() & Akonadi::Collection::CanDeleteItem);
0048             });
0049             if (it == collections.constEnd()) {
0050                 job->emitError(i18n("Could not find a collection to store the task into!"));
0051             } else {
0052                 auto col = *it;
0053                 Q_ASSERT(col.isValid());
0054                 auto createJob = m_storage->createItem(item, col);
0055                 job->addSubjob(createJob);
0056                 createJob->start();
0057             }
0058         });
0059         return job;
0060     }
0061 }
0062 
0063 KJob *TaskRepository::create(Domain::Task::Ptr task)
0064 {
0065     auto item = m_serializer->createItemFromTask(task);
0066     Q_ASSERT(!item.isValid());
0067     return createItem(item);
0068 }
0069 
0070 KJob *TaskRepository::createChild(Domain::Task::Ptr task, Domain::Task::Ptr parent)
0071 {
0072     Item taskItem = m_serializer->createItemFromTask(task);
0073     Q_ASSERT(!taskItem.isValid());
0074 
0075     Item parentItem = m_serializer->createItemFromTask(parent);
0076     Q_ASSERT(parentItem.isValid());
0077     Q_ASSERT(parentItem.parentCollection().isValid());
0078 
0079     m_serializer->updateItemParent(taskItem, parent);
0080 
0081     return m_storage->createItem(taskItem, parentItem.parentCollection());
0082 }
0083 
0084 KJob *TaskRepository::createInProject(Domain::Task::Ptr task, Domain::Project::Ptr project)
0085 {
0086     Item taskItem = m_serializer->createItemFromTask(task);
0087     Q_ASSERT(!taskItem.isValid());
0088 
0089     Item projectItem = m_serializer->createItemFromProject(project);
0090     Q_ASSERT(projectItem.isValid());
0091     Q_ASSERT(projectItem.parentCollection().isValid());
0092 
0093     m_serializer->updateItemProject(taskItem, project);
0094 
0095     return m_storage->createItem(taskItem, projectItem.parentCollection());
0096 }
0097 
0098 KJob *TaskRepository::createInContext(Domain::Task::Ptr task, Domain::Context::Ptr context)
0099 {
0100     Item item = m_serializer->createItemFromTask(task);
0101     Q_ASSERT(!item.isValid());
0102 
0103     m_serializer->addContextToTask(context, item);
0104 
0105     return createItem(item);
0106 }
0107 
0108 KJob *TaskRepository::update(Domain::Task::Ptr task)
0109 {
0110     auto item = m_serializer->createItemFromTask(task);
0111     Q_ASSERT(item.isValid());
0112     return m_storage->updateItem(item, this);
0113 }
0114 
0115 KJob *TaskRepository::remove(Domain::Task::Ptr task)
0116 {
0117     auto item = m_serializer->createItemFromTask(task);
0118     Q_ASSERT(item.isValid());
0119 
0120     auto compositeJob = new CompositeJob();
0121     ItemFetchJobInterface *fetchItemJob = m_storage->fetchItem(item, this);
0122     compositeJob->install(fetchItemJob->kjob(), [fetchItemJob, compositeJob, this] {
0123         if (fetchItemJob->kjob()->error() != KJob::NoError)
0124            return;
0125 
0126         Q_ASSERT(fetchItemJob->items().size() == 1);
0127         auto item = fetchItemJob->items().at(0);
0128 
0129         ItemFetchJobInterface *fetchCollectionItemsJob = m_storage->fetchItems(item.parentCollection(), this);
0130         compositeJob->install(fetchCollectionItemsJob->kjob(), [fetchCollectionItemsJob, item, compositeJob, this] {
0131             if (fetchCollectionItemsJob->kjob()->error() != KJob::NoError)
0132                 return;
0133 
0134             Item::List childItems = m_serializer->filterDescendantItems(fetchCollectionItemsJob->items(), item);
0135             childItems << item;
0136 
0137             auto removeJob = m_storage->removeItems(childItems, this);
0138             compositeJob->addSubjob(removeJob);
0139             removeJob->start();
0140         });
0141     });
0142 
0143     return compositeJob;
0144 }
0145 
0146 KJob *TaskRepository::promoteToProject(Domain::Task::Ptr task)
0147 {
0148     auto item = m_serializer->createItemFromTask(task);
0149 
0150     auto job = new CompositeJob();
0151     auto fetchJob = m_storage->fetchItem(item, this);
0152     job->install(fetchJob->kjob(), [fetchJob, job, this] {
0153         if (fetchJob->kjob()->error() != KJob::NoError)
0154            return;
0155 
0156         Q_ASSERT(fetchJob->items().size() == 1);
0157         auto item = fetchJob->items().at(0);
0158         m_serializer->promoteItemToProject(item);
0159 
0160         auto updateJob = m_storage->updateItem(item, this);
0161         job->addSubjob(updateJob);
0162         updateJob->start();
0163     });
0164     return job;
0165 }
0166 
0167 KJob *TaskRepository::associate(Domain::Task::Ptr parent, Domain::Task::Ptr child)
0168 {
0169     auto childItem = m_serializer->createItemFromTask(child);
0170 
0171     auto job = new CompositeJob();
0172     ItemFetchJobInterface *fetchItemJob = m_storage->fetchItem(childItem, this);
0173     job->install(fetchItemJob->kjob(), [fetchItemJob, child, parent, job, this] {
0174         if (fetchItemJob->kjob()->error() != KJob::NoError)
0175            return;
0176 
0177         Q_ASSERT(fetchItemJob->items().size() == 1);
0178         auto childItem = fetchItemJob->items().at(0);
0179         m_serializer->updateItemParent(childItem, parent);
0180 
0181         // Check collections to know if we need to move child
0182         auto partialParentItem = m_serializer->createItemFromTask(parent);
0183         ItemFetchJobInterface *fetchParentItemJob = m_storage->fetchItems(partialParentItem.parentCollection(), this);
0184         job->install(fetchParentItemJob->kjob(), [child, parent, fetchParentItemJob, partialParentItem, childItem, job, this] {
0185             if (fetchParentItemJob->kjob()->error() != KJob::NoError)
0186                 return;
0187 
0188             const auto items = fetchParentItemJob->items();
0189             const auto parentIndex = items.indexOf(partialParentItem);
0190             Q_ASSERT(parentIndex >= 0);
0191             const auto parentItem = items.at(parentIndex);
0192 
0193             const auto childUid = m_serializer->itemUid(childItem);
0194             auto relatedUid = m_serializer->relatedUidFromItem(parentItem);
0195             while (!relatedUid.isEmpty()) {
0196                 if (relatedUid == childUid) {
0197                     job->emitError(i18n("Could not associate '%1', it is an ancestor of '%2'",
0198                                         child->title(),
0199                                         parent->title()));
0200                     return;
0201                 }
0202 
0203                 auto it = std::find_if(items.constBegin(), items.constEnd(),
0204                                        [relatedUid, this] (const Akonadi::Item &item) {
0205                     return m_serializer->itemUid(item) == relatedUid;
0206                 });
0207                 if (it == items.end())
0208                     break;
0209 
0210                 relatedUid = m_serializer->relatedUidFromItem(*it);
0211             }
0212 
0213             const int itemCollectionId = childItem.parentCollection().id();
0214             const int parentCollectionId = parentItem.parentCollection().id();
0215 
0216             if (itemCollectionId != parentCollectionId) {
0217                 ItemFetchJobInterface *fetchChildrenItemJob = m_storage->fetchItems(childItem.parentCollection(), this);
0218                 job->install(fetchChildrenItemJob->kjob(), [fetchChildrenItemJob, childItem, parentItem, job, this] {
0219                     if (fetchChildrenItemJob->kjob()->error() != KJob::NoError)
0220                         return;
0221 
0222                     Item::List childItems = m_serializer->filterDescendantItems(fetchChildrenItemJob->items(), childItem);
0223 
0224                     auto transaction = m_storage->createTransaction(this);
0225                     m_storage->updateItem(childItem, transaction);
0226                     childItems.push_front(childItem);
0227                     m_storage->moveItems(childItems, parentItem.parentCollection(), transaction);
0228                     job->addSubjob(transaction);
0229                     transaction->start();
0230                 });
0231             } else {
0232                 auto updateJob = m_storage->updateItem(childItem, this);
0233                 job->addSubjob(updateJob);
0234                 updateJob->start();
0235             }
0236         });
0237     });
0238 
0239     return job;
0240 }
0241 
0242 KJob *TaskRepository::dissociate(Domain::Task::Ptr child)
0243 {
0244     auto job = new CompositeJob();
0245     auto childItem = m_serializer->createItemFromTask(child);
0246     ItemFetchJobInterface *fetchItemJob = m_storage->fetchItem(childItem, this);
0247     job->install(fetchItemJob->kjob(), [fetchItemJob, job, this] {
0248         if (fetchItemJob->kjob()->error() != KJob::NoError)
0249             return;
0250 
0251         Q_ASSERT(fetchItemJob->items().size() == 1);
0252         auto childItem = fetchItemJob->items().at(0);
0253 
0254         m_serializer->removeItemParent(childItem);
0255 
0256         auto updateJob = m_storage->updateItem(childItem, this);
0257         job->addSubjob(updateJob);
0258         updateJob->start();
0259     });
0260 
0261     return job;
0262 }
0263 
0264 KJob *TaskRepository::dissociateAll(Domain::Task::Ptr child)
0265 {
0266     auto job = new CompositeJob();
0267     auto childItem = m_serializer->createItemFromTask(child);
0268     ItemFetchJobInterface *fetchItemJob = m_storage->fetchItem(childItem, this);
0269     job->install(fetchItemJob->kjob(), [fetchItemJob, job, this] {
0270         if (fetchItemJob->kjob()->error() != KJob::NoError)
0271             return;
0272 
0273         Q_ASSERT(fetchItemJob->items().size() == 1);
0274         auto childItem = fetchItemJob->items().at(0);
0275 
0276         m_serializer->removeItemParent(childItem);
0277         m_serializer->clearItem(&childItem);
0278 
0279         auto updateJob = m_storage->updateItem(childItem, this);
0280         job->addSubjob(updateJob);
0281         updateJob->start();
0282     });
0283 
0284     return job;
0285 }
0286 
0287 #include "moc_akonaditaskrepository.cpp"