File indexing completed on 2024-05-12 05:12:42
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 "listcommand.h" 0020 0021 #include "collectionresolvejob.h" 0022 0023 #include <Akonadi/CollectionFetchJob> 0024 #include <Akonadi/CollectionFetchScope> 0025 #include <Akonadi/ItemFetchJob> 0026 #include <Akonadi/ItemFetchScope> 0027 0028 #include <QDateTime> 0029 0030 #include <iostream> 0031 0032 #include "commandfactory.h" 0033 0034 using namespace Akonadi; 0035 0036 DEFINE_COMMAND("list", ListCommand, I18N_NOOP("List sub-collections and/or items in a specified collection")); 0037 0038 ListCommand::ListCommand(QObject *parent) 0039 : AbstractCommand(parent) 0040 { 0041 } 0042 0043 void ListCommand::setupCommandOptions(QCommandLineParser *parser) 0044 { 0045 addOptionsOption(parser); 0046 parser->addOption(QCommandLineOption((QStringList() << "l" << "details"), i18n("List more detailed information"))); 0047 parser->addOption(QCommandLineOption((QStringList() << "c" << "collections"), i18n("List only sub-collections"))); 0048 parser->addOption(QCommandLineOption((QStringList() << "i" << "items"), i18n("List only contained items"))); 0049 0050 parser->addPositionalArgument("collection", i18nc("@info:shell", "The collection to list: an ID, path or Akonadi URL")); 0051 } 0052 0053 int ListCommand::initCommand(QCommandLineParser *parser) 0054 { 0055 const QStringList args = parser->positionalArguments(); 0056 if (!checkArgCount(args, 1, i18nc("@info:shell", "Missing collection argument"))) return InvalidUsage; 0057 0058 mListItems = parser->isSet("items"); // selection options specified 0059 mListCollections = parser->isSet("collections"); 0060 if (!mListCollections && !mListItems) { // if none given, then 0061 mListCollections = mListItems = true; // list both by default 0062 } 0063 mListDetails = parser->isSet("details"); // listing option specified 0064 0065 const QString collectionArg = args.first(); 0066 if (!getResolveJob(collectionArg)) return InvalidUsage; 0067 0068 return NoError; 0069 } 0070 0071 void ListCommand::start() 0072 { 0073 connect(resolveJob(), &KJob::result, this, &ListCommand::onBaseFetched); 0074 resolveJob()->start(); 0075 } 0076 0077 static void writeColumn(const QString &data, int width = 0) 0078 { 0079 std::cout << data.leftJustified(width).toLocal8Bit().constData() << " "; 0080 } 0081 0082 static void writeColumn(quint64 data, int width = 0) 0083 { 0084 writeColumn(QString::number(data), width); 0085 } 0086 0087 void ListCommand::onBaseFetched(KJob *job) 0088 { 0089 if (job->error() != 0) { 0090 emit error(job->errorString()); 0091 emit finished(RuntimeError); 0092 return; 0093 } 0094 0095 Q_ASSERT(job == resolveJob()); 0096 0097 if (mListCollections) { 0098 fetchCollections(); 0099 } else { 0100 fetchItems(); 0101 } 0102 } 0103 0104 void ListCommand::fetchCollections() 0105 { 0106 CollectionResolveJob *res = resolveJob(); 0107 Q_ASSERT(res->collection().isValid()); 0108 0109 CollectionFetchJob *job = new CollectionFetchJob(res->collection(), CollectionFetchJob::FirstLevel, this); 0110 job->fetchScope().setListFilter(CollectionFetchScope::NoFilter); 0111 connect(job, &KJob::result, this, &ListCommand::onCollectionsFetched); 0112 } 0113 0114 void ListCommand::onCollectionsFetched(KJob *job) 0115 { 0116 if (!checkJobResult(job)) return; 0117 CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *>(job); 0118 Q_ASSERT(fetchJob != nullptr); 0119 0120 Collection::List collections = fetchJob->collections(); 0121 if (collections.isEmpty()) { 0122 if (mListCollections) { // message only if collections requested 0123 std::cout << qPrintable(i18nc("@info:shell", 0124 "Collection %1 has no sub-collections", 0125 resolveJob()->formattedCollectionName())) << std::endl; 0126 } 0127 } else { 0128 // This works because Akonadi::Entity implements operator< 0129 // which compares item IDs numerically 0130 std::sort(collections.begin(), collections.end()); 0131 0132 std::cout << qPrintable(i18ncp("@info:shell output section header 1=count, 2=collection", 0133 "Collection %2 has %1 sub-collection:", 0134 "Collection %2 has %1 sub-collections:", 0135 collections.count(), 0136 resolveJob()->formattedCollectionName())) << std::endl; 0137 if (mListDetails) { 0138 std::cout << " "; 0139 writeColumn(i18nc("@info:shell column header", "ID"), 8); 0140 writeColumn(i18nc("@info:shell column header", "Name")); 0141 std::cout << std::endl; 0142 } 0143 0144 Q_FOREACH (const Collection &collection, collections) { 0145 std::cout << " "; 0146 if (mListDetails) { 0147 writeColumn(collection.id(), 8); 0148 writeColumn(collection.name()); 0149 } else { 0150 std::cout << qPrintable(collection.name()); 0151 } 0152 std::cout << std::endl; 0153 } 0154 } 0155 0156 if (mListItems) { 0157 fetchItems(); 0158 } else { 0159 emit finished(NoError); 0160 } 0161 } 0162 0163 void ListCommand::fetchItems() 0164 { 0165 Collection coll = resolveJob()->collection(); 0166 Q_ASSERT(coll.isValid()); 0167 0168 // only attempt item listing if collection has non-collection content MIME types 0169 QStringList contentMimeTypes = coll.contentMimeTypes(); 0170 contentMimeTypes.removeAll(Collection::mimeType()); 0171 if (!contentMimeTypes.isEmpty()) { 0172 ItemFetchJob *job = new ItemFetchJob(coll, this); 0173 job->fetchScope().setFetchModificationTime(true); 0174 job->fetchScope().fetchAllAttributes(false); 0175 job->fetchScope().fetchFullPayload(false); 0176 connect(job, &KJob::result, this, &ListCommand::onItemsFetched); 0177 } else { 0178 std::cout << qPrintable(i18nc("@info:shell", 0179 "Collection %1 cannot contain items", 0180 resolveJob()->formattedCollectionName())) << std::endl; 0181 emit finished(NoError); 0182 } 0183 } 0184 0185 void ListCommand::onItemsFetched(KJob *job) 0186 { 0187 if (!checkJobResult(job)) return; 0188 ItemFetchJob *fetchJob = qobject_cast<ItemFetchJob *>(job); 0189 Q_ASSERT(fetchJob != nullptr); 0190 Item::List items = fetchJob->items(); 0191 0192 if (items.isEmpty()) { 0193 if (mListItems) { // message only if items requested 0194 std::cout << qPrintable(i18nc("@info:shell", 0195 "Collection %1 has no items", 0196 resolveJob()->formattedCollectionName())) << std::endl; 0197 } 0198 } else { 0199 std::sort(items.begin(), items.end()); 0200 0201 std::cout << qPrintable(i18ncp("@info:shell output section header 1=count, 2=collection", 0202 "Collection %2 has %1 item:", 0203 "Collection %2 has %1 items:", 0204 items.count(), 0205 resolveJob()->formattedCollectionName())) << std::endl; 0206 if (mListDetails) { 0207 std::cout << " "; 0208 writeColumn(i18nc("@info:shell column header", "ID"), 8); 0209 writeColumn(i18nc("@info:shell column header", "MIME type"), 20); 0210 writeColumn(i18nc("@info:shell column header", "Size"), 10); 0211 writeColumn(i18nc("@info:shell column header", "Modification Time")); 0212 std::cout << std::endl; 0213 } 0214 0215 Q_FOREACH (const Item &item, items) { 0216 std::cout << " "; 0217 if (mListDetails) { 0218 writeColumn(item.id(), 8); 0219 writeColumn(item.mimeType(), 20); 0220 const QString size = QLocale::system().formattedDataSize(item.size()); 0221 writeColumn(size, 10); 0222 // from kdepim/akonadiconsole/browserwidget.cpp BrowserWidget::setItem() 0223 writeColumn((item.modificationTime().toString() + " UTC")); 0224 } else { 0225 std::cout << item.id(); 0226 } 0227 std::cout << std::endl; 0228 } 0229 } 0230 0231 emit finished(NoError); 0232 }