[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [choqok/mastodon] microblogs: Add Mastodon support
From: Andrea Scarpino <null () kde ! org>
Date: 2017-07-31 18:27:44
Message-ID: E1dcFPs-0006oe-Nu () code ! kde ! org
[Download RAW message or body]
Git commit 19c529bdaa74672582fd3e77dbf60c6ceb6f0a03 by Andrea Scarpino.
Committed on 31/07/2017 at 18:27.
Pushed by scarpino into branch 'mastodon'.
Add Mastodon support
M +1 -0 microblogs/CMakeLists.txt
A +41 -0 microblogs/mastodon/CMakeLists.txt
A +18 -0 microblogs/mastodon/choqok_mastodon.desktop
A +- -- microblogs/mastodon/icons/128-apps-mastodon_microblog.png
A +- -- microblogs/mastodon/icons/16-apps-mastodon_microblog.png
A +- -- microblogs/mastodon/icons/22-apps-mastodon_microblog.png
A +- -- microblogs/mastodon/icons/32-apps-mastodon_microblog.png
A +- -- microblogs/mastodon/icons/48-apps-mastodon_microblog.png
A +- -- microblogs/mastodon/icons/64-apps-mastodon_microblog.png
A +8 -0 microblogs/mastodon/icons/CMakeLists.txt
A +130 -0 microblogs/mastodon/mastodonaccount.cpp [License: GPL (v2/3)]
A +65 -0 microblogs/mastodon/mastodonaccount.h [License: GPL (v2/3)]
A +22 -0 microblogs/mastodon/mastodondebug.cpp [License: LGPL (v2+)]
A +27 -0 microblogs/mastodon/mastodondebug.h [License: LGPL (v2+)]
A +221 -0 microblogs/mastodon/mastodoneditaccountwidget.cpp [License: GPL \
(v2/3)] A +61 -0 microblogs/mastodon/mastodoneditaccountwidget.h [License: \
GPL (v2/3)] A +225 -0 microblogs/mastodon/mastodoneditaccountwidget.ui
A +745 -0 microblogs/mastodon/mastodonmicroblog.cpp [License: GPL (v2/3)]
A +120 -0 microblogs/mastodon/mastodonmicroblog.h [License: GPL (v2/3)]
A +65 -0 microblogs/mastodon/mastodonoauth.cpp [License: GPL (v2/3)]
A +55 -0 microblogs/mastodon/mastodonoauth.h [License: GPL (v2/3)]
A +38 -0 microblogs/mastodon/mastodonoauthreplyhandler.cpp [License: GPL \
(v2/3)] A +39 -0 microblogs/mastodon/mastodonoauthreplyhandler.h [License: \
GPL (v2/3)] A +31 -0 microblogs/mastodon/mastodonpost.cpp [License: GPL \
(v2/3)] A +38 -0 microblogs/mastodon/mastodonpost.h [License: GPL (v2/3)]
A +138 -0 microblogs/mastodon/mastodonpostwidget.cpp [License: GPL (v2/3)]
A +56 -0 microblogs/mastodon/mastodonpostwidget.h [License: GPL (v2/3)]
https://commits.kde.org/choqok/19c529bdaa74672582fd3e77dbf60c6ceb6f0a03
diff --git a/microblogs/CMakeLists.txt b/microblogs/CMakeLists.txt
index fbf153c8..2b4d3750 100644
--- a/microblogs/CMakeLists.txt
+++ b/microblogs/CMakeLists.txt
@@ -1,6 +1,7 @@
find_package(KF5Attica)
add_subdirectory(twitter)
+add_subdirectory(mastodon)
add_subdirectory(laconica)
add_subdirectory(friendica)
add_subdirectory(pumpio)
diff --git a/microblogs/mastodon/CMakeLists.txt b/microblogs/mastodon/CMakeLists.txt
new file mode 100644
index 00000000..5b6e6139
--- /dev/null
+++ b/microblogs/mastodon/CMakeLists.txt
@@ -0,0 +1,41 @@
+include_directories(
+ ${CHOQOK_INCLUDES}
+)
+
+set(choqok_mastodon_SRCS
+ mastodonaccount.cpp
+ mastodondebug.cpp
+ mastodoneditaccountwidget.cpp
+ mastodonmicroblog.cpp
+ mastodonoauth.cpp
+ mastodonoauthreplyhandler.cpp
+ mastodonpost.cpp
+ mastodonpostwidget.cpp
+)
+
+ki18n_wrap_ui(choqok_mastodon_SRCS
+ mastodoneditaccountwidget.ui
+)
+
+add_library(choqok_mastodon MODULE ${choqok_mastodon_SRCS})
+
+kcoreaddons_desktop_to_json(choqok_mastodon choqok_mastodon.desktop)
+
+target_link_libraries(choqok_mastodon
+PUBLIC
+ Qt5::Core
+ Qt5::Gui
+ Qt5::NetworkAuth
+ Qt5::Widgets
+ KF5::I18n
+ KF5::KIOCore
+ KF5::KIOWidgets
+ KF5::WidgetsAddons
+ qca-qt5
+ choqok
+)
+
+install(TARGETS choqok_mastodon DESTINATION ${PLUGIN_INSTALL_DIR})
+install(FILES choqok_mastodon.desktop DESTINATION ${SERVICES_INSTALL_DIR})
+
+add_subdirectory(icons)
diff --git a/microblogs/mastodon/choqok_mastodon.desktop \
b/microblogs/mastodon/choqok_mastodon.desktop new file mode 100644
index 00000000..341a5f61
--- /dev/null
+++ b/microblogs/mastodon/choqok_mastodon.desktop
@@ -0,0 +1,18 @@
+[Desktop Entry]
+Encoding=UTF-8
+Type=Service
+X-Choqok-Version=1
+Icon=mastodon_microblog
+ServiceTypes=Choqok/Plugin
+X-KDE-Library=choqok_mastodon
+X-KDE-PluginInfo-Author=Andrea Scarpino
+X-KDE-PluginInfo-Email=scarpino@kde.org
+X-KDE-PluginInfo-Name=choqok_mastodon
+X-KDE-PluginInfo-Version=0.1
+X-KDE-PluginInfo-Website=http://choqok.gnufolks.org
+X-KDE-PluginInfo-Category=MicroBlogs
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=Mastodon social
+Comment=Mastodon social
diff --git a/microblogs/mastodon/icons/128-apps-mastodon_microblog.png \
b/microblogs/mastodon/icons/128-apps-mastodon_microblog.png new file mode 100644
index 00000000..2058c3db
Binary files /dev/null and \
b/microblogs/mastodon/icons/128-apps-mastodon_microblog.png differ
diff --git a/microblogs/mastodon/icons/16-apps-mastodon_microblog.png \
b/microblogs/mastodon/icons/16-apps-mastodon_microblog.png new file mode 100644
index 00000000..6e14a7d3
Binary files /dev/null and b/microblogs/mastodon/icons/16-apps-mastodon_microblog.png \
differ
diff --git a/microblogs/mastodon/icons/22-apps-mastodon_microblog.png \
b/microblogs/mastodon/icons/22-apps-mastodon_microblog.png new file mode 100644
index 00000000..205ed9b5
Binary files /dev/null and b/microblogs/mastodon/icons/22-apps-mastodon_microblog.png \
differ
diff --git a/microblogs/mastodon/icons/32-apps-mastodon_microblog.png \
b/microblogs/mastodon/icons/32-apps-mastodon_microblog.png new file mode 100644
index 00000000..abc8b077
Binary files /dev/null and b/microblogs/mastodon/icons/32-apps-mastodon_microblog.png \
differ
diff --git a/microblogs/mastodon/icons/48-apps-mastodon_microblog.png \
b/microblogs/mastodon/icons/48-apps-mastodon_microblog.png new file mode 100644
index 00000000..7bf4a7a7
Binary files /dev/null and b/microblogs/mastodon/icons/48-apps-mastodon_microblog.png \
differ
diff --git a/microblogs/mastodon/icons/64-apps-mastodon_microblog.png \
b/microblogs/mastodon/icons/64-apps-mastodon_microblog.png new file mode 100644
index 00000000..20b52395
Binary files /dev/null and b/microblogs/mastodon/icons/64-apps-mastodon_microblog.png \
differ
diff --git a/microblogs/mastodon/icons/CMakeLists.txt \
b/microblogs/mastodon/icons/CMakeLists.txt new file mode 100644
index 00000000..54b3eafe
--- /dev/null
+++ b/microblogs/mastodon/icons/CMakeLists.txt
@@ -0,0 +1,8 @@
+ecm_install_icons( ICONS
+ 16-apps-mastodon_microblog.png
+ 22-apps-mastodon_microblog.png
+ 32-apps-mastodon_microblog.png
+ 48-apps-mastodon_microblog.png
+ 64-apps-mastodon_microblog.png
+ 128-apps-mastodon_microblog.png
+ DESTINATION ${ICON_INSTALL_DIR} )
diff --git a/microblogs/mastodon/mastodonaccount.cpp \
b/microblogs/mastodon/mastodonaccount.cpp new file mode 100644
index 00000000..e17c69f8
--- /dev/null
+++ b/microblogs/mastodon/mastodonaccount.cpp
@@ -0,0 +1,130 @@
+/*
+ This file is part of Choqok, the KDE micro-blogging client
+
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 of the license.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see http://www.gnu.org/licenses/
+*/
+
+#include "mastodonaccount.h"
+
+#include <KIO/AccessManager>
+
+#include "passwordmanager.h"
+
+#include "mastodonmicroblog.h"
+
+class MastodonAccount::Private
+{
+public:
+ QString consumerKey;
+ QString consumerSecret;
+ QString host;
+ QString acct;
+ QString tokenSecret;
+ QStringList following;
+ QVariantList lists;
+ MastodonOAuth *oAuth;
+ QStringList timelineNames;
+};
+
+MastodonAccount::MastodonAccount(MastodonMicroBlog *parent, const QString &alias):
+ Account(parent, alias), d(new Private)
+{
+ d->host = configGroup()->readEntry("Host", QString());
+ d->acct = configGroup()->readEntry("Acct", QString());
+ d->tokenSecret = \
Choqok::PasswordManager::self()->readPassword(QStringLiteral("%1_tokenSecret").arg(alias));
+ d->consumerKey = configGroup()->readEntry("ConsumerKey", QString());
+ d->consumerSecret = \
Choqok::PasswordManager::self()->readPassword(QStringLiteral("%1_consumerSecret").arg(alias));
+ d->oAuth = new MastodonOAuth(this);
+ d->oAuth->setToken(d->tokenSecret);
+
+ setPostCharLimit(500);
+}
+
+MastodonAccount::~MastodonAccount()
+{
+ d->oAuth->deleteLater();
+ delete d;
+}
+
+void MastodonAccount::writeConfig()
+{
+ configGroup()->writeEntry("Host", d->host);
+ configGroup()->writeEntry("Acct", d->acct);
+ configGroup()->writeEntry("ConsumerKey", d->consumerKey);
+ Choqok::PasswordManager::self()->writePassword(QStringLiteral("%1_consumerSecret").arg(alias()),
+ d->consumerSecret);
+ Choqok::PasswordManager::self()->writePassword(QStringLiteral("%1_tokenSecret").arg(alias()),
+ d->tokenSecret);
+ Choqok::Account::writeConfig();
+}
+
+QString MastodonAccount::host()
+{
+ return d->host;
+}
+
+void MastodonAccount::setHost(const QString &host)
+{
+ d->host = host;
+}
+
+QString MastodonAccount::acct()
+{
+ return d->acct;
+}
+
+void MastodonAccount::setAcct(const QString &acct)
+{
+ d->acct = acct;
+}
+
+QString MastodonAccount::consumerKey()
+{
+ return d->consumerKey;
+}
+
+void MastodonAccount::setConsumerKey(const QString &consumerKey)
+{
+ d->consumerKey = consumerKey;
+}
+
+QString MastodonAccount::consumerSecret()
+{
+ return d->consumerSecret;
+}
+
+void MastodonAccount::setConsumerSecret(const QString &consumerSecret)
+{
+ d->consumerSecret = consumerSecret;
+}
+
+QString MastodonAccount::tokenSecret()
+{
+ return d->tokenSecret;
+}
+
+void MastodonAccount::setTokenSecret(const QString &tokenSecret)
+{
+ d->tokenSecret = tokenSecret;
+}
+
+MastodonOAuth *MastodonAccount::oAuth()
+{
+ return d->oAuth;
+}
diff --git a/microblogs/mastodon/mastodonaccount.h \
b/microblogs/mastodon/mastodonaccount.h new file mode 100644
index 00000000..05dea4eb
--- /dev/null
+++ b/microblogs/mastodon/mastodonaccount.h
@@ -0,0 +1,65 @@
+/*
+ This file is part of Choqok, the KDE micro-blogging client
+
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 of the license.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see http://www.gnu.org/licenses/
+*/
+
+#ifndef MASTODONACCOUNT_H
+#define MASTODONACCOUNT_H
+
+#include "account.h"
+#include "choqoktypes.h"
+
+#include "mastodonoauth.h"
+
+class MastodonMicroBlog;
+
+class MastodonAccount : public Choqok::Account
+{
+ Q_OBJECT
+public:
+ explicit MastodonAccount(MastodonMicroBlog *parent, const QString &alias);
+ ~MastodonAccount();
+
+ virtual void writeConfig() override;
+
+ QString host();
+ void setHost(const QString &host);
+
+ QString acct();
+ void setAcct(const QString &acct);
+
+ QString consumerKey();
+ void setConsumerKey(const QString &consumerKey);
+
+ QString consumerSecret();
+ void setConsumerSecret(const QString &consumerSecret);
+
+ QString tokenSecret();
+ void setTokenSecret(const QString &tokenSecret);
+
+ MastodonOAuth *oAuth();
+
+private:
+ class Private;
+ Private *d;
+
+};
+
+#endif // MASTODONACCOUNT_H
diff --git a/microblogs/mastodon/mastodondebug.cpp \
b/microblogs/mastodon/mastodondebug.cpp new file mode 100644
index 00000000..1acdbe80
--- /dev/null
+++ b/microblogs/mastodon/mastodondebug.cpp
@@ -0,0 +1,22 @@
+/* This file is part of the KDE project
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "mastodondebug.h"
+Q_LOGGING_CATEGORY(CHOQOK, "org.kde.choqok.mastodon")
+
diff --git a/microblogs/mastodon/mastodondebug.h \
b/microblogs/mastodon/mastodondebug.h new file mode 100644
index 00000000..723b95a6
--- /dev/null
+++ b/microblogs/mastodon/mastodondebug.h
@@ -0,0 +1,27 @@
+/* This file is part of the KDE project
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef MASTODONDEBUG_H
+#define MASTODONDEBUG_H
+
+#include <QLoggingCategory>
+Q_DECLARE_LOGGING_CATEGORY(CHOQOK)
+
+#endif
+
diff --git a/microblogs/mastodon/mastodoneditaccountwidget.cpp \
b/microblogs/mastodon/mastodoneditaccountwidget.cpp new file mode 100644
index 00000000..07072e77
--- /dev/null
+++ b/microblogs/mastodon/mastodoneditaccountwidget.cpp
@@ -0,0 +1,221 @@
+/*
+ This file is part of Choqok, the KDE micro-blogging client
+
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 of the license.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see http://www.gnu.org/licenses/
+*/
+
+#include "mastodoneditaccountwidget.h"
+
+#include <QCoreApplication>
+#include <QCheckBox>
+#include <QInputDialog>
+#include <QJsonDocument>
+#include <QEventLoop>
+#include <QUrl>
+
+#include <KIO/AccessManager>
+#include <KIO/StoredTransferJob>
+#include <KMessageBox>
+
+#include "choqoktools.h"
+#include "accountmanager.h"
+
+#include "mastodonaccount.h"
+#include "mastodondebug.h"
+#include "mastodonmicroblog.h"
+#include "mastodonoauth.h"
+
+MastodonEditAccountWidget::MastodonEditAccountWidget(MastodonMicroBlog *microblog,
+ MastodonAccount *account,
+ QWidget *parent):
+ ChoqokEditAccountWidget(account, parent)
+ , m_account(account)
+{
+ setupUi(this);
+
+ connect(kcfg_authorize, SIGNAL(clicked(bool)), SLOT(authorizeUser()));
+
+ if (m_account) {
+ kcfg_alias->setText(m_account->alias());
+ kcfg_acct->setText(m_account->acct());
+ setAuthenticated(!m_account->tokenSecret().isEmpty());
+ } else {
+ setAuthenticated(false);
+ QString newAccountAlias = microblog->serviceName();
+ const QString servName = newAccountAlias;
+ int counter = 1;
+ while (Choqok::AccountManager::self()->findAccount(newAccountAlias)) {
+ newAccountAlias = QStringLiteral("%1%2").arg(servName).arg(counter);
+ counter++;
+ }
+ m_account = new MastodonAccount(microblog, newAccountAlias);
+ setAccount(m_account);
+ kcfg_alias->setText(newAccountAlias);
+ }
+
+ loadTimelinesTable();
+}
+
+MastodonEditAccountWidget::~MastodonEditAccountWidget()
+{
+}
+
+Choqok::Account *MastodonEditAccountWidget::apply()
+{
+ m_account->setAlias(kcfg_alias->text());
+ m_account->setAcct(kcfg_acct->text());
+ m_account->setTokenSecret(m_account->oAuth()->token());
+ m_account->writeConfig();
+ saveTimelinesTable();
+ return m_account;
+}
+
+void MastodonEditAccountWidget::authorizeUser()
+{
+ qCDebug(CHOQOK);
+ if (kcfg_acct->text().isEmpty() || \
!kcfg_acct->text().contains(QLatin1Char('@'))) { + return;
+ }
+ if (m_account->consumerKey().isEmpty() || m_account->consumerSecret().isEmpty()) \
{ + registerClient();
+ }
+
+ connect(m_account->oAuth(), &QAbstractOAuth::authorizeWithBrowser, \
&Choqok::openUrl); + connect(m_account->oAuth(), &QAbstractOAuth::statusChanged, \
this, &MastodonEditAccountWidget::gotToken); +
+ m_account->oAuth()->grant();
+
+ QString verifier = QInputDialog::getText(this, i18n("coe"),
+ i18n("Enter the code received from %1", m_account->host()));
+ if (verifier.isEmpty()) {
+ return;
+ }
+
+ m_account->oAuth()->getToken(verifier);
+}
+
+void MastodonEditAccountWidget::gotToken()
+{
+ isAuthenticated = false;
+ if (m_account->oAuth()->status() == QAbstractOAuth::Status::Granted) {
+ setAuthenticated(true);
+ KMessageBox::information(this, i18n("Choqok is authorized successfully."), \
i18n("Authorized")); + } else {
+ KMessageBox::detailedError(this, i18n("Authorization Error"), i18n("OAuth \
authorization error")); + }
+}
+
+bool MastodonEditAccountWidget::validateData()
+{
+ if (kcfg_alias->text().isEmpty() || kcfg_acct->text().isEmpty() ||
+ !kcfg_acct->text().contains(QLatin1Char('@')) ||
+ !isAuthenticated) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+void MastodonEditAccountWidget::setAuthenticated(bool authenticated)
+{
+ isAuthenticated = authenticated;
+ if (authenticated) {
+ kcfg_authorize->setIcon(QIcon::fromTheme(QLatin1String("object-unlocked")));
+ kcfg_authenticateLed->on();
+ kcfg_authenticateStatus->setText(i18n("Authenticated"));
+ } else {
+ kcfg_authorize->setIcon(QIcon::fromTheme(QLatin1String("object-locked")));
+ kcfg_authenticateLed->off();
+ kcfg_authenticateStatus->setText(i18n("Not Authenticated"));
+ }
+}
+
+void MastodonEditAccountWidget::loadTimelinesTable()
+{
+ for (const QString &timeline: m_account->microblog()->timelineNames()) {
+ int newRow = timelinesTable->rowCount();
+ timelinesTable->insertRow(newRow);
+ timelinesTable->setItem(newRow, 0, new QTableWidgetItem(timeline));
+
+ QCheckBox *enable = new QCheckBox(timelinesTable);
+ enable->setChecked(m_account->timelineNames().contains(timeline));
+ timelinesTable->setCellWidget(newRow, 1, enable);
+ }
+}
+
+void MastodonEditAccountWidget::registerClient()
+{
+ if (kcfg_acct->text().contains(QLatin1Char('@'))) {
+ m_account->setUsername(kcfg_acct->text().split(QLatin1Char('@'))[0]);
+ m_account->setHost(QLatin1String("https://") + \
kcfg_acct->text().split(QLatin1Char('@'))[1]); +
+ m_account->oAuth()->setAccessTokenUrl(QUrl(m_account->host() + \
QLatin1String("/oauth/token"))); + \
m_account->oAuth()->setAuthorizationUrl(QUrl(m_account->host() + \
QLatin1String("/oauth/authorize"))); +
+ QUrl url(m_account->host() + QLatin1String("/api/v1/apps"));
+ QByteArray data;
+ data += "client_name=" + QCoreApplication::applicationName().toLatin1();
+ data += "&redirect_uris=" + \
QUrl::toPercentEncoding(QLatin1String("urn:ietf:wg:oauth:2.0:oob")); + data += \
"&scopes=" + QUrl::toPercentEncoding(QLatin1String("read write follow")); + \
data += "&website=" + \
QUrl::toPercentEncoding(QLatin1String("http://choqok.gnufolks.org/")); +
+ KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, \
KIO::HideProgressInfo); + if (!job) {
+ qCDebug(CHOQOK) << "Cannot create an http POST request!";
+ return;
+ }
+ job->addMetaData(QLatin1String("content-type"), QLatin1String("Content-Type: \
application/x-www-form-urlencoded")); + QEventLoop loop;
+ connect(job, SIGNAL(result(KJob*)), &loop, SLOT(quit()));
+ job->start();
+ loop.exec();
+
+ if (job->error()) {
+ qCDebug(CHOQOK) << "An error occurred in Job";
+ return;
+ } else {
+ KIO::StoredTransferJob *stj = qobject_cast<KIO::StoredTransferJob \
*>(job); +
+ const QJsonDocument json = QJsonDocument::fromJson(stj->data());
+ if (!json.isNull()) {
+ const QVariantMap result = json.toVariant().toMap();
+ m_account->setConsumerKey(result[QLatin1String("client_id")].toString());
+ m_account->setConsumerSecret(result[QLatin1String("client_secret")].toString());
+ m_account->oAuth()->setClientIdentifier(m_account->consumerKey());
+ m_account->oAuth()->setClientIdentifierSharedKey(m_account->consumerSecret());
+ } else {
+ qCDebug(CHOQOK) << "Cannot parse JSON reply";
+ }
+ }
+ } else {
+ qCDebug(CHOQOK) << "username is not valid";
+ }
+}
+
+void MastodonEditAccountWidget::saveTimelinesTable()
+{
+ QStringList timelines;
+ for (int i = 0; i < timelinesTable->rowCount(); ++i) {
+ QCheckBox *enable = qobject_cast<QCheckBox *>(timelinesTable->cellWidget(i, \
1)); + if (enable && enable->isChecked()) {
+ timelines.append(timelinesTable->item(i, 0)->text());
+ }
+ }
+ //m_account->setTimelineNames(timelines);
+}
diff --git a/microblogs/mastodon/mastodoneditaccountwidget.h \
b/microblogs/mastodon/mastodoneditaccountwidget.h new file mode 100644
index 00000000..aa1d8e73
--- /dev/null
+++ b/microblogs/mastodon/mastodoneditaccountwidget.h
@@ -0,0 +1,61 @@
+/*
+ This file is part of Choqok, the KDE micro-blogging client
+
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 of the license.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see http://www.gnu.org/licenses/
+*/
+
+#ifndef MASTODONEDITACCOUNTWIDGET_H
+#define MASTODONEDITACCOUNTWIDGET_H
+
+#include "editaccountwidget.h"
+
+#include <QUrlQuery>
+
+#include "ui_mastodoneditaccountwidget.h"
+
+class MastodonAccount;
+class MastodonMicroBlog;
+
+class MastodonEditAccountWidget : public ChoqokEditAccountWidget, \
Ui::MastodonEditAccountWidget +{
+ Q_OBJECT
+public:
+ explicit MastodonEditAccountWidget(MastodonMicroBlog *microblog, MastodonAccount \
*account, + QWidget *parent);
+ ~MastodonEditAccountWidget();
+
+ virtual Choqok::Account *apply() override;
+
+ virtual bool validateData() override;
+
+private Q_SLOTS:
+ void authorizeUser();
+ void gotToken();
+
+private:
+ void setAuthenticated(bool authenticated);
+ void loadTimelinesTable();
+ void registerClient();
+ void saveTimelinesTable();
+
+ MastodonAccount *m_account;
+ bool isAuthenticated;
+};
+
+#endif // MASTODONEDITACCOUNTWIDGET_H
diff --git a/microblogs/mastodon/mastodoneditaccountwidget.ui \
b/microblogs/mastodon/mastodoneditaccountwidget.ui new file mode 100644
index 00000000..819fc07f
--- /dev/null
+++ b/microblogs/mastodon/mastodoneditaccountwidget.ui
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MastodonEditAccountWidget</class>
+ <widget class="QWidget" name="MastodonEditAccountWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>361</width>
+ <height>403</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTabWidget" name="tabwidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tabAccount">
+ <attribute name="title">
+ <string>Mastodon Account</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>A&lias:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="buddy">
+ <cstring>kcfg_alias</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="kcfg_alias">
+ <property name="toolTip">
+ <string>The account alias</string>
+ </property>
+ <property name="whatsThis">
+ <string>The alias is the name you want to give to your account. It should \
be unique. You can have several connections to the same service so the alias lets you \
give them names.</string> + </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" \
"http://www.w3.org/TR/REC-html40/strict.dtd"> \
+<html><head><meta name="qrichtext" content="1" \
/><style type="text/css"> +p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'DejaVu Sans'; \
font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" \
margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; \
-qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; \
font-weight:600;">Note:</span><span style=" \
font-size:8pt;"> The alias must be \
unique.</span></p></body></html></string> + \
</property> + <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QGroupBox" name="kcfg_basicAuth">
+ <property name="title">
+ <string>Credentials</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::ExpandingFieldsGrow</enum>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>username@domain:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="kcfg_acct"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <widget class="QGroupBox" name="kcfg_credentialsBox">
+ <property name="title">
+ <string>OAuth Authentication</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0" colspan="2">
+ <widget class="QLabel" name="label_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Click the below button; if everything goes well, you will \
pointed to Mastodon website to allow access to Choqok.</string> + \
</property> + <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="KLed" name="kcfg_authenticateLed">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="state">
+ <enum>KLed::Off</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="kcfg_authenticateStatus">
+ <property name="text">
+ <string>Not Authenticated</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QPushButton" name="kcfg_authorize">
+ <property name="toolTip">
+ <string>Verify credentials</string>
+ </property>
+ <property name="text">
+ <string>&Authenticate with Mastodon Service</string>
+ </property>
+ <property name="icon">
+ <iconset theme="object-locked">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="4" column="0" colspan="2">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>Timelines Configuration</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_5">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Which timelines do you like to be enabled?</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTableWidget" name="timelinesTable">
+ <column>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Enable</string>
+ </property>
+ </column>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>KLed</class>
+ <extends>QWidget</extends>
+ <header>kled.h</header>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>kcfg_alias</tabstop>
+ <tabstop>kcfg_acct</tabstop>
+ <tabstop>kcfg_authorize</tabstop>
+ <tabstop>tabwidget</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/microblogs/mastodon/mastodonmicroblog.cpp \
b/microblogs/mastodon/mastodonmicroblog.cpp new file mode 100644
index 00000000..93da4db6
--- /dev/null
+++ b/microblogs/mastodon/mastodonmicroblog.cpp
@@ -0,0 +1,745 @@
+/*
+ This file is part of Choqok, the KDE micro-blogging client
+
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 of the license.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see http://www.gnu.org/licenses/
+*/
+
+#include "mastodonmicroblog.h"
+
+#include <QAction>
+#include <QFile>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QMenu>
+#include <QMimeDatabase>
+#include <QTextDocument>
+
+#include <KIO/StoredTransferJob>
+#include <KPluginFactory>
+
+#include "accountmanager.h"
+#include "application.h"
+#include "choqokbehaviorsettings.h"
+#include "notifymanager.h"
+#include "postwidget.h"
+
+#include "mastodonaccount.h"
+#include "mastodondebug.h"
+#include "mastodoneditaccountwidget.h"
+#include "mastodonpost.h"
+#include "mastodonpostwidget.h"
+
+class MastodonMicroBlog::Private
+{
+public:
+ Private(): countOfTimelinesToSave(0)
+ {}
+ int countOfTimelinesToSave;
+};
+
+K_PLUGIN_FACTORY_WITH_JSON(MastodonMicroBlogFactory, "choqok_mastodon.json",
+ registerPlugin < MastodonMicroBlog > ();)
+
+const QString MastodonMicroBlog::homeTimeline(QLatin1String("/api/v1/timelines/home"));
+const QString MastodonMicroBlog::publicTimeline(QLatin1String("/api/v1/timelines/public"));
+
+MastodonMicroBlog::MastodonMicroBlog(QObject *parent, const QVariantList &args):
+ MicroBlog(QStringLiteral("Mastodon") , parent), d(new Private)
+{
+ Q_UNUSED(args)
+ setServiceName(QLatin1String("Mastodon"));
+ setServiceHomepageUrl(QLatin1String("https://mastodon.social"));
+ QStringList timelineNames;
+ timelineNames << QLatin1String("Home") << QLatin1String("Local") << \
QLatin1String("Federated"); + setTimelineNames(timelineNames);
+ setTimelinesInfo();
+}
+
+MastodonMicroBlog::~MastodonMicroBlog()
+{
+ qDeleteAll(m_timelinesInfos);
+ delete d;
+}
+
+void MastodonMicroBlog::aboutToUnload()
+{
+ for (Choqok::Account *acc: Choqok::AccountManager::self()->accounts()) {
+ if (acc->microblog() == this) {
+ d->countOfTimelinesToSave += acc->timelineNames().count();
+ }
+ }
+ Q_EMIT saveTimelines();
+}
+
+ChoqokEditAccountWidget *MastodonMicroBlog::createEditAccountWidget(Choqok::Account \
*account, + QWidget *parent)
+{
+ MastodonAccount *acc = qobject_cast<MastodonAccount * >(account);
+ if (acc || !account) {
+ return new MastodonEditAccountWidget(this, acc, parent);
+ } else {
+ qCDebug(CHOQOK) << "Account passed here was not a valid MastodonAccount!";
+ return 0;
+ }
+}
+
+void MastodonMicroBlog::createPost(Choqok::Account *theAccount, Choqok::Post *post)
+{
+ if (!post || post->content.isEmpty()) {
+ qCDebug(CHOQOK) << "ERROR: Status text is empty!";
+ Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::OtherError,
+ i18n("Creating the new post failed. Text is empty."), \
MicroBlog::Critical); + return;
+ }
+
+ MastodonAccount *acc = qobject_cast<MastodonAccount *>(theAccount);
+ if (acc) {
+ QVariantMap object;
+ object.insert(QLatin1String("status"), post->content);
+
+ const QByteArray data = QJsonDocument::fromVariant(object).toJson();
+
+ QUrl url(acc->host());
+ url = url.adjusted(QUrl::StripTrailingSlash);
+ url.setPath(url.path() + QLatin1String("/api/v1/statuses"));
+ KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, \
KIO::HideProgressInfo); + job->addMetaData(QLatin1String("content-type"), \
QLatin1String("Content-Type: application/json")); + \
job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc)); + \
if (!job) { + qCDebug(CHOQOK) << "Cannot create an http POST request!";
+ return;
+ }
+ m_accountJobs[job] = acc;
+ m_createPostJobs[job] = post;
+ connect(job, SIGNAL(result(KJob*)), this, SLOT(slotCreatePost(KJob*)));
+ job->start();
+ } else {
+ qCDebug(CHOQOK) << "theAccount is not a MastodonAccount!";
+ }
+}
+
+Choqok::Account *MastodonMicroBlog::createNewAccount(const QString &alias)
+{
+ MastodonAccount *acc = qobject_cast<MastodonAccount *>(
+ Choqok::AccountManager::self()->findAccount(alias));
+ if (!acc) {
+ return new MastodonAccount(this, alias);
+ } else {
+ qCDebug(CHOQOK) << "Cannot create a new MastodonAccount!";
+ return 0;
+ }
+}
+
+QString MastodonMicroBlog::lastTimelineId(Choqok::Account *theAccount,
+ const QString &timeline) const
+{
+ qCDebug(CHOQOK) << "Latest ID for timeline " << timeline << \
m_timelinesLatestIds[theAccount][timeline]; + return \
m_timelinesLatestIds[theAccount][timeline]; +}
+
+QList< Choqok::Post * > MastodonMicroBlog::readTimeline(const QByteArray &buffer)
+{
+ QList<Choqok::Post * > posts;
+ const QJsonDocument json = QJsonDocument::fromJson(buffer);
+ if (!json.isNull()) {
+ const QVariantList list = json.array().toVariantList();
+ for (const QVariant &element: list) {
+ posts.prepend(readPost(element.toMap(), new MastodonPost));
+ }
+ } else {
+ qCDebug(CHOQOK) << "Cannot parse JSON reply";
+ }
+
+ return posts;
+}
+
+Choqok::Post *MastodonMicroBlog::readPost(const QVariantMap &var, Choqok::Post \
*post) +{
+ MastodonPost *p = dynamic_cast< MastodonPost * >(post);
+ if (p) {
+ QVariantMap reblog = var[QLatin1String("reblog")].toMap();
+ QVariantMap status;
+ if (reblog.isEmpty()) {
+ status = var;
+ } else {
+ status = reblog;
+ }
+
+ QTextDocument content;
+ content.setHtml(status[QLatin1String("spoiler_text")].toString() + \
QLatin1String("<br />") + status[QLatin1String("content")].toString()); + \
p->content += content.toPlainText().trimmed(); +
+ p->creationDateTime = \
QDateTime::fromString(var[QLatin1String("created_at")].toString(), + \
Qt::ISODate); + p->creationDateTime.setTimeSpec(Qt::UTC);
+
+ p->link = var[QLatin1String("url")].toString();
+ p->isFavorited = var[QLatin1String("favourited")].toBool();
+ if (p->isFavorited) {
+ p->isRead = true;
+ }
+ p->postId = var[QLatin1String("id")].toString();
+
+ p->conversationId = var[QLatin1String("id")].toString();
+
+ QVariantMap application = var[QLatin1String("application")].toMap();
+ if (!application.isEmpty()) {
+ p->source = application[QLatin1String("name")].toString();
+ }
+
+ if (var[QLatin1String("visibility")].toString().compare(QLatin1String("direct")) \
== 0) { + p->isPrivate = true;
+ }
+
+ QVariantMap account = status[QLatin1Literal("account")].toMap();
+
+ p->author.userId = account[QLatin1String("acct")].toString();
+ p->author.userName = account[QLatin1String("username")].toString();
+ p->author.realName = account[QLatin1String("display_name")].toString();
+ p->author.homePageUrl = account[QLatin1String("url")].toString();
+ p->author.followersCount = \
account[QLatin1String("followers_count")].toUInt(); +
+ QTextDocument description;
+ description.setHtml(account[QLatin1String("note")].toString());
+ p->author.description = description.toPlainText().trimmed();
+
+ p->author.profileImageUrl = account[QLatin1String("avatar")].toString();
+
+ p->replyToPostId = var[QLatin1String("in_reply_to_id")].toString();
+ p->replyToUserId = var[QLatin1String("in_reply_to_account_id")].toString();
+
+ if (!reblog.isEmpty()) {
+ p->repeatedDateTime = \
QDateTime::fromString(var[QLatin1String("created_at")].toString(), + \
Qt::ISODate); + p->repeatedDateTime.setTimeSpec(Qt::UTC);
+
+ p->repeatedPostId = var[QLatin1String("id")].toString();
+ p->repeatedFromUsername = \
var[QLatin1Literal("account")].toMap()[QLatin1String("acct")].toString(); + }
+
+ return p;
+ } else {
+ qCDebug(CHOQOK) << "post is not a MastodonPost!";
+ return post;
+ }
+}
+
+void MastodonMicroBlog::toggleReblog(Choqok::Account *theAccount, Choqok::Post \
*post) +{
+ MastodonAccount *acc = qobject_cast<MastodonAccount *>(theAccount);
+ if (acc) {
+ QUrl url(acc->host());
+ url = url.adjusted(QUrl::StripTrailingSlash);
+ if (acc->username().compare(post->repeatedFromUsername) == 0) {
+ url.setPath(url.path() + \
QStringLiteral("/api/v1/statuses/%1/unreblog").arg(post->postId)); + } else {
+ url.setPath(url.path() + \
QStringLiteral("/api/v1/statuses/%1/reblog").arg(post->postId)); + }
+ KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, \
KIO::HideProgressInfo); + job->addMetaData(QLatin1String("content-type"), \
QLatin1String("Content-Type: application/json")); + \
job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc)); + \
if (!job) { + qCDebug(CHOQOK) << "Cannot create an http POST request!";
+ return;
+ }
+ m_accountJobs[job] = acc;
+ m_shareJobs[job] = post;
+ connect(job, SIGNAL(result(KJob*)), this, SLOT(slotReblog(KJob*)));
+ job->start();
+ } else {
+ qCDebug(CHOQOK) << "theAccount is not a MastodonAccount!";
+ }
+}
+
+void MastodonMicroBlog::slotReblog(KJob *job)
+{
+ qCDebug(CHOQOK);
+ if (!job) {
+ qCDebug(CHOQOK) << "Job is null pointer";
+ return;
+ }
+ Choqok::Post *post = m_shareJobs.take(job);
+ Choqok::Account *theAccount = m_accountJobs.take(job);
+ if (!post || !theAccount) {
+ qCDebug(CHOQOK) << "Account or Post is NULL pointer";
+ return;
+ }
+ int ret = 1;
+ if (job->error()) {
+ qCDebug(CHOQOK) << "Job Error:" << job->errorString();
+ } else {
+ Choqok::UI::Global::mainWindow()->showStatusMessage(
+ i18n("The post has been shared."));
+ KIO::StoredTransferJob *j = qobject_cast<KIO::StoredTransferJob * >(job);
+
+ const QJsonDocument json = QJsonDocument::fromJson(j->data());
+ if (!json.isNull()) {
+ ret = 0;
+ } else {
+ qCDebug(CHOQOK) << "Cannot parse JSON reply";
+ }
+ }
+
+ if (ret) {
+ Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError,
+ i18n("Cannot share the post. %1", job->errorString()));
+ }
+}
+
+void MastodonMicroBlog::toggleFavorite(Choqok::Account *theAccount, Choqok::Post \
*post) +{
+ MastodonAccount *acc = qobject_cast<MastodonAccount *>(theAccount);
+ if (acc) {
+ QUrl url(acc->host());
+ url = url.adjusted(QUrl::StripTrailingSlash);
+
+ if (post->isFavorited) {
+ url.setPath(url.path() + \
QStringLiteral("/api/v1/statuses/%1/unfavourite").arg(post->postId)); + } else \
{ + url.setPath(url.path() + \
QStringLiteral("/api/v1/statuses/%1/favourite").arg(post->postId)); + }
+
+ KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, \
KIO::HideProgressInfo); + job->addMetaData(QLatin1String("content-type"), \
QLatin1String("Content-Type: application/json")); + \
job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc)); + \
if (!job) { + qCDebug(CHOQOK) << "Cannot create an http POST request!";
+ return;
+ }
+ m_accountJobs[job] = acc;
+ m_favoriteJobs[job] = post;
+ connect(job, SIGNAL(result(KJob*)), this, SLOT(slotFavorite(KJob*)));
+ job->start();
+ } else {
+ qCDebug(CHOQOK) << "theAccount is not a MastodonAccount!";
+ }
+}
+
+void MastodonMicroBlog::slotFavorite(KJob *job)
+{
+ qCDebug(CHOQOK);
+ if (!job) {
+ qCDebug(CHOQOK) << "Job is null pointer";
+ return;
+ }
+ Choqok::Post *post = m_favoriteJobs.take(job);
+ Choqok::Account *theAccount = m_accountJobs.take(job);
+ if (!post || !theAccount) {
+ qCDebug(CHOQOK) << "Account or Post is NULL pointer";
+ return;
+ }
+ if (job->error()) {
+ qCDebug(CHOQOK) << "Job Error:" << job->errorString();
+ Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError,
+ i18n("Cannot set/unset the post as favorite. %1", \
job->errorString())); + } else {
+ post->isFavorited = !post->isFavorited;
+ Q_EMIT favorite(theAccount, post);
+ }
+}
+
+void MastodonMicroBlog::setLastTimelineId(Choqok::Account *theAccount,
+ const QString &timeline,
+ const QString &id)
+{
+ m_timelinesLatestIds[theAccount][timeline] = id;
+}
+
+void MastodonMicroBlog::setTimelinesInfo()
+{
+ Choqok::TimelineInfo *t = new Choqok::TimelineInfo;
+ t->name = i18nc("Timeline Name", "Home");
+ t->description = i18nc("Timeline description", "You and people you follow");
+ t->icon = QLatin1String("user-home");
+ m_timelinesInfos[QLatin1String("Home")] = t;
+ m_timelinesPaths[QLatin1String("Home")] = homeTimeline;
+
+ t = new Choqok::TimelineInfo;
+ t->name = i18nc("Timeline Name", "Local");
+ t->description = i18nc("Timeline description", "Local timeline");
+ t->icon = QLatin1String("folder-public");
+ m_timelinesInfos[QLatin1String("Local")] = t;
+ m_timelinesPaths[QLatin1String("Local")] = publicTimeline;
+
+ t = new Choqok::TimelineInfo;
+ t->name = i18nc("Timeline Name", "Federated");
+ t->description = i18nc("Timeline description", "Federated timelime");
+ t->icon = QLatin1String("folder-remote");
+ m_timelinesInfos[QLatin1String("Federated")] = t;
+ m_timelinesPaths[QLatin1String("Federated")] = publicTimeline;
+}
+
+void MastodonMicroBlog::removePost(Choqok::Account *theAccount, Choqok::Post *post)
+{
+ MastodonAccount *acc = qobject_cast<MastodonAccount *>(theAccount);
+ if (acc) {
+ QUrl url(acc->host());
+ url = url.adjusted(QUrl::StripTrailingSlash);
+ url.setPath(url.path() + \
QStringLiteral("/api/v1/statuses/%1").arg(post->postId)); + KIO::TransferJob \
*job = KIO::http_delete(url, KIO::HideProgressInfo); + \
job->addMetaData(QLatin1String("content-type"), QLatin1String("Content-Type: \
application/json")); + job->addMetaData(QLatin1String("customHTTPHeader"), \
authorizationMetaData(acc)); + if (!job) {
+ qCDebug(CHOQOK) << "Cannot create an http POST request!";
+ return;
+ }
+ m_accountJobs[job] = acc;
+ m_removePostJobs[job] = post;
+ connect(job, SIGNAL(result(KJob*)), this, SLOT(slotRemovePost(KJob*)));
+ job->start();
+ } else {
+ qCDebug(CHOQOK) << "theAccount is not a MastodonAccount!";
+ }
+}
+
+QList<Choqok::Post * > MastodonMicroBlog::loadTimeline(Choqok::Account *account,
+ const QString &timelineName)
+{
+ QList< Choqok::Post * > list;
+ const QString fileName = \
Choqok::AccountManager::generatePostBackupFileName(account->alias(), + \
timelineName); + const KConfig postsBackup(fileName, KConfig::NoGlobals, \
QStandardPaths::DataLocation); + const QStringList tmpList = \
postsBackup.groupList(); +
+ // don't load old archives
+ if (tmpList.isEmpty() || !(QDateTime::fromString(tmpList.first()).isValid())) {
+ return list;
+ }
+
+ QList<QDateTime> groupList;
+ for (const QString &str: tmpList) {
+ groupList.append(QDateTime::fromString(str));
+ }
+ qSort(groupList);
+ MastodonPost *st;
+ for (const QDateTime &datetime: groupList) {
+ st = new MastodonPost;
+ KConfigGroup grp(&postsBackup, datetime.toString());
+ st->creationDateTime = grp.readEntry("creationDateTime", \
QDateTime::currentDateTime()); + st->postId = grp.readEntry("postId", \
QString()); + st->link = grp.readEntry("link", QString());
+ st->content = grp.readEntry("content", QString());
+ st->source = grp.readEntry("source", QString());
+ st->isFavorited = grp.readEntry("favorited", false);
+ st->author.userId = grp.readEntry("authorId", QString());
+ st->author.userName = grp.readEntry("authorUserName", QString());
+ st->author.realName = grp.readEntry("authorRealName", QString());
+ st->author.description = grp.readEntry("authorDescription" , QString());
+ st->author.profileImageUrl = grp.readEntry("authorProfileImageUrl", \
QString()); + st->author.homePageUrl = grp.readEntry("authorHomePageUrl", \
QString()); + st->isRead = grp.readEntry("isRead", true);
+ st->conversationId = grp.readEntry("conversationId", QString());
+ st->replyToPostId = grp.readEntry("replyToPostId", QString());
+ st->replyToUserId = grp.readEntry("replyToUserId", QString());
+ st->repeatedFromUsername = grp.readEntry("repeatedFrom", QString());
+ st->repeatedPostId = grp.readEntry("repeatedPostId", QString());
+ st->repeatedDateTime = grp.readEntry("repeatedDateTime", QDateTime());
+
+ list.append(st);
+ }
+
+ if (!list.isEmpty()) {
+ setLastTimelineId(account, timelineName, list.last()->conversationId);
+ }
+
+ return list;
+}
+
+QUrl MastodonMicroBlog::profileUrl(Choqok::Account *account, const Choqok::User \
&user) const +{
+ Q_UNUSED(account)
+ return QUrl(user.homePageUrl);
+}
+
+QString MastodonMicroBlog::hostFromAcct(const QString &acct)
+{
+ if (acct.contains(QLatin1Char('@'))) {
+ return acct.split(QLatin1Char('@'))[1];
+ } else {
+ return acct;
+ }
+}
+
+QString MastodonMicroBlog::userNameFromAcct(const QString &acct)
+{
+ if (acct.contains(QLatin1Char('@'))) {
+ return acct.split(QLatin1Char('@'))[0];
+ } else {
+ return acct;
+ }
+}
+
+void MastodonMicroBlog::saveTimeline(Choqok::Account *account, const QString \
&timelineName, + const QList< \
Choqok::UI::PostWidget * > &timeline) +{
+ const QString fileName = \
Choqok::AccountManager::generatePostBackupFileName(account->alias(), + \
timelineName); + KConfig postsBackup(fileName, KConfig::NoGlobals, \
QStandardPaths::DataLocation); +
+ ///Clear previous data:
+ for (const QString &group: postsBackup.groupList()) {
+ postsBackup.deleteGroup(group);
+ }
+
+ for (Choqok::UI::PostWidget *wd: timeline) {
+ MastodonPost *post = dynamic_cast<MastodonPost * >(wd->currentPost());
+ KConfigGroup grp(&postsBackup, post->creationDateTime.toString());
+ grp.writeEntry("creationDateTime", post->creationDateTime);
+ grp.writeEntry("postId", post->postId);
+ grp.writeEntry("link", post->link);
+ grp.writeEntry("content", post->content);
+ grp.writeEntry("source", post->source);
+ grp.writeEntry("favorited", post->isFavorited);
+ grp.writeEntry("authorId", post->author.userId);
+ grp.writeEntry("authorRealName", post->author.realName);
+ grp.writeEntry("authorUserName", post->author.userName);
+ grp.writeEntry("authorDescription", post->author.description);
+ grp.writeEntry("authorProfileImageUrl", post->author.profileImageUrl);
+ grp.writeEntry("authorHomePageUrl", post->author.homePageUrl);
+ grp.writeEntry("isRead", post->isRead);
+ grp.writeEntry("conversationId", post->conversationId);
+ grp.writeEntry("replyToPostId", post->replyToPostId);
+ grp.writeEntry("replyToUserId", post->replyToUserId);
+ grp.writeEntry("repeatedFrom", post->repeatedFromUsername);
+ grp.writeEntry("repeatedPostId", post->repeatedPostId);
+ grp.writeEntry("repeatedDateTime", post->repeatedDateTime);
+ }
+ postsBackup.sync();
+
+ if (Choqok::Application::isShuttingDown()) {
+ --d->countOfTimelinesToSave;
+ if (d->countOfTimelinesToSave < 1) {
+ Q_EMIT readyForUnload();
+ }
+ }
+}
+
+Choqok::TimelineInfo *MastodonMicroBlog::timelineInfo(const QString &timelineName)
+{
+ return m_timelinesInfos.value(timelineName);
+}
+
+void MastodonMicroBlog::updateTimelines(Choqok::Account *theAccount)
+{
+ MastodonAccount *acc = qobject_cast<MastodonAccount *>(theAccount);
+ if (acc) {
+ for (const QString &timeline: acc->timelineNames()) {
+ QUrl url(acc->host());
+ url = url.adjusted(QUrl::StripTrailingSlash);
+ url.setPath(url.path() + QLatin1Char('/') + m_timelinesPaths[timeline]);
+
+ QUrlQuery query;
+ if (timeline.compare(QLatin1String("Local")) == 0) {
+ query.addQueryItem(QLatin1String("local"), QLatin1String("true"));
+ }
+ url.setQuery(query);
+
+ KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, \
KIO::HideProgressInfo); + if (!job) {
+ qCDebug(CHOQOK) << "Cannot create an http GET request!";
+ continue;
+ }
+ job->addMetaData(QLatin1String("customHTTPHeader"), \
authorizationMetaData(acc)); + m_timelinesRequests[job] = timeline;
+ m_accountJobs[job] = acc;
+ connect(job, SIGNAL(result(KJob*)), this, \
SLOT(slotUpdateTimeline(KJob*))); + job->start();
+ }
+ } else {
+ qCDebug(CHOQOK) << "theAccount is not a MastodonAccount!";
+ }
+}
+
+QString MastodonMicroBlog::authorizationMetaData(MastodonAccount *account) const
+{
+ return QStringLiteral("Authorization: Bearer ") + account->oAuth()->token();
+}
+
+Choqok::UI::PostWidget *MastodonMicroBlog::createPostWidget(Choqok::Account \
*account, + Choqok::Post *post,
+ QWidget *parent)
+{
+ return new MastodonPostWidget(account, post, parent);
+}
+
+void MastodonMicroBlog::fetchPost(Choqok::Account *theAccount, Choqok::Post *post)
+{
+ MastodonAccount *acc = qobject_cast<MastodonAccount *>(theAccount);
+ if (acc) {
+ if (!post->link.startsWith(acc->host())) {
+ qCDebug(CHOQOK) << "You can only fetch posts from your host!";
+ return;
+ }
+ QUrl url(post->link);
+
+ KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, \
KIO::HideProgressInfo); + if (!job) {
+ qCDebug(CHOQOK) << "Cannot create an http GET request!";
+ return;
+ }
+ job->addMetaData(QLatin1String("customHTTPHeader"), \
authorizationMetaData(acc)); + m_accountJobs[job] = acc;
+ connect(job, SIGNAL(result(KJob*)), this, SLOT(slotFetchPost(KJob*)));
+ job->start();
+ } else {
+ qCDebug(CHOQOK) << "theAccount is not a MastodonAccount!";
+ }
+}
+
+void MastodonMicroBlog::slotCreatePost(KJob *job)
+{
+ qCDebug(CHOQOK);
+ if (!job) {
+ qCDebug(CHOQOK) << "Job is null pointer";
+ return;
+ }
+ Choqok::Post *post = m_createPostJobs.take(job);
+ Choqok::Account *theAccount = m_accountJobs.take(job);
+ if (!post || !theAccount) {
+ qCDebug(CHOQOK) << "Account or Post is NULL pointer";
+ return;
+ }
+ int ret = 1;
+ if (job->error()) {
+ qCDebug(CHOQOK) << "Job Error:" << job->errorString();
+ } else {
+ KIO::StoredTransferJob *j = qobject_cast<KIO::StoredTransferJob * >(job);
+
+ const QJsonDocument json = QJsonDocument::fromJson(j->data());
+ if (!json.isNull()) {
+ const QVariantMap reply = json.toVariant().toMap();
+ if (!reply[QLatin1String("id")].toString().isEmpty()) {
+ Choqok::NotifyManager::success(i18n("New post submitted \
successfully")); + ret = 0;
+ Q_EMIT postCreated(theAccount, post);
+ }
+ } else {
+ qCDebug(CHOQOK) << "Cannot parse JSON reply";
+ }
+ }
+
+ if (ret) {
+ Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::CommunicationError,
+ i18n("Creating the new post failed. %1", \
job->errorString()), + MicroBlog::Critical);
+ }
+}
+
+void MastodonMicroBlog::slotFetchPost(KJob *job)
+{
+ qCDebug(CHOQOK);
+ if (!job) {
+ qCDebug(CHOQOK) << "Job is null pointer";
+ return;
+ }
+ Choqok::Account *theAccount = m_accountJobs.take(job);
+ if (!theAccount) {
+ qCDebug(CHOQOK) << "Account or postId is NULL pointer";
+ return;
+ }
+ int ret = 1;
+ if (job->error()) {
+ qCDebug(CHOQOK) << "Job Error:" << job->errorString();
+ } else {
+ KIO::StoredTransferJob *j = qobject_cast<KIO::StoredTransferJob * >(job);
+
+ const QJsonDocument json = QJsonDocument::fromJson(j->data());
+ if (!json.isNull()) {
+ const QVariantMap reply = json.toVariant().toMap();
+ MastodonPost *post = new MastodonPost;
+ readPost(reply, post);
+ ret = 0;
+ Q_EMIT postFetched(theAccount, post);
+ } else {
+ qCDebug(CHOQOK) << "Cannot parse JSON reply";
+ }
+ }
+
+ if (ret) {
+ Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError,
+ i18n("Cannot fetch post. %1", job->errorString()),
+ MicroBlog::Critical);
+ }
+}
+
+void MastodonMicroBlog::slotRemovePost(KJob *job)
+{
+ qCDebug(CHOQOK);
+ if (!job) {
+ qCDebug(CHOQOK) << "Job is null pointer";
+ return;
+ }
+ Choqok::Post *post = m_removePostJobs.take(job);
+ Choqok::Account *theAccount = m_accountJobs.take(job);
+ if (!post || !theAccount) {
+ qCDebug(CHOQOK) << "Account or Post is NULL pointer";
+ return;
+ }
+ int ret = 1;
+ if (job->error()) {
+ qCDebug(CHOQOK) << "Job Error:" << job->errorString();
+ } else {
+ KIO::TransferJob *j = qobject_cast<KIO::TransferJob * >(job);
+
+ if (j->metaData().contains(QStringLiteral("responsecode"))) {
+ int responseCode = \
j->queryMetaData(QStringLiteral("responsecode")).toInt(); +
+ if (responseCode == 200 || responseCode == 404) {
+ ret = 0;
+ Q_EMIT postRemoved(theAccount, post);
+ }
+ }
+ }
+
+ if (ret) {
+ Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::CommunicationError,
+ i18n("Removing the post failed. %1", job->errorString()),
+ MicroBlog::Critical);
+ }
+}
+
+void MastodonMicroBlog::slotUpdateTimeline(KJob *job)
+{
+ qCDebug(CHOQOK);
+ if (!job) {
+ qCDebug(CHOQOK) << "Job is null pointer";
+ return;
+ }
+ Choqok::Account *account = m_accountJobs.take(job);
+ if (!account) {
+ qCDebug(CHOQOK) << "Account or Post is NULL pointer";
+ return;
+ }
+ if (job->error()) {
+ qCDebug(CHOQOK) << "Job Error:" << job->errorString();
+ Q_EMIT error(account, Choqok::MicroBlog::CommunicationError,
+ i18n("An error occurred when fetching the timeline"));
+ } else {
+ KIO::StoredTransferJob *j = qobject_cast<KIO::StoredTransferJob * >(job);
+ const QList<Choqok::Post * > list = readTimeline(j->data());
+ const QString timeline(m_timelinesRequests.take(job));
+ if (!list.isEmpty()) {
+ setLastTimelineId(account, timeline, list.last()->conversationId);
+ }
+
+ Q_EMIT timelineDataReceived(account, timeline, list);
+ }
+}
+
+#include "mastodonmicroblog.moc"
diff --git a/microblogs/mastodon/mastodonmicroblog.h \
b/microblogs/mastodon/mastodonmicroblog.h new file mode 100644
index 00000000..3f489e52
--- /dev/null
+++ b/microblogs/mastodon/mastodonmicroblog.h
@@ -0,0 +1,120 @@
+/*
+ This file is part of Choqok, the KDE micro-blogging client
+
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 of the license.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see http://www.gnu.org/licenses/
+*/
+
+#ifndef MASTODONMICROBLOG_H
+#define MASTODONMICROBLOG_H
+
+#include <QNetworkAccessManager>
+
+#include "microblog.h"
+
+class QUrl;
+class KJob;
+class MastodonAccount;
+class MastodonPost;
+
+class MastodonMicroBlog : public Choqok::MicroBlog
+{
+ Q_OBJECT
+public:
+ explicit MastodonMicroBlog(QObject *parent, const QVariantList &args);
+ virtual ~MastodonMicroBlog();
+
+ virtual void aboutToUnload() override;
+
+ virtual ChoqokEditAccountWidget *createEditAccountWidget(Choqok::Account \
*account, QWidget *parent) override; +
+ virtual void createPost(Choqok::Account *theAccount, Choqok::Post *post) \
override; +
+ virtual Choqok::Account *createNewAccount(const QString &alias) override;
+
+ virtual Choqok::UI::PostWidget *createPostWidget(Choqok::Account *account,
+ Choqok::Post *post,
+ QWidget *parent) override;
+
+ virtual void fetchPost(Choqok::Account *theAccount, Choqok::Post *post) \
override; +
+ virtual QList<Choqok::Post * > loadTimeline(Choqok::Account *account,
+ const QString &timelineName) \
override; +
+ virtual void removePost(Choqok::Account *theAccount, Choqok::Post *post) \
override; +
+ virtual QUrl profileUrl(Choqok::Account *account, const Choqok::User &user) \
const override; +
+ virtual void saveTimeline(Choqok::Account *account, const QString &timelineName,
+ const QList< Choqok::UI::PostWidget * > &timeline) \
override; +
+ virtual Choqok::TimelineInfo *timelineInfo(const QString &timelineName) \
override; +
+ virtual void updateTimelines(Choqok::Account *theAccount) override;
+
+ void toggleReblog(Choqok::Account *theAccount, Choqok::Post *post);
+
+ void toggleFavorite(Choqok::Account *theAccount, Choqok::Post *post);
+
+ static QString userNameFromAcct(const QString &acct);
+ static QString hostFromAcct(const QString &acct);
+
+Q_SIGNALS:
+ void favorite(Choqok::Account *, Choqok::Post *);
+
+protected Q_SLOTS:
+ void slotCreatePost(KJob *job);
+ void slotFavorite(KJob *job);
+ void slotFetchPost(KJob *job);
+ void slotReblog(KJob *job);
+ void slotRemovePost(KJob *job);
+ void slotUpdateTimeline(KJob *job);
+
+protected:
+ static const QString homeTimeline;
+ static const QString publicTimeline;
+
+ QString authorizationMetaData(MastodonAccount *account) const;
+
+ QString lastTimelineId(Choqok::Account *theAccount, const QString &timeline) \
const; +
+ Choqok::Post *readPost(const QVariantMap &var, Choqok::Post *post);
+
+ QList<Choqok::Post * > readTimeline(const QByteArray &buffer);
+
+ void setLastTimelineId(Choqok::Account *theAccount, const QString &timeline,
+ const QString &id);
+ void setTimelinesInfo();
+
+ QMap<KJob *, Choqok::Account * > m_accountJobs;
+ QMap<KJob *, Choqok::Post * > m_createPostJobs;
+ QMap<KJob *, Choqok::Post * > m_favoriteJobs;
+ QMap<KJob *, Choqok::Post * > m_removePostJobs;
+ QMap<KJob *, Choqok::Post * > m_shareJobs;
+ QMap<QString, Choqok::TimelineInfo * > m_timelinesInfos;
+ QHash<Choqok::Account *, QMap<QString, QString> > m_timelinesLatestIds;
+ QHash<QString, QString> m_timelinesPaths;
+ QMap<KJob *, QString> m_timelinesRequests;
+
+private:
+ class Private;
+ Private *const d;
+
+};
+
+#endif // MASTODONMICROBLOG_H
diff --git a/microblogs/mastodon/mastodonoauth.cpp \
b/microblogs/mastodon/mastodonoauth.cpp new file mode 100644
index 00000000..007b9ae6
--- /dev/null
+++ b/microblogs/mastodon/mastodonoauth.cpp
@@ -0,0 +1,65 @@
+/*
+ This file is part of Choqok, the KDE micro-blogging client
+
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 of the license.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see http://www.gnu.org/licenses/
+
+*/
+
+#include "mastodonoauth.h"
+
+#include <QNetworkReply>
+#include <QUrlQuery>
+
+#include <KIO/AccessManager>
+
+#include "mastodonaccount.h"
+#include "mastodondebug.h"
+#include "mastodonoauthreplyhandler.h"
+
+MastodonOAuth::MastodonOAuth(MastodonAccount *account)
+ : QOAuth2AuthorizationCodeFlow(account),
+ m_replyHandler(0), m_networkAccessManager(0)
+{
+ qCDebug(CHOQOK);
+
+ m_replyHandler = new MastodonOAuthReplyHandler(this);
+ setReplyHandler(m_replyHandler);
+
+ m_networkAccessManager = new KIO::AccessManager(this);
+ setNetworkAccessManager(m_networkAccessManager);
+
+ setClientIdentifier(account->consumerKey());
+ setClientIdentifierSharedKey(account->consumerSecret());
+
+ setScope(QLatin1String("read write follow"));
+
+ setAccessTokenUrl(QUrl(account->host() + QLatin1String("/oauth/token")));
+ setAuthorizationUrl(QUrl(account->host() + QLatin1String("/oauth/authorize")));
+}
+
+MastodonOAuth::~MastodonOAuth()
+{
+ m_replyHandler->deleteLater();
+ m_networkAccessManager->deleteLater();
+}
+
+void MastodonOAuth::getToken(const QString &code)
+{
+ requestAccessToken(code);
+}
diff --git a/microblogs/mastodon/mastodonoauth.h \
b/microblogs/mastodon/mastodonoauth.h new file mode 100644
index 00000000..20bee770
--- /dev/null
+++ b/microblogs/mastodon/mastodonoauth.h
@@ -0,0 +1,55 @@
+/*
+ This file is part of Choqok, the KDE micro-blogging client
+
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 of the license.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see http://www.gnu.org/licenses/
+
+*/
+
+#ifndef MASTODONOAUTH_H
+#define MASTODONOAUTH_H
+
+#include <QOAuth2AuthorizationCodeFlow>
+#include <QNetworkAccessManager>
+
+class MastodonAccount;
+
+namespace KIO {
+class AccessManager;
+}
+
+class MastodonOAuthReplyHandler;
+
+class MastodonOAuth : public QOAuth2AuthorizationCodeFlow
+{
+ Q_OBJECT
+public:
+ explicit MastodonOAuth(MastodonAccount *account);
+ ~MastodonOAuth();
+
+ QByteArray authorizationHeader(const QUrl &requestUrl, \
QNetworkAccessManager::Operation method, + const \
QVariantMap ¶ms = QVariantMap()); +
+ void getToken(const QString &code);
+
+private:
+ MastodonOAuthReplyHandler *m_replyHandler;
+ KIO::AccessManager *m_networkAccessManager;
+};
+
+#endif // MASTODONOAUTH_H
diff --git a/microblogs/mastodon/mastodonoauthreplyhandler.cpp \
b/microblogs/mastodon/mastodonoauthreplyhandler.cpp new file mode 100644
index 00000000..9d14e574
--- /dev/null
+++ b/microblogs/mastodon/mastodonoauthreplyhandler.cpp
@@ -0,0 +1,38 @@
+/*
+ This file is part of Choqok, the KDE micro-blogging client
+
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 of the license.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see http://www.gnu.org/licenses/
+
+*/
+
+#include "mastodonoauthreplyhandler.h"
+
+MastodonOAuthReplyHandler::MastodonOAuthReplyHandler(QObject *parent)
+ : QOAuthOobReplyHandler(parent)
+{
+}
+
+MastodonOAuthReplyHandler::~MastodonOAuthReplyHandler()
+{
+}
+
+QString MastodonOAuthReplyHandler::callback() const
+{
+ return QLatin1String("urn:ietf:wg:oauth:2.0:oob");
+}
diff --git a/microblogs/mastodon/mastodonoauthreplyhandler.h \
b/microblogs/mastodon/mastodonoauthreplyhandler.h new file mode 100644
index 00000000..e7bf642d
--- /dev/null
+++ b/microblogs/mastodon/mastodonoauthreplyhandler.h
@@ -0,0 +1,39 @@
+/*
+ This file is part of Choqok, the KDE micro-blogging client
+
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 of the license.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see http://www.gnu.org/licenses/
+
+*/
+
+#ifndef MASTODONOAUTHREPLYHANDLER_H
+#define MASTODONOAUTHREPLYHANDLER_H
+
+#include <QOAuthOobReplyHandler>
+
+class MastodonOAuthReplyHandler : public QOAuthOobReplyHandler
+{
+ Q_OBJECT
+public:
+ explicit MastodonOAuthReplyHandler(QObject *parent = nullptr);
+ ~MastodonOAuthReplyHandler();
+
+ QString callback() const override;
+};
+
+#endif // MASTODONOAUTHREPLYHANDLER_H
diff --git a/microblogs/mastodon/mastodonpost.cpp \
b/microblogs/mastodon/mastodonpost.cpp new file mode 100644
index 00000000..5fb34aa9
--- /dev/null
+++ b/microblogs/mastodon/mastodonpost.cpp
@@ -0,0 +1,31 @@
+/*
+ This file is part of Choqok, the KDE micro-blogging client
+
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 of the license.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see http://www.gnu.org/licenses/
+*/
+
+#include "mastodonpost.h"
+
+MastodonPost::MastodonPost() : Post()
+{
+}
+
+MastodonPost::~MastodonPost()
+{
+}
diff --git a/microblogs/mastodon/mastodonpost.h b/microblogs/mastodon/mastodonpost.h
new file mode 100644
index 00000000..a5d5fffe
--- /dev/null
+++ b/microblogs/mastodon/mastodonpost.h
@@ -0,0 +1,38 @@
+/*
+ This file is part of Choqok, the KDE micro-blogging client
+
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 of the license.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see http://www.gnu.org/licenses/
+*/
+
+#ifndef MASTODONPOST_H
+#define MASTODONPOST_H
+
+#include <QStringList>
+
+#include "choqoktypes.h"
+
+class MastodonPost : public Choqok::Post
+{
+public:
+ explicit MastodonPost();
+ ~MastodonPost();
+
+};
+
+#endif //MASTODONPOST_H
diff --git a/microblogs/mastodon/mastodonpostwidget.cpp \
b/microblogs/mastodon/mastodonpostwidget.cpp new file mode 100644
index 00000000..ad44e189
--- /dev/null
+++ b/microblogs/mastodon/mastodonpostwidget.cpp
@@ -0,0 +1,138 @@
+/*
+ This file is part of Choqok, the KDE micro-blogging client
+
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 of the license.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see http://www.gnu.org/licenses/
+*/
+
+#include "mastodonpostwidget.h"
+
+#include <QAction>
+#include <QMenu>
+#include <QPushButton>
+
+#include <KLocalizedString>
+
+#include "mediamanager.h"
+#include "textbrowser.h"
+
+#include "mastodonaccount.h"
+#include "mastodondebug.h"
+#include "mastodonmicroblog.h"
+#include "mastodonpost.h"
+
+const QIcon MastodonPostWidget::unFavIcon(Choqok::MediaManager::convertToGrayScale(QIcon::fromTheme(QLatin1String("rating")).pixmap(16)));
+
+class MastodonPostWidget::Private
+{
+public:
+ QPushButton *btnFavorite;
+};
+
+MastodonPostWidget::MastodonPostWidget(Choqok::Account *account, Choqok::Post *post,
+ QWidget *parent):
+ PostWidget(account, post, parent), d(new Private)
+{
+}
+
+MastodonPostWidget::~MastodonPostWidget()
+{
+ delete d;
+}
+
+QString MastodonPostWidget::generateSign()
+{
+ QString ss;
+
+ MastodonPost *post = dynamic_cast<MastodonPost * >(currentPost());
+ MastodonAccount *account = qobject_cast<MastodonAccount * >(currentAccount());
+ MastodonMicroBlog *microblog = qobject_cast<MastodonMicroBlog * \
>(account->microblog()); + if (post) {
+ if (post->author.userName != account->username()) {
+ ss += QLatin1String("<b><a href=\"") + microblog->profileUrl(account, \
post->author).toDisplayString() + + QLatin1String("\" title=\"") + \
post->author.realName + QLatin1String("\">") + + \
post->author.userName + QLatin1String("</a></b> - "); + }
+
+ ss += QLatin1String("<a href=\"") + microblog->postUrl(account, \
post->author.userName, + post->postId) \
+ QLatin1String("\" title=\"") + + \
post->creationDateTime.toString(Qt::DefaultLocaleLongDate) + + \
QLatin1String("\">%1</a>"); +
+ if (!post->source.isEmpty()) {
+ ss += QLatin1String(" - ") + post->source;
+ }
+
+ if (!post->repeatedFromUsername.isEmpty()) {
+ ss += QLatin1String(" - ");
+ ss += i18n("Boosted by: ") + \
microblog->userNameFromAcct(post->repeatedFromUsername); + }
+ } else {
+ qCDebug(CHOQOK) << "post is not a MastodonPost!";
+ }
+
+ return ss;
+}
+
+void MastodonPostWidget::initUi()
+{
+ Choqok::UI::PostWidget::initUi();
+
+ if (isResendAvailable()) {
+ buttons().value(QLatin1String("btnResend"))->setToolTip(i18nc("@info:tooltip", \
"Boost")); + }
+
+ d->btnFavorite = addButton(QLatin1String("btnFavorite"), i18nc("@info:tooltip", \
"Favourite"), QLatin1String("rating")); + d->btnFavorite->setCheckable(true);
+ connect(d->btnFavorite, SIGNAL(clicked(bool)), this, SLOT(toggleFavorite()));
+ updateFavStat();
+}
+
+void MastodonPostWidget::slotResendPost()
+{
+ qCDebug(CHOQOK);
+ setReadWithSignal();
+ MastodonMicroBlog *microBlog = qobject_cast<MastodonMicroBlog \
*>(currentAccount()->microblog()); + microBlog->toggleReblog(currentAccount(), \
currentPost()); +}
+
+void MastodonPostWidget::toggleFavorite()
+{
+ qCDebug(CHOQOK);
+ setReadWithSignal();
+ MastodonMicroBlog *microBlog = qobject_cast<MastodonMicroBlog \
*>(currentAccount()->microblog()); + connect(microBlog, \
SIGNAL(favorite(Choqok::Account*,Choqok::Post*)), + this, \
SLOT(slotToggleFavorite(Choqok::Account*,Choqok::Post*))); + \
microBlog->toggleFavorite(currentAccount(), currentPost()); +}
+
+void MastodonPostWidget::slotToggleFavorite(Choqok::Account *, Choqok::Post *)
+{
+ qCDebug(CHOQOK);
+ updateFavStat();
+}
+
+void MastodonPostWidget::updateFavStat()
+{
+ d->btnFavorite->setChecked(currentPost()->isFavorited);
+ if (currentPost()->isFavorited) {
+ d->btnFavorite->setIcon(QIcon::fromTheme(QLatin1String("rating")));
+ } else {
+ d->btnFavorite->setIcon(unFavIcon);
+ }
+}
diff --git a/microblogs/mastodon/mastodonpostwidget.h \
b/microblogs/mastodon/mastodonpostwidget.h new file mode 100644
index 00000000..3c3753ff
--- /dev/null
+++ b/microblogs/mastodon/mastodonpostwidget.h
@@ -0,0 +1,56 @@
+/*
+ This file is part of Choqok, the KDE micro-blogging client
+
+ Copyright (C) 2017 Andrea Scarpino <scarpino@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License or (at your option) version 3 or any later version
+ accepted by the membership of KDE e.V. (or its successor approved
+ by the membership of KDE e.V.), which shall act as a proxy
+ defined in Section 14 of version 3 of the license.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see http://www.gnu.org/licenses/
+*/
+
+#ifndef MASTODONPOSTWIDGET_H
+#define MASTODONPOSTWIDGET_H
+
+#include "postwidget.h"
+
+class MastodonPostWidget : public Choqok::UI::PostWidget
+{
+ Q_OBJECT
+public:
+ explicit MastodonPostWidget(Choqok::Account *account, Choqok::Post *post, \
QWidget *parent = 0); + virtual ~MastodonPostWidget();
+
+ virtual QString generateSign() override;
+
+ virtual void initUi() override;
+
+protected Q_SLOTS:
+ virtual void slotResendPost() override;
+
+ void slotToggleFavorite(Choqok::Account *, Choqok::Post *);
+
+ void toggleFavorite();
+
+protected:
+ static const QIcon unFavIcon;
+
+private:
+ void updateFavStat();
+
+ class Private;
+ Private *const d;
+};
+
+#endif // MASTODONPOSTWIDGET_H
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic