File indexing completed on 2024-05-12 05:12:42
0001 /* 0002 * Copyright (C) 2014 Bhaskar Kandiyal <bkandiyal@gmail.com> 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 0020 #include "importcommand.h" 0021 0022 #include <Akonadi/XmlWriteJob> 0023 #include <Akonadi/XmlDocument> 0024 #include <Akonadi/CollectionCreateJob> 0025 #include <Akonadi/CollectionFetchJob> 0026 #include <Akonadi/CollectionFetchScope> 0027 #include <Akonadi/ItemCreateJob> 0028 0029 #include <klocalizedstring.h> 0030 0031 #include <qfile.h> 0032 0033 #include "commandfactory.h" 0034 #include "errorreporter.h" 0035 #include "collectionresolvejob.h" 0036 0037 using namespace Akonadi; 0038 0039 DEFINE_COMMAND("import", ImportCommand, I18N_NOOP("Import an XML file")); 0040 0041 ImportCommand::ImportCommand(QObject *parent) 0042 : AbstractCommand(parent), 0043 mDocument(nullptr) 0044 { 0045 } 0046 0047 ImportCommand::~ImportCommand() 0048 { 0049 delete mDocument; 0050 } 0051 0052 void ImportCommand::setupCommandOptions(QCommandLineParser *parser) 0053 { 0054 addOptionsOption(parser); 0055 addDryRunOption(parser); 0056 0057 parser->addPositionalArgument("parent", i18nc("@info:shell", "The parent collection under which to import the file")); 0058 parser->addPositionalArgument("file", i18nc("@info:shell", "The file to import")); 0059 } 0060 0061 int ImportCommand::initCommand(QCommandLineParser *parser) 0062 { 0063 const QStringList args = parser->positionalArguments(); 0064 if (!checkArgCount(args, 1, i18nc("@info:shell", "No parent collection specified"))) return InvalidUsage; 0065 if (!checkArgCount(args, 2, i18nc("@info:shell", "No import file specified"))) return InvalidUsage; 0066 0067 if (!getCommonOptions(parser)) return InvalidUsage; 0068 0069 if (!getResolveJob(args.first())) return InvalidUsage; 0070 0071 const QString fileArg = args.at(1); 0072 mDocument = new XmlDocument(fileArg); 0073 if (!mDocument->isValid()) { 0074 emit error(i18nc("@info:shell", "Invalid XML file, %1", mDocument->lastError())); 0075 return InvalidUsage; 0076 } 0077 0078 mCollections = mDocument->collections(); 0079 0080 return NoError; 0081 } 0082 0083 void ImportCommand::start() 0084 { 0085 connect(resolveJob(), &KJob::result, this, &ImportCommand::onParentFetched); 0086 resolveJob()->start(); 0087 } 0088 0089 void ImportCommand::onParentFetched(KJob *job) 0090 { 0091 if (!checkJobResult(job, i18nc("@info:shell", "Unable to fetch parent collection, %1", job->errorString()))) return; 0092 0093 mParentCollection = resolveJob()->collection(); 0094 QMetaObject::invokeMethod(this, "processNextCollection", Qt::QueuedConnection); 0095 } 0096 0097 void ImportCommand::onChildrenFetched(KJob *job) 0098 { 0099 if (!checkJobResult(job, i18nc("@info:shell", "Unable to fetch children of parent collection, %1", job->errorString()))) return; 0100 0101 QString rid = job->property("rid").toString(); 0102 Collection parent = job->property("parent").value<Collection>(); 0103 Collection collection = mDocument->collectionByRemoteId(rid); 0104 Collection newCol; 0105 Collection::List collections = qobject_cast<CollectionFetchJob *>(job)->collections(); 0106 bool found = false; 0107 0108 Q_FOREACH (const Collection &col, collections) { 0109 if (collection.name() == col.name()) { 0110 found = true; 0111 newCol = col; 0112 break; 0113 } 0114 } 0115 0116 if (found) { 0117 ErrorReporter::progress(i18nc("@info:shell", "Collection '%1' already exists", collection.name())); 0118 mCollectionMap.insert(rid, newCol); 0119 QMetaObject::invokeMethod(this, "processNextCollection", Qt::QueuedConnection); 0120 } else { 0121 ErrorReporter::progress(i18nc("@info:shell", "Creating collection '%1'", collection.name())); 0122 collection.setParentCollection(parent); 0123 if (!isDryRun()) { 0124 CollectionCreateJob *createJob = new CollectionCreateJob(collection, this); 0125 createJob->setProperty("rid", rid); 0126 connect(createJob, &KJob::result, this, &ImportCommand::onCollectionCreated); 0127 } else { 0128 QMetaObject::invokeMethod(this, "processNextCollection", Qt::QueuedConnection); 0129 } 0130 } 0131 } 0132 0133 void ImportCommand::processNextCollection() 0134 { 0135 if (mCollections.isEmpty()) { 0136 processNextCollectionFromMap(); 0137 return; 0138 } 0139 0140 Collection collection = mCollections.takeFirst(); 0141 Collection parent; 0142 0143 ErrorReporter::progress(i18nc("@info:shell", "Processing collection '%1'", collection.name())); 0144 0145 if (collection.parentCollection().remoteId().isEmpty()) { 0146 parent = mParentCollection; 0147 } else { 0148 parent = mCollectionMap.value(collection.parentCollection().remoteId()); 0149 if (!parent.isValid() && !isDryRun()) { 0150 ErrorReporter::warning(i18nc("@info:shell", "Invalid parent for collection with remote ID '%1'", 0151 collection.remoteId())); 0152 QMetaObject::invokeMethod(this, "processNextCollection", Qt::QueuedConnection); 0153 } 0154 } 0155 0156 if (!isDryRun()) { 0157 CollectionFetchJob *fetchJob = new CollectionFetchJob(parent, CollectionFetchJob::FirstLevel, this); 0158 fetchJob->fetchScope().setListFilter(CollectionFetchScope::NoFilter); 0159 fetchJob->setProperty("rid", collection.remoteId()); 0160 fetchJob->setProperty("parent", QVariant::fromValue<Collection>(parent)); 0161 connect(fetchJob, &KJob::result, this, &ImportCommand::onChildrenFetched); 0162 } else { 0163 QMetaObject::invokeMethod(this, "processNextCollection", Qt::QueuedConnection); 0164 } 0165 } 0166 0167 void ImportCommand::onCollectionFetched(KJob *job) 0168 { 0169 Collection collection = job->property("collection").value<Collection>(); 0170 0171 if (job->error() != 0) { 0172 ErrorReporter::warning(i18nc("@info:shell", "Unable to fetch collection with remote ID '%1', %2", 0173 collection.remoteId(), job->errorString())); 0174 0175 if (!isDryRun()) { 0176 CollectionCreateJob *createJob = new CollectionCreateJob(collection, this); 0177 createJob->setProperty("rid", collection.remoteId()); 0178 connect(createJob, &KJob::result, this, &ImportCommand::onCollectionCreated); 0179 } else { 0180 QMetaObject::invokeMethod(this, "processNextCollection", Qt::QueuedConnection); 0181 } 0182 } else { 0183 CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *>(job); 0184 mCollectionMap.insert(collection.remoteId(), fetchJob->collections().first()); 0185 QMetaObject::invokeMethod(this, "processNextCollection", Qt::QueuedConnection); 0186 } 0187 } 0188 0189 void ImportCommand::onCollectionCreated(KJob *job) 0190 { 0191 if (!checkJobResult(job, i18nc("@info:shell", "Unable to create collection with remote ID '%1'", job->property("rid").toString()))) return; 0192 CollectionCreateJob *createJob = qobject_cast<CollectionCreateJob *>(job); 0193 mCollectionMap.insert(job->property("rid").toString(), createJob->collection()); 0194 QMetaObject::invokeMethod(this, "processNextCollection", Qt::QueuedConnection); 0195 } 0196 0197 void ImportCommand::processNextCollectionFromMap() 0198 { 0199 if (mCollectionMap.isEmpty()) { 0200 emit finished(NoError); 0201 return; 0202 } 0203 0204 QString rid = mCollectionMap.keys().at(0); 0205 Collection newCollection = mCollectionMap.take(rid); 0206 Collection oldCollection = mDocument->collectionByRemoteId(rid); 0207 0208 ErrorReporter::progress(i18nc("@info:shell", "Processing items for '%1'", newCollection.name())); 0209 0210 mItemQueue = mDocument->items(oldCollection, true); 0211 mCurrentCollection = newCollection; 0212 0213 QMetaObject::invokeMethod(this, "processNextItemFromQueue", Qt::QueuedConnection); 0214 } 0215 0216 void ImportCommand::processNextItemFromQueue() 0217 { 0218 if (mItemQueue.isEmpty()) { 0219 QMetaObject::invokeMethod(this, "processNextCollectionFromMap", Qt::QueuedConnection); 0220 return; 0221 } 0222 0223 Item item = mItemQueue.takeFirst(); 0224 ItemCreateJob *createJob = new ItemCreateJob(item, mCurrentCollection, this); 0225 connect(createJob, &KJob::result, this, &ImportCommand::onItemCreated); 0226 } 0227 0228 void ImportCommand::onItemCreated(KJob *job) 0229 { 0230 if (!checkJobResult(job, i18nc("@info:shell", "Error creating item, %1", job->errorString()))) return; 0231 ItemCreateJob *itemCreateJob = qobject_cast<ItemCreateJob *>(job); 0232 ErrorReporter::progress(i18nc("@info:shell", "Created item '%1'", itemCreateJob->item().remoteId())); 0233 QMetaObject::invokeMethod(this, "processNextItemFromQueue", Qt::QueuedConnection); 0234 }