File indexing completed on 2024-11-10 04:40:07
0001 /* 0002 SPDX-FileCopyrightText: 2006 Tobias Koenig <tokoe@kde.org> 0003 SPDX-FileCopyrightText: 2009 Volker Krause <vkrause@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "knutresource.h" 0009 #include "knutresource_debug.h" 0010 #include "settingsadaptor.h" 0011 #include "xmlreader.h" 0012 #include "xmlwriter.h" 0013 0014 #include "agentfactory.h" 0015 #include "changerecorder.h" 0016 #include "itemfetchscope.h" 0017 #include "tagcreatejob.h" 0018 #include <QDBusConnection> 0019 0020 #include <KLocalizedString> 0021 #include <QFileDialog> 0022 0023 #include <QDir> 0024 #include <QFile> 0025 #include <QFileSystemWatcher> 0026 #include <QStandardPaths> 0027 #include <QUuid> 0028 0029 using namespace Akonadi; 0030 0031 KnutResource::KnutResource(const QString &id) 0032 : ResourceBase(id) 0033 , mWatcher(new QFileSystemWatcher(this)) 0034 , mSettings(new KnutSettings()) 0035 { 0036 changeRecorder()->itemFetchScope().fetchFullPayload(); 0037 changeRecorder()->fetchCollection(true); 0038 0039 new SettingsAdaptor(mSettings); 0040 QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"), mSettings, QDBusConnection::ExportAdaptors); 0041 connect(this, &KnutResource::reloadConfiguration, this, &KnutResource::load); 0042 connect(mWatcher, &QFileSystemWatcher::fileChanged, this, &KnutResource::load); 0043 load(); 0044 } 0045 0046 KnutResource::~KnutResource() 0047 { 0048 delete mSettings; 0049 } 0050 0051 void KnutResource::load() 0052 { 0053 if (!mWatcher->files().isEmpty()) { 0054 mWatcher->removePaths(mWatcher->files()); 0055 } 0056 0057 // file loading 0058 QString fileName = mSettings->dataFile(); 0059 if (fileName.isEmpty()) { 0060 Q_EMIT status(Broken, i18n("No data file selected.")); 0061 return; 0062 } 0063 0064 if (!QFile::exists(fileName)) { 0065 fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf6/akonadi_knut_resource/knut-template.xml")); 0066 } 0067 0068 if (!mDocument.loadFile(fileName)) { 0069 Q_EMIT status(Broken, mDocument.lastError()); 0070 return; 0071 } 0072 0073 if (mSettings->fileWatchingEnabled()) { 0074 mWatcher->addPath(fileName); 0075 } 0076 0077 Q_EMIT status(Idle, i18n("File '%1' loaded successfully.", fileName)); 0078 synchronize(); 0079 } 0080 0081 void KnutResource::save() 0082 { 0083 if (mSettings->readOnly()) { 0084 return; 0085 } 0086 const QString fileName = mSettings->dataFile(); 0087 if (!mDocument.writeToFile(fileName)) { 0088 Q_EMIT error(mDocument.lastError()); 0089 return; 0090 } 0091 } 0092 0093 void KnutResource::configure(WId windowId) 0094 { 0095 QString oldFile = mSettings->dataFile(); 0096 if (oldFile.isEmpty()) { 0097 oldFile = QDir::homePath(); 0098 } 0099 0100 // TODO: Use windowId 0101 Q_UNUSED(windowId) 0102 const QString newFile = 0103 QFileDialog::getSaveFileName(nullptr, 0104 i18n("Select Data File"), 0105 QString(), 0106 QStringLiteral("*.xml |") + i18nc("Filedialog filter for Akonadi data file", "Akonadi Knut Data File")); 0107 0108 if (newFile.isEmpty() || oldFile == newFile) { 0109 return; 0110 } 0111 0112 mSettings->setDataFile(newFile); 0113 mSettings->save(); 0114 load(); 0115 0116 Q_EMIT configurationDialogAccepted(); 0117 } 0118 0119 void KnutResource::retrieveCollections() 0120 { 0121 const Collection::List collections = mDocument.collections(); 0122 collectionsRetrieved(collections); 0123 const Tag::List tags = mDocument.tags(); 0124 for (const Tag &tag : tags) { 0125 auto createjob = new TagCreateJob(tag); 0126 createjob->setMergeIfExisting(true); 0127 } 0128 } 0129 0130 void KnutResource::retrieveItems(const Akonadi::Collection &collection) 0131 { 0132 Item::List items = mDocument.items(collection, false); 0133 if (!mDocument.lastError().isEmpty()) { 0134 cancelTask(mDocument.lastError()); 0135 return; 0136 } 0137 0138 itemsRetrieved(items); 0139 } 0140 0141 #ifdef DO_IT_THE_OLD_WAY 0142 bool KnutResource::retrieveItem(const Item &item, const QSet<QByteArray> &parts) 0143 { 0144 Q_UNUSED(parts) 0145 0146 const QDomElement itemElem = mDocument.itemElementByRemoteId(item.remoteId()); 0147 if (itemElem.isNull()) { 0148 cancelTask(i18n("No item found for remoteid %1", item.remoteId())); 0149 return false; 0150 } 0151 0152 Item i = XmlReader::elementToItem(itemElem, true); 0153 i.setId(item.id()); 0154 itemRetrieved(i); 0155 return true; 0156 } 0157 #endif 0158 0159 bool KnutResource::retrieveItems(const Item::List &items, const QSet<QByteArray> &parts) 0160 { 0161 Q_UNUSED(parts) 0162 0163 Item::List results; 0164 results.reserve(items.size()); 0165 for (const auto &item : items) { 0166 const QDomElement itemElem = mDocument.itemElementByRemoteId(item.remoteId()); 0167 if (itemElem.isNull()) { 0168 cancelTask(i18n("No item found for remoteid %1", item.remoteId())); 0169 return false; 0170 } 0171 0172 Item i = XmlReader::elementToItem(itemElem, true); 0173 i.setParentCollection(item.parentCollection()); 0174 i.setId(item.id()); 0175 results.push_back(i); 0176 } 0177 0178 itemsRetrieved(results); 0179 return true; 0180 } 0181 0182 void KnutResource::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) 0183 { 0184 QDomElement parentElem = mDocument.collectionElementByRemoteId(parent.remoteId()); 0185 if (parentElem.isNull()) { 0186 Q_EMIT error(i18n("Parent collection not found in DOM tree.")); 0187 changeProcessed(); 0188 return; 0189 } 0190 0191 Collection c(collection); 0192 c.setRemoteId(QUuid::createUuid().toString()); 0193 if (XmlWriter::writeCollection(c, parentElem).isNull()) { 0194 Q_EMIT error(i18n("Unable to write collection.")); 0195 changeProcessed(); 0196 } else { 0197 save(); 0198 changeCommitted(c); 0199 } 0200 } 0201 0202 void KnutResource::collectionChanged(const Akonadi::Collection &collection) 0203 { 0204 QDomElement oldElem = mDocument.collectionElementByRemoteId(collection.remoteId()); 0205 if (oldElem.isNull()) { 0206 Q_EMIT error(i18n("Modified collection not found in DOM tree.")); 0207 changeProcessed(); 0208 return; 0209 } 0210 0211 QDomElement newElem; 0212 newElem = XmlWriter::collectionToElement(collection, mDocument.document()); 0213 // move all items/collections over to the new node 0214 const QDomNodeList children = oldElem.childNodes(); 0215 const int numberOfChildren = children.count(); 0216 for (int i = 0; i < numberOfChildren; ++i) { 0217 const QDomElement child = children.at(i).toElement(); 0218 qCDebug(KNUTRESOURCE_LOG) << "reparenting " << child.tagName() << child.attribute(QStringLiteral("rid")); 0219 if (child.isNull()) { 0220 continue; 0221 } 0222 if (child.tagName() == QLatin1StringView("item") || child.tagName() == QStringLiteral("collection")) { 0223 newElem.appendChild(child); // reparents 0224 --i; // children, despite being const is modified by the reparenting 0225 } 0226 } 0227 oldElem.parentNode().replaceChild(newElem, oldElem); 0228 save(); 0229 changeCommitted(collection); 0230 } 0231 0232 void KnutResource::collectionRemoved(const Akonadi::Collection &collection) 0233 { 0234 const QDomElement colElem = mDocument.collectionElementByRemoteId(collection.remoteId()); 0235 if (colElem.isNull()) { 0236 Q_EMIT error(i18n("Deleted collection not found in DOM tree.")); 0237 changeProcessed(); 0238 return; 0239 } 0240 0241 colElem.parentNode().removeChild(colElem); 0242 save(); 0243 changeProcessed(); 0244 } 0245 0246 void KnutResource::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) 0247 { 0248 QDomElement parentElem = mDocument.collectionElementByRemoteId(collection.remoteId()); 0249 if (parentElem.isNull()) { 0250 Q_EMIT error(i18n("Parent collection '%1' not found in DOM tree.", collection.remoteId())); 0251 changeProcessed(); 0252 return; 0253 } 0254 0255 Item i(item); 0256 i.setRemoteId(QUuid::createUuid().toString()); 0257 if (XmlWriter::writeItem(i, parentElem).isNull()) { 0258 Q_EMIT error(i18n("Unable to write item.")); 0259 changeProcessed(); 0260 } else { 0261 save(); 0262 changeCommitted(i); 0263 } 0264 } 0265 0266 void KnutResource::itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &parts) 0267 { 0268 Q_UNUSED(parts) 0269 0270 const QDomElement oldElem = mDocument.itemElementByRemoteId(item.remoteId()); 0271 if (oldElem.isNull()) { 0272 Q_EMIT error(i18n("Modified item not found in DOM tree.")); 0273 changeProcessed(); 0274 return; 0275 } 0276 0277 const QDomElement newElem = XmlWriter::itemToElement(item, mDocument.document()); 0278 oldElem.parentNode().replaceChild(newElem, oldElem); 0279 save(); 0280 changeCommitted(item); 0281 } 0282 0283 void KnutResource::itemRemoved(const Akonadi::Item &item) 0284 { 0285 const QDomElement itemElem = mDocument.itemElementByRemoteId(item.remoteId()); 0286 if (itemElem.isNull()) { 0287 Q_EMIT error(i18n("Deleted item not found in DOM tree.")); 0288 changeProcessed(); 0289 return; 0290 } 0291 0292 itemElem.parentNode().removeChild(itemElem); 0293 save(); 0294 changeProcessed(); 0295 } 0296 0297 void KnutResource::itemMoved(const Item &item, const Collection &collectionSource, const Collection &collectionDestination) 0298 { 0299 const QDomElement oldElem = mDocument.itemElementByRemoteId(item.remoteId()); 0300 if (oldElem.isNull()) { 0301 qCWarning(KNUTRESOURCE_LOG) << "Moved item not found in DOM tree"; 0302 changeProcessed(); 0303 return; 0304 } 0305 0306 QDomElement sourceParentElem = mDocument.collectionElementByRemoteId(collectionSource.remoteId()); 0307 if (sourceParentElem.isNull()) { 0308 Q_EMIT error(i18n("Parent collection '%1' not found in DOM tree.", collectionSource.remoteId())); 0309 changeProcessed(); 0310 return; 0311 } 0312 0313 QDomElement destParentElem = mDocument.collectionElementByRemoteId(collectionDestination.remoteId()); 0314 if (destParentElem.isNull()) { 0315 Q_EMIT error(i18n("Parent collection '%1' not found in DOM tree.", collectionDestination.remoteId())); 0316 changeProcessed(); 0317 return; 0318 } 0319 QDomElement itemElem = mDocument.itemElementByRemoteId(item.remoteId()); 0320 if (itemElem.isNull()) { 0321 Q_EMIT error(i18n("No item found for remoteid %1", item.remoteId())); 0322 } 0323 0324 sourceParentElem.removeChild(itemElem); 0325 destParentElem.appendChild(itemElem); 0326 0327 if (XmlWriter::writeItem(item, destParentElem).isNull()) { 0328 Q_EMIT error(i18n("Unable to write item.")); 0329 } else { 0330 save(); 0331 } 0332 changeProcessed(); 0333 } 0334 0335 QSet<qint64> KnutResource::parseQuery(const QString &queryString) 0336 { 0337 QSet<qint64> resultSet; 0338 Akonadi::SearchQuery query = Akonadi::SearchQuery::fromJSON(queryString.toLatin1()); 0339 const QList<SearchTerm> subTerms = query.term().subTerms(); 0340 for (const Akonadi::SearchTerm &term : subTerms) { 0341 if (term.key() == QLatin1StringView("resource")) { 0342 resultSet << term.value().toInt(); 0343 } 0344 } 0345 return resultSet; 0346 } 0347 0348 void KnutResource::search(const QString &query, const Collection &collection) 0349 { 0350 Q_UNUSED(collection) 0351 const QList<qint64> result = parseQuery(query).values().toVector(); 0352 qCDebug(KNUTRESOURCE_LOG) << "KNUT QUERY:" << query; 0353 qCDebug(KNUTRESOURCE_LOG) << "KNUT RESOURCE:" << result; 0354 searchFinished(result, Akonadi::AgentSearchInterface::Uid); 0355 } 0356 0357 void KnutResource::addSearch(const QString &query, const QString &queryLanguage, const Collection &resultCollection) 0358 { 0359 Q_UNUSED(query) 0360 Q_UNUSED(queryLanguage) 0361 Q_UNUSED(resultCollection) 0362 qCDebug(KNUTRESOURCE_LOG) << "addSearch: query=" << query << ", queryLanguage=" << queryLanguage << ", resultCollection=" << resultCollection.id(); 0363 } 0364 0365 void KnutResource::removeSearch(const Collection &resultCollection) 0366 { 0367 Q_UNUSED(resultCollection) 0368 qCDebug(KNUTRESOURCE_LOG) << "removeSearch:" << resultCollection.id(); 0369 } 0370 0371 AKONADI_RESOURCE_MAIN(KnutResource) 0372 0373 #include "moc_knutresource.cpp"