File indexing completed on 2024-05-12 05:12:41

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 "editcommand.h"
0021 
0022 #include <Akonadi/Item>
0023 #include <Akonadi/ItemFetchJob>
0024 #include <Akonadi/ItemFetchScope>
0025 #include <Akonadi/ItemModifyJob>
0026 
0027 #include <qprocess.h>
0028 #include <qtemporaryfile.h>
0029 
0030 #include <iostream>
0031 
0032 #include "commandfactory.h"
0033 #include "collectionresolvejob.h"
0034 
0035 using namespace Akonadi;
0036 
0037 DEFINE_COMMAND("edit", EditCommand, I18N_NOOP("Edit the raw payload for the specified item using $EDITOR"));
0038 
0039 EditCommand::EditCommand(QObject *parent)
0040     : AbstractCommand(parent),
0041       mTempFile(nullptr)
0042 {
0043 }
0044 
0045 EditCommand::~EditCommand()
0046 {
0047     delete mTempFile;
0048 }
0049 
0050 void EditCommand::setupCommandOptions(QCommandLineParser *parser)
0051 {
0052     addOptionsOption(parser);
0053     addDryRunOption(parser);
0054 
0055     parser->addPositionalArgument("item", i18nc("@info:shell", "The item to edit"));
0056 }
0057 
0058 int EditCommand::initCommand(QCommandLineParser *parser)
0059 {
0060     const QStringList args = parser->positionalArguments();
0061     if (!checkArgCount(args, 1, i18nc("@info:shell", "No item specified"))) return InvalidUsage;
0062 
0063     if (!getCommonOptions(parser)) return InvalidUsage;
0064 
0065     mItemArg = args.first();
0066 
0067     return NoError;
0068 }
0069 
0070 void EditCommand::start()
0071 {
0072     Item item = CollectionResolveJob::parseItem(mItemArg, true);
0073     if (!item.isValid()) {
0074         emit finished(InvalidUsage);
0075     }
0076 
0077     ItemFetchJob *job = new ItemFetchJob(item, this);
0078     job->fetchScope().setFetchModificationTime(false);
0079     job->fetchScope().fetchAllAttributes(false);
0080     job->fetchScope().fetchFullPayload(true);
0081     connect(job, &ItemFetchJob::result, this, &EditCommand::onItemFetched);
0082 }
0083 
0084 void EditCommand::onItemFetched(KJob *job)
0085 {
0086     if (!checkJobResult(job)) return;
0087     ItemFetchJob *fetchJob = qobject_cast<ItemFetchJob *>(job);
0088     Q_ASSERT(fetchJob != nullptr);
0089 
0090     Item::List items = fetchJob->items();
0091 
0092     if (items.count() < 1) {
0093         emit error(i18nc("@info:shell", "No result found for item '%1'", job->property("arg").toString()));
0094         emit finished(RuntimeError);
0095         return;
0096     }
0097 
0098     Akonadi::Item item = items.first();
0099 
0100     if (!item.hasPayload()) {
0101         emit error(i18nc("@info:shell", "Item '%1' has no payload", job->property("arg").toString()));
0102         emit finished(RuntimeError);
0103         return;
0104     }
0105 
0106     QString editor = QString::fromLocal8Bit(qgetenv("EDITOR"));
0107     if (editor.isEmpty()) {
0108         emit error(i18nc("@info:shell", "EDITOR environment variable needs to be set"));
0109         emit finished(RuntimeError);
0110         return;
0111     }
0112 
0113     mTempFile = new QTemporaryFile(this);
0114     mTempFile->open();
0115     mTempFile->write(item.payloadData());
0116     mTempFile->flush(); // Make sure the data is written to the file before opening up the editor
0117     mTempFile->close();
0118 
0119     /* Using system() because KProcess / QProcess does not behave properly with console commands that expect input from stdin */
0120     int ret = system(QString(editor + " " + mTempFile->fileName()).toLocal8Bit().data());
0121     if (ret != 0) {
0122         emit error(i18nc("@info:shell", "Cannot launch text editor '%1'", editor));
0123         emit finished(RuntimeError);
0124         return;
0125     }
0126 
0127     if (!isDryRun()) {
0128         mTempFile->open();
0129         mTempFile->seek(0); // Seek to the beginning of the file
0130         item.setPayloadFromData(mTempFile->readAll());
0131         ItemModifyJob *modifyJob = new ItemModifyJob(item);
0132         connect(modifyJob, &ItemModifyJob::result, this, &EditCommand::onItemModified);
0133     } else {
0134         onItemModified(job);
0135     }
0136 }
0137 
0138 void EditCommand::onItemModified(KJob *job)
0139 {
0140     if (!checkJobResult(job)) return;
0141     std::cout << i18nc("@info:shell", "Changes to item '%1' have been saved", mItemArg).toLocal8Bit().constData() << std::endl;
0142     emit finished(NoError);
0143 }