File indexing completed on 2024-05-12 05:12:40
0001 /* 0002 Copyright (C) 2012 Kevin Krammer <krammer@kde.org> 0003 0004 This program is free software; you can redistribute it and/or modify 0005 it under the terms of the GNU General Public License as published by 0006 the Free Software Foundation; either version 2 of the License, or 0007 (at your option) any later version. 0008 0009 This program is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0012 GNU General Public License for more details. 0013 0014 You should have received a copy of the GNU General Public License along 0015 with this program; if not, write to the Free Software Foundation, Inc., 0016 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 0017 */ 0018 0019 #include "collectionresolvejob.h" 0020 0021 #include <Akonadi/CollectionFetchJob> 0022 #include <Akonadi/CollectionFetchScope> 0023 #include <Akonadi/CollectionPathResolver> 0024 0025 #include <KLocalizedString> 0026 0027 #include "errorreporter.h" 0028 0029 using namespace Akonadi; 0030 0031 HackedCollectionPathResolver::HackedCollectionPathResolver(const QString &path, QObject *parent) 0032 : CollectionPathResolver(path, parent) 0033 {} 0034 0035 HackedCollectionPathResolver::HackedCollectionPathResolver(const Collection &col, QObject *parent) 0036 : CollectionPathResolver(col, parent) 0037 {} 0038 0039 bool HackedCollectionPathResolver::addSubjob(KJob *job) 0040 { 0041 if (auto akjob = qobject_cast<Akonadi::Job*>(job)) { 0042 connect(akjob, &Job::aboutToStart, 0043 [](Akonadi::Job *subjob) { 0044 if (auto fetchJob = qobject_cast<CollectionFetchJob*>(subjob)) { 0045 fetchJob->fetchScope().setListFilter(CollectionFetchScope::NoFilter); 0046 } 0047 }); 0048 } 0049 return CollectionPathResolver::addSubjob(job); 0050 } 0051 0052 0053 CollectionResolveJob::CollectionResolveJob(const QString &userInput, QObject *parent) 0054 : KCompositeJob(parent), 0055 mUserInput(userInput), 0056 mHadSlash(false) 0057 { 0058 setAutoDelete(false); 0059 0060 // A collection path ending with a '/' is accepted by a 0061 // CollectionPathResolver. However, we strip any slash here 0062 // so that an ID or a URL can also be parsed (but, obviously, 0063 // not from a single slash meaning the root). Note whether 0064 // any slash was removed, so that the caller can act on it 0065 // if required. 0066 0067 QString in = userInput; 0068 if (in.length() > 1 && in.endsWith(QLatin1Char('/'))) { 0069 in.chop(1); 0070 mHadSlash = true; 0071 } 0072 0073 mCollection = parseCollection(in); 0074 } 0075 0076 void CollectionResolveJob::start() 0077 { 0078 if (!hasUsableInput()) { 0079 emitResult(); 0080 return; 0081 } 0082 0083 if (mCollection.isValid()) { 0084 fetchBase(); 0085 } else { 0086 CollectionPathResolver *resolver = new HackedCollectionPathResolver(mUserInput, this); 0087 addSubjob(resolver); 0088 resolver->start(); 0089 } 0090 } 0091 0092 bool CollectionResolveJob::hasUsableInput() 0093 { 0094 if (mCollection.isValid() || mUserInput.startsWith(CollectionPathResolver::pathDelimiter())) { 0095 return true; 0096 } 0097 0098 setError(Akonadi::Job::UserError); 0099 setErrorText(i18nc("@info:shell", "Unknown Akonadi collection format '%1'", mUserInput)); 0100 return false; 0101 } 0102 0103 void CollectionResolveJob::fetchBase() 0104 { 0105 if (mCollection == Collection::root()) { 0106 emitResult(); 0107 return; 0108 } 0109 0110 CollectionFetchJob *job = new CollectionFetchJob(mCollection, CollectionFetchJob::Base, this); 0111 job->fetchScope().setListFilter(CollectionFetchScope::NoFilter); 0112 addSubjob(job); 0113 } 0114 0115 void CollectionResolveJob::slotResult(KJob *job) 0116 { 0117 if (job->error() == 0) { 0118 CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *>(job); 0119 if (fetchJob != nullptr) { 0120 mCollection = fetchJob->collections().first(); 0121 } else { 0122 CollectionPathResolver *resolver = qobject_cast<CollectionPathResolver *>(job); 0123 mCollection = Collection(resolver->collection()); 0124 fetchBase(); 0125 } 0126 } 0127 0128 bool willEmitResult = (job->error() && !error()); 0129 // If willEmitResult is true, then emitResult() will be 0130 // done inside the call of KCompositeJob::slotResult() below. 0131 // So there will be no need for us to do it again. 0132 KCompositeJob::slotResult(job); 0133 0134 if (!hasSubjobs() && !willEmitResult) { 0135 emitResult(); 0136 } 0137 } 0138 0139 QString CollectionResolveJob::formattedCollectionName() const 0140 { 0141 if (mCollection == Collection::root()) { 0142 return (i18nc("@info:shell 1=collection ID", 0143 "%1 (root)", QString::number(mCollection.id()))); 0144 } else { 0145 return (i18nc("@info:shell 1=collection ID, 2=collection name", 0146 "%1 (\"%2\")", 0147 QString::number(mCollection.id()), mCollection.name())); 0148 } 0149 } 0150 0151 Akonadi::Item CollectionResolveJob::parseItem(const QString &userInput, bool verbose) 0152 { 0153 Item item; 0154 0155 // See if user input is a valid integer as an item ID 0156 bool ok; 0157 unsigned int id = userInput.toUInt(&ok); 0158 if (ok) { 0159 item = Item(id); // conversion succeeded 0160 } else { 0161 // Otherwise check if we have an Akonadi URL 0162 const QUrl url = QUrl::fromUserInput(userInput); 0163 if (url.isValid() && url.scheme() == QLatin1String("akonadi")) { 0164 // valid Akonadi URL 0165 item = Item::fromUrl(url); 0166 } 0167 } 0168 // Otherwise return an invalid Item 0169 0170 if (!item.isValid() && verbose) { // report error if required 0171 ErrorReporter::error(i18nc("@info:shell", "Invalid item syntax '%1'", userInput)); 0172 } 0173 0174 return (item); 0175 } 0176 0177 Akonadi::Collection CollectionResolveJob::parseCollection(const QString &userInput) 0178 { 0179 Collection coll; 0180 0181 // First see if user input is a valid integer as a collection ID 0182 bool ok; 0183 unsigned int id = userInput.toUInt(&ok); 0184 if (ok) { // conversion succeeded 0185 if (id == 0) { 0186 coll = Collection::root(); // the root collection 0187 } else { 0188 coll = Collection(id); // the specified collection 0189 } 0190 } else { 0191 // Then quickly check for a path of "/", meaning the root 0192 if (userInput == QLatin1String("/")) { 0193 coll = Collection::root(); 0194 } else { 0195 // Next check if we have an Akonadi URL 0196 const QUrl url = QUrl::fromUserInput(userInput); 0197 if (url.isValid() && url.scheme() == QLatin1String("akonadi")) { 0198 // valid Akonadi URL 0199 coll = Collection::fromUrl(url); 0200 } 0201 } 0202 } 0203 // If none of these applied, assume that we have a path 0204 // and return an invalid Collection. 0205 0206 return (coll); 0207 }