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"