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 if (job->error()) {
64 setError(job->error());
65 setErrorText(job->errorText());
66 } else if (error.error) {
67 setError(1);
68 setErrorText(error.errorString());
69 } else if (!resultMap.value(QLatin1String("success")).toBool()) {
70 setError(2);
71 const QJsonObject dataMap = resultMap[QLatin1String("data")].toObject();
72 setErrorText(dataMap[QLatin1String("error")].toString());
73 } else {
74 return resultMap[QLatin1String("data")].toObject();
75 }
76 emitResult();
77 return {};
78 }
79
80 void albumCreated(KJob *job)
81 {
82 const QJsonObject dataMap = processResponse(job);
83 if (!dataMap.isEmpty()) {
84 m_albumId = dataMap[QLatin1String("id")].toString();
85 m_albumDeleteHash = dataMap[QLatin1String("deletehash")].toString();
86 startUploading();
87 }
88 }
89
90 void startUploading()
91 {
92 Q_EMIT infoMessage(this, i18n("Uploading files to imgur..."));
93 const QJsonArray urls = data().value(QLatin1String("urls")).toArray();
94 for (const QJsonValue &val : urls) {
95 QString u = val.toString();
97 connect(job, &KJob::finished, this, &ImgurShareJob::fileFetched);
98 m_pendingJobs++;
99 }
100 }
101
102 void fileFetched(KJob *j)
103 {
104 if (j->error()) {
105 setError(j->error());
106 setErrorText(j->errorText());
107 emitResult();
108
109 qDebug() << "error:" << j->errorText() << j->errorString();
110
111 return;
112 }
113
114 MPForm form;
115 KIO::StoredTransferJob *job = qobject_cast<KIO::StoredTransferJob *>(j);
116 form.addFile(QStringLiteral("image"), job->url(), job->data());
117 form.addPair(QStringLiteral("album"), m_albumDeleteHash, {});
118 form.finish();
119
120 KIO::StoredTransferJob *tJob = KIO::storedHttpPost(form.formData(), *imageImgurUrl, KIO::HideProgressInfo);
122 {QStringLiteral("content-type"), QString::fromLocal8Bit(form.contentType())},
123 {QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: Client-ID ") + *YOUR_CLIENT_ID},
124 });
125 connect(tJob, &KJob::result, this, &ImgurShareJob::imageUploaded);
126 }
127
128 void imageUploaded(KJob *job)
129 {
130 const QJsonObject dataMap = processResponse(job);
131 if (!dataMap.isEmpty()) {
132 const QString url = dataMap[QStringLiteral("link")].toString();
133 Q_EMIT infoMessage(this, url);
134 const QString deletehash = dataMap[QStringLiteral("deletehash")].toString();
135 Q_EMIT infoMessage(this, deletehash);
136 --m_pendingJobs;
137
138 if (m_pendingJobs == 0) {
139 const QString finalUrl = m_albumId.isEmpty() ? url : QStringLiteral("https://imgur.com/a/") + m_albumId;
140 const QString deleteUrl = QStringLiteral("https://imgur.com/delete/") + deletehash;
141
143 KNotification::event(KNotification::Notification,
144 i18n("Imgur Upload"),
145 i18n("The shared image link (<a href=\"%1\">%1</a>) has been copied to the clipboard.<br><br>If you would like to remove "
146 "the uploaded image, visit <a href=\"%2\">%2</a>",
147 finalUrl,
148 deleteUrl),
150
151 emitResult();
152 }
153 }
154 }
155
156private:
157 QString m_albumId;
158 QString m_albumDeleteHash;
159 int m_pendingJobs;
160};
161
162class ImgurPlugin : public Purpose::PluginBase
163{
165public:
166 using PluginBase::PluginBase;
167 Purpose::Job *createJob() const override
168 {
169 return new ImgurShareJob(nullptr);
170 }
171};
172
173K_PLUGIN_CLASS_WITH_JSON(ImgurPlugin, "imgurplugin.json")
174
175#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 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 Fri Nov 22 2024 12:08:47 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.