From 0bd7e28895eb7a695e3a4cb72d89f9c91af9f3c1 Mon Sep 17 00:00:00 2001 From: Chris K Date: Thu, 21 Jul 2016 21:12:03 -0300 Subject: [PATCH] Introducing a second uploader service: pomf.se clones! --- dialogs/optionsdialog.cpp | 32 ++++- dialogs/optionsdialog.ui | 97 +++++++++++++- lightscreen.pro | 11 +- lightscreenwindow.cpp | 4 +- tools/uploader/imageuploader.cpp | 5 +- tools/uploader/imguruploader.cpp | 3 + tools/uploader/imguruploader.h | 2 +- tools/uploader/pomfuploader.cpp | 121 ++++++++++++++++++ tools/uploader/pomfuploader.h | 27 ++++ tools/uploader/uploader.cpp | 11 ++ tools/uploader/uploader.h | 1 + widgets/imguroptionswidget.cpp | 10 +- widgets/imguroptionswidget.h | 9 +- ...{imguroptions.ui => imguroptionswidget.ui} | 0 widgets/pomfoptionswidget.cpp | 61 +++++++++ widgets/pomfoptionswidget.h | 18 +++ widgets/pomfoptionswidget.ui | 66 ++++++++++ 17 files changed, 455 insertions(+), 23 deletions(-) create mode 100644 tools/uploader/pomfuploader.cpp create mode 100644 tools/uploader/pomfuploader.h rename widgets/{imguroptions.ui => imguroptionswidget.ui} (100%) create mode 100644 widgets/pomfoptionswidget.cpp create mode 100644 widgets/pomfoptionswidget.h create mode 100644 widgets/pomfoptionswidget.ui diff --git a/dialogs/optionsdialog.cpp b/dialogs/optionsdialog.cpp index 781e2f2..2b43515 100644 --- a/dialogs/optionsdialog.cpp +++ b/dialogs/optionsdialog.cpp @@ -281,6 +281,19 @@ void OptionsDialog::loadSettings() settings()->beginGroup("imgur"); ui.imgurOptions->setUser(settings()->value("account_username", "").toString()); settings()->endGroup(); + + settings()->beginGroup("pomf"); + QString pomf_url = settings()->value("pomf_url", "").toString(); + + if (!pomf_url.isEmpty()) { + if (ui.pomfOptions->ui.pomfUrlComboBox->findText(pomf_url, Qt::MatchFixedString) == -1) { + ui.pomfOptions->ui.pomfUrlComboBox->addItem(pomf_url); + } + + ui.pomfOptions->ui.pomfUrlComboBox->setCurrentText(settings()->value("pomf_url", "").toString()); + } + settings()->endGroup(); + settings()->endGroup(); QTimer::singleShot(0, this, &OptionsDialog::updatePreview); @@ -381,8 +394,12 @@ void OptionsDialog::saveSettings() settings()->setValue("service", ui.uploadServiceComboBox->currentIndex()); settings()->beginGroup("imgur"); - settings()->setValue("anonymous", settings()->value("account_username").toString().isEmpty()); - settings()->setValue("album" , ui.imgurOptions->ui.albumComboBox->property("currentData").toString()); + settings()->setValue("anonymous", settings()->value("account_username").toString().isEmpty()); + settings()->setValue("album" , ui.imgurOptions->ui.albumComboBox->property("currentData").toString()); + settings()->endGroup(); + + settings()->beginGroup("pomf"); + settings()->setValue("pomf_url", ui.pomfOptions->ui.pomfUrlComboBox->currentText()); settings()->endGroup(); settings()->endGroup(); @@ -576,6 +593,8 @@ void OptionsDialog::init() // Version ui.versionLabel->setText(tr("Version %1").arg(qApp->applicationVersion())); + ui.uploadSslWarningLabel->setVisible(!QSslSocket::supportsSsl()); + setEnabled(false); // We disable the widgets to prevent any user interaction until the settings have loaded. // @@ -650,12 +669,13 @@ void OptionsDialog::init() } }); - connect(ui.mainLabel , &QLabel::linkActivated, this, &OptionsDialog::openUrl); - connect(ui.licenseAboutLabel, &QLabel::linkActivated, this, &OptionsDialog::openUrl); - connect(ui.linksLabel, &QLabel::linkActivated, this, &OptionsDialog::openUrl); + connect(ui.mainLabel , &QLabel::linkActivated, this, &OptionsDialog::openUrl); + connect(ui.licenseAboutLabel, &QLabel::linkActivated, this, &OptionsDialog::openUrl); + connect(ui.linksLabel, &QLabel::linkActivated, this, &OptionsDialog::openUrl); + connect(ui.uploadSslWarningLabel,&QLabel::linkActivated, this, &OptionsDialog::openUrl); connect(ui.tabWidget, &QTabWidget::currentChanged, [&](int index) { - if (index == 2 && !ui.imgurOptions->mCurrentUser.isEmpty() && ui.imgurOptions->ui.albumComboBox->count() == 1) { + if (index == 2 && ui.uploadServiceStackWidget->currentIndex() == 0 && !ui.imgurOptions->mCurrentUser.isEmpty() && ui.imgurOptions->ui.albumComboBox->count() == 1) { QTimer::singleShot(20, ui.imgurOptions, &ImgurOptionsWidget::requestAlbumList); } }); diff --git a/dialogs/optionsdialog.ui b/dialogs/optionsdialog.ui index f429608..071e5a8 100644 --- a/dialogs/optionsdialog.ui +++ b/dialogs/optionsdialog.ui @@ -6,7 +6,7 @@ 0 0 - 399 + 401 318 @@ -555,6 +555,22 @@ Quality is related to file size and of course to readability and overall quality Upload + + + + + 75 + true + + + + QLabel { padding: 4px; border: 1px solid red; background-color: rgba(248, 182, 182, 60); } + + + WARNING: You have no SSL support, <a href="http://lightscreen.com.ar/help#SSL">click here</a> to learn more. + + + @@ -585,6 +601,11 @@ Quality is related to file size and of course to readability and overall quality Imgur + + + Pomf-clone + + @@ -624,7 +645,55 @@ Quality is related to file size and of course to readability and overall quality 4 - + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + @@ -676,7 +745,7 @@ Quality is related to file size and of course to readability and overall quality 0 0 - 358 + 360 706 @@ -1360,6 +1429,12 @@ Created by <a href="https://ckaiser.com.ar">Christian Kaiser<
widgets/imguroptionswidget.h
1 + + PomfOptionsWidget + QWidget +
widgets/pomfoptionswidget.h
+ 1 +
fileGroupBox @@ -1459,5 +1534,21 @@ Created by <a href="https://ckaiser.com.ar">Christian Kaiser< + + uploadServiceComboBox + currentIndexChanged(int) + uploadServiceStackWidget + setCurrentIndex(int) + + + 92 + 96 + + + 198 + 196 + + + diff --git a/lightscreen.pro b/lightscreen.pro index 85107b6..706c18e 100644 --- a/lightscreen.pro +++ b/lightscreen.pro @@ -16,8 +16,10 @@ HEADERS += dialogs/areadialog.h \ widgets/hotkeywidget.h \ tools/uploader/imageuploader.h \ tools/uploader/imguruploader.h \ + tools/uploader/pomfuploader.h \ tools/uploader/uploader.h \ - widgets/imguroptionswidget.h + widgets/imguroptionswidget.h \ + widgets/pomfoptionswidget.h SOURCES += dialogs/areadialog.cpp \ dialogs/historydialog.cpp \ @@ -35,14 +37,17 @@ SOURCES += dialogs/areadialog.cpp \ widgets/hotkeywidget.cpp \ tools/uploader/imageuploader.cpp \ tools/uploader/imguruploader.cpp \ + tools/uploader/pomfuploader.cpp \ tools/uploader/uploader.cpp \ - widgets/imguroptionswidget.cpp + widgets/imguroptionswidget.cpp \ + widgets/pomfoptionswidget.cpp FORMS += dialogs/historydialog.ui \ dialogs/namingdialog.ui \ dialogs/optionsdialog.ui \ lightscreenwindow.ui \ - widgets/imguroptions.ui + widgets/pomfoptionswidget.ui \ + widgets/imguroptionswidget.ui RESOURCES += lightscreen.qrc CODECFORSRC = UTF-8 diff --git a/lightscreenwindow.cpp b/lightscreenwindow.cpp index 73ad0b0..3a1310e 100644 --- a/lightscreenwindow.cpp +++ b/lightscreenwindow.cpp @@ -548,7 +548,7 @@ void LightscreenWindow::screenshotAction(int mode) options.upload = settings()->value("options/uploadAuto", false).toBool(); options.optimize = settings()->value("options/optipng", false).toBool(); - options.uploadService = settings()->value("options/upload/service", "imgur").toString(); + options.uploadService = Uploader::serviceName(settings()->value("upload/service", 0).toInt()); Screenshot::NamingOptions namingOptions; namingOptions.naming = (Screenshot::Naming) settings()->value("file/naming").toInt(); @@ -780,7 +780,7 @@ void LightscreenWindow::updaterDone(bool result) void LightscreenWindow::upload(const QString &fileName) { - Uploader::instance()->upload(fileName, settings()->value("options/upload/service", "imgur").toString()); + Uploader::instance()->upload(fileName, Uploader::serviceName(settings()->value("upload/service", 0).toInt())); } void LightscreenWindow::uploadCancel() diff --git a/tools/uploader/imageuploader.cpp b/tools/uploader/imageuploader.cpp index 57d2eff..887b9e6 100644 --- a/tools/uploader/imageuploader.cpp +++ b/tools/uploader/imageuploader.cpp @@ -1,5 +1,6 @@ #include "imageuploader.h" #include "imguruploader.h" +#include "pomfuploader.h" #include #include "../screenshotmanager.h" @@ -7,7 +8,9 @@ ImageUploader *ImageUploader::factory(const QString &name) { if (name == "imgur") { - return new ImgurUploader(0); + return new ImgurUploader; + } else if (name == "pomf") { + return new PomfUploader; } return 0; diff --git a/tools/uploader/imguruploader.cpp b/tools/uploader/imguruploader.cpp index b82c6a6..5c839ca 100644 --- a/tools/uploader/imguruploader.cpp +++ b/tools/uploader/imguruploader.cpp @@ -105,6 +105,7 @@ void ImgurUploader::upload(const QString &fileName) QNetworkReply *reply = Uploader::instance()->nam()->post(request, multiPart); reply->setProperty("fileName", fileName); this->setProperty("fileName", fileName); + multiPart->setParent(reply); connect(reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(uploadProgress(qint64, qint64))); connect(this , SIGNAL(cancelRequest()), reply, SLOT(abort())); @@ -177,6 +178,8 @@ void ImgurUploader::uploadProgress(qint64 bytesReceived, qint64 bytesTotal) void ImgurUploader::authorizationReply(QNetworkReply *reply, AuthorizationCallback callback) { + reply->deleteLater(); + connect(reply, &QNetworkReply::finished, [reply, callback] { bool authorized = false; diff --git a/tools/uploader/imguruploader.h b/tools/uploader/imguruploader.h index 82e916c..4f9dd59 100644 --- a/tools/uploader/imguruploader.h +++ b/tools/uploader/imguruploader.h @@ -14,7 +14,7 @@ class ImgurUploader : public ImageUploader public: typedef std::function AuthorizationCallback; - ImgurUploader(QObject *parent); + ImgurUploader(QObject *parent = 0); static const QString clientId(); static const QString clientSecret(); static void authorize(const QString &pin, AuthorizationCallback callback); diff --git a/tools/uploader/pomfuploader.cpp b/tools/uploader/pomfuploader.cpp new file mode 100644 index 0000000..59d49d0 --- /dev/null +++ b/tools/uploader/pomfuploader.cpp @@ -0,0 +1,121 @@ +#include "pomfuploader.h" +#include "../uploader/uploader.h" + +#include +#include +#include + +PomfUploader::PomfUploader(QObject *parent) : ImageUploader(parent) +{ + mUploaderType = "pomf"; + loadSettings(); +} + +void PomfUploader::verify(const QString &url, VerificationCallback callback) +{ + QNetworkRequest request(QUrl::fromUserInput(QString("%1/upload.php").arg(url))); + + if (!request.url().isValid()) { + callback(false); + } + + QNetworkReply *reply = Uploader::instance()->nam()->get(request); + + connect(reply, &QNetworkReply::finished, [reply, callback] { + reply->deleteLater(); + + const QJsonObject pomfResponse = QJsonDocument::fromJson(reply->readAll()).object(); + + if (!pomfResponse.isEmpty() && pomfResponse.contains("success")) { + callback(true); + } else { + callback(false); + } + }); +} + +void PomfUploader::upload(const QString &fileName) +{ + QString pomfUrl = mSettings["pomf_url"].toString(); + + if (pomfUrl.isEmpty()) { + emit error(ImageUploader::HostError, tr("Invalid pomf uploader URL!"), fileName); + return; + } + + QUrl url = QUrl::fromUserInput(pomfUrl + "/upload.php"); + + QFile *file = new QFile(fileName); + + if (!file->open(QIODevice::ReadOnly)) { + emit error(ImageUploader::FileError, tr("Unable to read screenshot file"), fileName); + file->deleteLater(); + return; + } + + QNetworkRequest request(url); + + QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + QHttpPart imagePart; + imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QMimeDatabase().mimeTypeForFile(fileName, QMimeDatabase::MatchExtension).name()); + imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QString("form-data; name=\"files[]\"; filename=\"%1\"").arg(QFileInfo(fileName).fileName())); + imagePart.setBodyDevice(file); + + file->setParent(multiPart); + multiPart->append(imagePart); + + QNetworkReply *reply = Uploader::instance()->nam()->post(request, multiPart); + this->setProperty("fileName", fileName); + multiPart->setParent(reply); + + connect(this , &PomfUploader::cancelRequest, reply, &QNetworkReply::abort); + connect(this , &PomfUploader::cancelRequest, reply, &QNetworkReply::deleteLater); + + connect(reply, &QNetworkReply::uploadProgress, this, [&](qint64 bytesSent, qint64 bytesTotal) { + float b = (float) bytesSent / bytesTotal; + int p = qRound(b * 100); + setProgress(p); + }); + + connect(reply, &QNetworkReply::finished, this, [&, reply, fileName] { + const QJsonObject pomfResponse = QJsonDocument::fromJson(reply->readAll()).object(); + + if (reply->error() != QNetworkReply::NoError && pomfResponse.isEmpty()) { + emit error(ImageUploader::NetworkError, tr("Error reaching uploader"), fileName); + return; + } + + if (!pomfResponse.contains("success") || !pomfResponse.contains("files")) { + emit error(ImageUploader::HostError, tr("Invalid response from uploader"), fileName); + return; + } + + if (pomfResponse["success"].toBool()) { + emit uploaded(fileName, pomfResponse["files"].toArray().at(0).toObject()["url"].toString(), ""); + } else { + QString description; + + if (pomfResponse.contains("description")) { + description = pomfResponse["description"].toString(); + } + + if (description.isEmpty()) { + description = tr("Host error"); + } + + emit error(ImageUploader::HostError, description, fileName); + } + }); +} + +void PomfUploader::retry() +{ + upload(property("fileName").toString()); +} + +void PomfUploader::cancel() +{ + emit cancelRequest(); +} + diff --git a/tools/uploader/pomfuploader.h b/tools/uploader/pomfuploader.h new file mode 100644 index 0000000..5b746e1 --- /dev/null +++ b/tools/uploader/pomfuploader.h @@ -0,0 +1,27 @@ +#ifndef POMFUPLOADER_H +#define POMFUPLOADER_H + +#include "imageuploader.h" +#include + +class PomfUploader : public ImageUploader +{ + Q_OBJECT + +public: + PomfUploader(QObject *parent = 0); + + typedef std::function VerificationCallback; + static void verify(const QString &url, VerificationCallback callback); + +public slots: + void upload(const QString &fileName); + void retry(); + void cancel(); + +signals: + void cancelRequest(); + +}; + +#endif // POMFUPLOADER_H diff --git a/tools/uploader/uploader.cpp b/tools/uploader/uploader.cpp index 506d1be..8332cc3 100644 --- a/tools/uploader/uploader.cpp +++ b/tools/uploader/uploader.cpp @@ -49,6 +49,17 @@ QNetworkAccessManager *Uploader::nam() return mNetworkAccessManager; } +QString Uploader::serviceName(int index) +{ // TODO: Move somewhere else? Use indexes everywhere? an enum? + switch (index) { + case 1: + return "pomf"; + case 0: + default: + return "imgur"; + } +} + QString Uploader::lastUrl() const { return mLastUrl; diff --git a/tools/uploader/uploader.h b/tools/uploader/uploader.h index 3c001f2..0951136 100644 --- a/tools/uploader/uploader.h +++ b/tools/uploader/uploader.h @@ -33,6 +33,7 @@ class Uploader : public QObject QString lastUrl() const; int progress() const; QNetworkAccessManager *nam(); + static QString serviceName(int index); public slots: void cancel(); diff --git a/widgets/imguroptionswidget.cpp b/widgets/imguroptionswidget.cpp index 8b48274..6520c95 100644 --- a/widgets/imguroptionswidget.cpp +++ b/widgets/imguroptionswidget.cpp @@ -71,7 +71,10 @@ void ImgurOptionsWidget::authorize() ui.authButton->setText(tr("Authorizing..")); ui.authButton->setEnabled(false); - ImgurUploader::authorize(pin, [&](bool result) { + QPointer guard(parentWidget()); + ImgurUploader::authorize(pin, [&, guard](bool result) { + if (guard.isNull()) return; + ui.authButton->setEnabled(true); if (result) { @@ -100,9 +103,10 @@ void ImgurOptionsWidget::requestAlbumList() request.setRawHeader("Authorization", QByteArray("Bearer ") + settings()->value("upload/imgur/access_token").toByteArray()); QNetworkReply *reply = Uploader::instance()->nam()->get(request); + QPointer guard(parentWidget()); - connect(reply, &QNetworkReply::finished, this, [&, reply] { - if (mCurrentUser.isEmpty()) return; + connect(reply, &QNetworkReply::finished, this, [&, guard, reply] { + if (mCurrentUser.isEmpty() || guard.isNull()) return; if (reply->error() != QNetworkReply::NoError) { diff --git a/widgets/imguroptionswidget.h b/widgets/imguroptionswidget.h index 1c3fed9..bea88f0 100644 --- a/widgets/imguroptionswidget.h +++ b/widgets/imguroptionswidget.h @@ -1,8 +1,9 @@ -#ifndef IMGUROPTIONS_H -#define IMGUROPTIONS_H +#ifndef IMGUROPTIONSWIDGET_H +#define IMGUROPTIONSWIDGET_H #include -#include "ui_imguroptions.h" + +#include "ui_imguroptionswidget.h" class QSettings; class ImgurOptionsWidget : public QWidget @@ -23,4 +24,4 @@ private slots: friend class OptionsDialog; }; -#endif // IMGUROPTIONS_H +#endif // IMGUROPTIONSWIDGET_H diff --git a/widgets/imguroptions.ui b/widgets/imguroptionswidget.ui similarity index 100% rename from widgets/imguroptions.ui rename to widgets/imguroptionswidget.ui diff --git a/widgets/pomfoptionswidget.cpp b/widgets/pomfoptionswidget.cpp new file mode 100644 index 0000000..83c58ab --- /dev/null +++ b/widgets/pomfoptionswidget.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +#include +#include "pomfoptionswidget.h" +#include "../uploader/uploader.h" +#include "../uploader/pomfuploader.h" + +#include "../screenshotmanager.h" +#include "../os.h" + +PomfOptionsWidget::PomfOptionsWidget(QWidget *parent) : QWidget(parent) +{ + ui.setupUi(this); + + connect(ui.verifyButton, &QPushButton::clicked, this, [&]() { + ui.verifyButton->setEnabled(false); + ui.pomfUrlComboBox->setEnabled(false); + + QPointer guard(parentWidget()); + PomfUploader::verify(ui.pomfUrlComboBox->currentText(), [&, guard](bool result) { + if (guard.isNull()) return; + + ui.verifyButton->setEnabled(true); + ui.pomfUrlComboBox->setEnabled(true); + + if (result) { + ui.verifyButton->setText(tr("Valid uploader!")); + ui.verifyButton->setStyleSheet("color: green;"); + ui.verifyButton->setIcon(os::icon("yes")); + } else { + ui.verifyButton->setStyleSheet("color: red;"); + ui.verifyButton->setIcon(os::icon("no")); + ui.verifyButton->setText(tr("Invalid :(")); + } + }); + }); + + connect(ui.pomfUrlComboBox, &QComboBox::currentTextChanged, [&](const QString &text) { + bool validUrl = false; + if (text.startsWith("http://") || text.startsWith("https://")) { // TODO: Something a bit more complex + validUrl = true; + } + + ui.visitButton->setEnabled(validUrl); + ui.verifyButton->setEnabled(validUrl); + + if (ui.verifyButton->styleSheet().count() > 0) { + ui.verifyButton->setStyleSheet(""); + ui.verifyButton->setIcon(QIcon()); + ui.verifyButton->setText(tr("Verify")); + } + }); + + connect(ui.visitButton, &QPushButton::clicked, this, [&]() { + QDesktopServices::openUrl(ui.pomfUrlComboBox->currentText()); + }); +} diff --git a/widgets/pomfoptionswidget.h b/widgets/pomfoptionswidget.h new file mode 100644 index 0000000..38d13c8 --- /dev/null +++ b/widgets/pomfoptionswidget.h @@ -0,0 +1,18 @@ +#ifndef POMFOPTIONSWIDGET_H +#define POMFOPTIONSWIDGET_H + +#include +#include "ui_pomfoptionswidget.h" + +class PomfOptionsWidget : public QWidget +{ + Q_OBJECT +public: + explicit PomfOptionsWidget(QWidget *parent = 0); + +private: + Ui::PomfOptions ui; + friend class OptionsDialog; +}; + +#endif // POMFOPTIONSWIDGET_H diff --git a/widgets/pomfoptionswidget.ui b/widgets/pomfoptionswidget.ui new file mode 100644 index 0000000..2956498 --- /dev/null +++ b/widgets/pomfoptionswidget.ui @@ -0,0 +1,66 @@ + + + PomfOptions + + + + 0 + 0 + 305 + 75 + + + + Form + + + + + + Pomf Clone URL: + + + + + + + true + + + + + + + + + Visit Site + + + + + + + Verify + + + + + + + + + Qt::Vertical + + + + 198 + 97 + + + + + + + + +