Purpose

imgurplugin.cpp
1/*
2 SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
3
4 SPDX-License-Identifier: LGPL-2.1-or-later
5*/
6
7#include "mpform.h"
8#include <KIO/StoredTransferJob>
9#include <KIO/TransferJob>
10#include <KJob>
11#include <KLocalizedString>
12#include <KNotification>
13#include <KPluginFactory>
14#include <QClipboard>
15#include <QDebug>
16#include <QGuiApplication>
17#include <QJsonArray>
18#include <QJsonDocument>
19#include <QJsonObject>
20#include <QStandardPaths>
21#include <purpose/pluginbase.h>
22
23Q_GLOBAL_STATIC_WITH_ARGS(const QUrl, imageImgurUrl, (QLatin1String("https://api.imgur.com/3/image")))
24Q_GLOBAL_STATIC_WITH_ARGS(const QUrl, albumImgurUrl, (QLatin1String("https://api.imgur.com/3/album")))
25
26// key associated with aleixpol@kde.org
27Q_GLOBAL_STATIC_WITH_ARGS(const QString, YOUR_CLIENT_ID, (QLatin1String("0bffa5b4ac8383c")))
28
29class ImgurShareJob : public Purpose::Job
30{
31 Q_OBJECT
32public:
33 explicit ImgurShareJob(QObject *parent)
34 : Purpose::Job(parent)
35 , m_pendingJobs(0)
36 {
37 }
38
39 void start() override
40 {
41 m_pendingJobs = 0;
42 const QJsonArray urls = data().value(QLatin1String("urls")).toArray();
43 if (urls.isEmpty()) {
44 qWarning() << "no urls to share" << urls << data();
45 emitResult();
46 return;
47 }
48
49 if (urls.count() > 1) {
51 tJob->setMetaData(QMap<QString, QString>{{QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: Client-ID ") + *YOUR_CLIENT_ID}});
52 connect(tJob, &KJob::result, this, &ImgurShareJob::albumCreated);
53 } else {
54 startUploading();
55 }
56 }
57
58 QJsonObject processResponse(KJob *job)
59 {
60 KIO::StoredTransferJob *sjob = qobject_cast<KIO::StoredTransferJob *>(job);
62 const QJsonObject resultMap = QJsonDocument::fromJson(sjob->data(), &error).object();
63
64 if (job->error() || !resultMap.value(QLatin1String("success")).toBool()) {
65 setError(2);
66 if (resultMap.contains(QLatin1String("data"))) {
67 const QJsonObject dataMap = resultMap[QLatin1String("data")].toObject();
68 setErrorText(i18n("Upload failed: %1", dataMap[QLatin1String("error")].toString()));
69 } else {
70 setErrorText(i18n("Upload failed: %1", error.errorString()));
71 }
72 } else {
73 return resultMap[QLatin1String("data")].toObject();
74 }
75 emitResult();
76 return {};
77 }
78
79 void albumCreated(KJob *job)
80 {
81 const QJsonObject dataMap = processResponse(job);
82 if (!dataMap.isEmpty()) {
83 m_albumId = dataMap[QLatin1String("id")].toString();
84 m_albumDeleteHash = dataMap[QLatin1String("deletehash")].toString();
85 startUploading();
86 }
87 }
88
89 void startUploading()
90 {
91 Q_EMIT infoMessage(this, i18n("Uploading files to imgur..."));
92 const QJsonArray urls = data().value(QLatin1String("urls")).toArray();
93 for (const QJsonValue &val : urls) {
94 QString u = val.toString();
96 connect(job, &KJob::finished, this, &ImgurShareJob::fileFetched);
97 m_pendingJobs++;
98 }
99 }
100
101 void fileFetched(KJob *j)
102 {
103 if (j->error()) {
104 setError(j->error());
105 setErrorText(j->errorText());
106 emitResult();
107
108 qDebug() << "error:" << j->errorText() << j->errorString();
109
110 return;
111 }
112
113 MPForm form;
114 KIO::StoredTransferJob *job = qobject_cast<KIO::StoredTransferJob *>(j);
115 form.addFile(QStringLiteral("image"), job->url(), job->data());
116 form.addPair(QStringLiteral("album"), m_albumDeleteHash, {});
117 form.finish();
118
119 KIO::StoredTransferJob *tJob = KIO::storedHttpPost(form.formData(), *imageImgurUrl, KIO::HideProgressInfo);
121 {QStringLiteral("content-type"), QString::fromLocal8Bit(form.contentType())},
122 {QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: Client-ID ") + *YOUR_CLIENT_ID},
123 });
124 connect(tJob, &KJob::result, this, &ImgurShareJob::imageUploaded);
125 }
126
127 void imageUploaded(KJob *job)
128 {
129 const QJsonObject dataMap = processResponse(job);
130 if (!dataMap.isEmpty()) {
131 const QString url = dataMap[QStringLiteral("link")].toString();
132 Q_EMIT infoMessage(this, url);
133 const QString deletehash = dataMap[QStringLiteral("deletehash")].toString();
134 Q_EMIT infoMessage(this, deletehash);
135 --m_pendingJobs;
136
137 if (m_pendingJobs == 0) {
138 const QString finalUrl = m_albumId.isEmpty() ? url : QStringLiteral("https://imgur.com/a/") + m_albumId;
139 const QString deleteUrl = QStringLiteral("https://imgur.com/delete/") + deletehash;
140
142 KNotification::event(KNotification::Notification,
143 i18n("Imgur Upload"),
144 i18n("The shared image link (<a href=\"%1\">%1</a>) has been copied to the clipboard.<br><br>If you would like to remove "
145 "the uploaded image, visit <a href=\"%2\">%2</a>",
146 finalUrl,
147 deleteUrl),
149
150 emitResult();
151 }
152 }
153 }
154
155private:
156 QString m_albumId;
157 QString m_albumDeleteHash;
158 int m_pendingJobs;
159};
160
161class ImgurPlugin : public Purpose::PluginBase
162{
164public:
165 using PluginBase::PluginBase;
166 Purpose::Job *createJob() const override
167 {
168 return new ImgurShareJob(nullptr);
169 }
170};
171
172K_PLUGIN_CLASS_WITH_JSON(ImgurPlugin, "imgurplugin.json")
173
174#include "imgurplugin.moc"
void setMetaData(const KIO::MetaData &metaData)
const QUrl & url() const
QByteArray data() const
virtual QString errorString() const
int error() const
void result(KJob *job)
void finished(KJob *job)
QString errorText() const
static KNotification * event(const QString &eventId, const QString &text=QString(), const QPixmap &pixmap=QPixmap(), const NotificationFlags &flags=CloseOnTimeout, const QString &componentName=QString())
#define K_PLUGIN_CLASS_WITH_JSON(classname, jsonFile)
Job that will actually perform the sharing.
Definition job.h:34
Base class to implement by plugins.
Definition pluginbase.h:29
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
KIOCORE_EXPORT StoredTransferJob * storedGet(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
KIOCORE_EXPORT StoredTransferJob * storedHttpPost(const QByteArray &arr, const QUrl &url, JobFlags flags=DefaultFlags)
HideProgressInfo
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
void setText(const QString &text, Mode mode)
QClipboard * clipboard()
qsizetype count() const const
bool isEmpty() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
bool contains(QLatin1StringView key) const const
bool isEmpty() const const
QJsonValue value(QLatin1StringView key) const const
Q_OBJECTQ_OBJECT
QString fromLocal8Bit(QByteArrayView str)
bool isEmpty() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:03:06 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.