diff --git a/microblogs/pumpio/CMakeLists.txt b/microblogs/pumpio/CMakeLists.txt index 40d549d0..eee7cee1 100644 --- a/microblogs/pumpio/CMakeLists.txt +++ b/microblogs/pumpio/CMakeLists.txt @@ -1,34 +1,35 @@ include_directories( ${CHOQOK_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR} ) set(choqok_pumpio_SRCS pumpioaccount.cpp + pumpiocomposerwidget.cpp pumpioeditaccountwidget.cpp pumpiomessagedialog.cpp pumpiomicroblog.cpp pumpiopost.cpp pumpiopostwidget.cpp pumpioshowthread.cpp ) kde4_add_ui_files(choqok_pumpio_SRCS pumpioeditaccountwidget.ui pumpiomessagedialog.ui pumpioshowthread.ui ) kde4_add_plugin(choqok_pumpio ${choqok_pumpio_SRCS}) target_link_libraries(choqok_pumpio choqok ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBS} ${QTOAUTH_LIBRARY} ${QJSON_LIBRARY} ) install(TARGETS choqok_pumpio DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES choqok_pumpio.desktop DESTINATION ${SERVICES_INSTALL_DIR}) add_subdirectory(icons) \ No newline at end of file diff --git a/microblogs/pumpio/pumpioaccount.h b/microblogs/pumpio/pumpioaccount.h index f3e682f0..ed752256 100644 --- a/microblogs/pumpio/pumpioaccount.h +++ b/microblogs/pumpio/pumpioaccount.h @@ -1,73 +1,73 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2013 Andrea Scarpino 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 PUMPIOACCOUNT_H #define PUMPIOACCOUNT_H #include #include "account.h" #include "choqoktypes.h" class PumpIOMicroBlog; class PumpIOAccount : public Choqok::Account { Q_OBJECT public: explicit PumpIOAccount(PumpIOMicroBlog* parent, const QString& alias); virtual ~PumpIOAccount(); + virtual void writeConfig(); + QString host(); void setHost(const QString& host); QString consumerKey(); void setConsumerKey(const QString& consumerKey); QString consumerSecret(); void setConsumerSecret(const QString& consumerSecret); QString token(); void setToken(const QString& token); QString tokenSecret(); void setTokenSecret(const QString& tokenSecret); QStringList following(); void setFollowing(const QStringList following); QVariantList lists(); void setLists(const QVariantList lists); QString webfingerID(); QOAuth::Interface *oAuth(); - virtual void writeConfig(); - private: class Private; Private *d; }; #endif // PUMPIOACCOUNT_H diff --git a/microblogs/pumpio/pumpiocomposerwidget.cpp b/microblogs/pumpio/pumpiocomposerwidget.cpp new file mode 100644 index 00000000..d5b47438 --- /dev/null +++ b/microblogs/pumpio/pumpiocomposerwidget.cpp @@ -0,0 +1,168 @@ +/* + This file is part of Choqok, the KDE micro-blogging client + + Copyright (C) 2013 Andrea Scarpino + Copyright (C) 2008-2012 Mehrdad Momeny + + 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 "pumpiocomposerwidget.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "account.h" +#include "choqoktextedit.h" +#include "shortenmanager.h" + +#include "pumpiomicroblog.h" + +class PumpIOComposerWidget::Private +{ +public: + QString mediumToAttach; + KPushButton *btnAttach; + QPointer mediumName; + QPointer btnCancel; + QGridLayout *editorLayout; +}; + +PumpIOComposerWidget::PumpIOComposerWidget(Choqok::Account* account, + QWidget* parent): + ComposerWidget(account, parent) + , d(new Private) +{ + d->editorLayout = qobject_cast(editorContainer()->layout()); + d->btnAttach = new KPushButton(editorContainer()); + d->btnAttach->setIcon(KIcon("mail-attachment")); + d->btnAttach->setToolTip(i18n("Attach a file")); + d->btnAttach->setMaximumWidth(d->btnAttach->height()); + connect(d->btnAttach, SIGNAL(clicked(bool)), this, SLOT(attachMedia())); + QVBoxLayout *vLayout = new QVBoxLayout; + vLayout->addWidget(d->btnAttach); + vLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::MinimumExpanding)); + d->editorLayout->addItem(vLayout, 0, 1); +} + +PumpIOComposerWidget::~PumpIOComposerWidget() +{ + delete d; +} + +void PumpIOComposerWidget::submitPost(const QString& text) +{ + kDebug(); + editorContainer()->setEnabled(false); + QString txt = text; + if (currentAccount()->microblog()->postCharLimit() && + txt.size() > (int) currentAccount()->microblog()->postCharLimit()) { + txt = Choqok::ShortenManager::self()->parseText(txt); + } + setPostToSubmit(0L); + setPostToSubmit(new Choqok::Post); + postToSubmit()->content = txt; + if (!replyToId.isEmpty()) { + postToSubmit()->replyToPostId = replyToId; + } + connect(currentAccount()->microblog(), SIGNAL(postCreated(Choqok::Account*,Choqok::Post*)), + this, SLOT(slotPostSubmited(Choqok::Account*,Choqok::Post*))); + connect(currentAccount()->microblog(), + SIGNAL(errorPost(Choqok::Account*,Choqok::Post*,Choqok::MicroBlog::ErrorType, + QString,Choqok::MicroBlog::ErrorLevel)), this, + SLOT(slotErrorPost(Choqok::Account*,Choqok::Post*))); + btnAbort = new KPushButton(KIcon("dialog-cancel"), i18n("Abort"), this); + layout()->addWidget(btnAbort); + connect( btnAbort, SIGNAL(clicked(bool)), SLOT(abort()) ); + PumpIOMicroBlog *mBlog = qobject_cast(currentAccount()->microblog()); + + + if (d->mediumToAttach.isEmpty()) { + postToSubmit()->type = "note"; + mBlog->createPost(currentAccount(), postToSubmit()); + } else { + mBlog->createPostWithMedia(currentAccount(), postToSubmit(), d->mediumToAttach); + } +} + +void PumpIOComposerWidget::slotPostSubmited(Choqok::Account* theAccount, Choqok::Post* post) +{ + kDebug(); + if( currentAccount() == theAccount && post == postToSubmit() ) { + kDebug()<<"Accepted"; + disconnect(currentAccount()->microblog(), SIGNAL(postCreated(Choqok::Account*,Choqok::Post*)), + this, SLOT(slotPostSubmited(Choqok::Account*,Choqok::Post*)) ); + disconnect(currentAccount()->microblog(), + SIGNAL(errorPost(Choqok::Account*,Choqok::Post*,Choqok::MicroBlog::ErrorType, + QString,Choqok::MicroBlog::ErrorLevel)), + this, SLOT(slotErrorPost(Choqok::Account*,Choqok::Post*))); + if(btnAbort){ + btnAbort->deleteLater(); + } + editor()->clear(); + editorCleared(); + editorContainer()->setEnabled(true); + delete postToSubmit(); + postToSubmit() = 0L; + cancelAttach(); + currentAccount()->microblog()->updateTimelines(currentAccount()); + } +} + + +void PumpIOComposerWidget::attachMedia() +{ + kDebug(); + d->mediumToAttach = KFileDialog::getOpenFileName(KUrl("kfiledialog:///image?global"), + QString(), this, + i18n("Select Media to Upload")); + if (d->mediumToAttach.isEmpty()) { + kDebug() << "No file selected"; + return; + } + QString fileName = KUrl(d->mediumToAttach).fileName(); + if (!d->mediumName) { + d->mediumName = new QLabel(editorContainer()); + d->btnCancel = new KPushButton(editorContainer()); + d->btnCancel->setIcon(KIcon("list-remove")); + d->btnCancel->setToolTip(i18n("Discard Attachment")); + d->btnCancel->setMaximumWidth(d->btnCancel->height()); + connect(d->btnCancel, SIGNAL(clicked(bool)), SLOT(cancelAttach())); + + d->editorLayout->addWidget(d->mediumName, 1, 0); + d->editorLayout->addWidget(d->btnCancel, 1, 1); + } + d->mediumName->setText(i18n("Attaching %1", fileName)); + editor()->setFocus(); +} + +void PumpIOComposerWidget::cancelAttach() +{ + kDebug(); + delete d->mediumName; + d->mediumName = 0; + delete d->btnCancel; + d->btnCancel = 0; + d->mediumToAttach.clear(); +} diff --git a/microblogs/pumpio/pumpioaccount.h b/microblogs/pumpio/pumpiocomposerwidget.h similarity index 51% copy from microblogs/pumpio/pumpioaccount.h copy to microblogs/pumpio/pumpiocomposerwidget.h index f3e682f0..b782d95b 100644 --- a/microblogs/pumpio/pumpioaccount.h +++ b/microblogs/pumpio/pumpiocomposerwidget.h @@ -1,73 +1,50 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2013 Andrea Scarpino 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 PUMPIOACCOUNT_H -#define PUMPIOACCOUNT_H +#ifndef PUMPIOCOMPOSERWIDGET_H +#define PUMPIOCOMPOSERWIDGET_H -#include +#include "composerwidget.h" -#include "account.h" -#include "choqoktypes.h" -class PumpIOMicroBlog; - -class PumpIOAccount : public Choqok::Account +class PumpIOComposerWidget : public Choqok::UI::ComposerWidget { Q_OBJECT public: - explicit PumpIOAccount(PumpIOMicroBlog* parent, const QString& alias); - virtual ~PumpIOAccount(); - - QString host(); - void setHost(const QString& host); - - QString consumerKey(); - void setConsumerKey(const QString& consumerKey); - - QString consumerSecret(); - void setConsumerSecret(const QString& consumerSecret); - - QString token(); - void setToken(const QString& token); - - QString tokenSecret(); - void setTokenSecret(const QString& tokenSecret); - - QStringList following(); - void setFollowing(const QStringList following); - - QVariantList lists(); - void setLists(const QVariantList lists); + explicit PumpIOComposerWidget(Choqok::Account* account, QWidget* parent = 0); + virtual ~PumpIOComposerWidget(); - QString webfingerID(); - QOAuth::Interface *oAuth(); +protected Q_SLOTS: + virtual void submitPost(const QString& text); + virtual void slotPostSubmited(Choqok::Account* theAccount, Choqok::Post* post); - virtual void writeConfig(); + void cancelAttach(); + void attachMedia(); private: class Private; - Private *d; + Private * const d; }; -#endif // PUMPIOACCOUNT_H +#endif // PUMPIOCOMPOSERWIDGET_H \ No newline at end of file diff --git a/microblogs/pumpio/pumpioeditaccountwidget.cpp b/microblogs/pumpio/pumpioeditaccountwidget.cpp index 7bea0eff..afae296d 100644 --- a/microblogs/pumpio/pumpioeditaccountwidget.cpp +++ b/microblogs/pumpio/pumpioeditaccountwidget.cpp @@ -1,191 +1,192 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2013 Andrea Scarpino 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 "pumpioeditaccountwidget.h" #include #include #include #include #include #include #include #include #include #include "choqoktools.h" #include "accountmanager.h" #include "pumpioaccount.h" #include "pumpiomicroblog.h" PumpIOEditAccountWidget::PumpIOEditAccountWidget(PumpIOMicroBlog* microblog, PumpIOAccount* 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_webfingerid->setText(m_account->webfingerID()); isAuthenticated(); } else { QString newAccountAlias = microblog->serviceName(); const QString servName = newAccountAlias; int counter = 1; while (Choqok::AccountManager::self()->findAccount(newAccountAlias)) { newAccountAlias = QString("%1%2").arg(servName).arg(counter); counter++; } m_account = new PumpIOAccount(microblog, newAccountAlias); setAccount(m_account); kcfg_alias->setText(newAccountAlias); } } PumpIOEditAccountWidget::~PumpIOEditAccountWidget() { } Choqok::Account* PumpIOEditAccountWidget::apply() { m_account->setAlias(kcfg_alias->text()); m_account->setUsername(kcfg_webfingerid->text().split('@')[0]); m_account->writeConfig(); return m_account; } void PumpIOEditAccountWidget::authorizeUser() { + kDebug(); m_qoauth = new QOAuth::Interface(new KIO::Integration::AccessManager(this), this); if (m_account->consumerKey().isEmpty() || m_account->consumerSecret().isEmpty()) { registerClient(); } m_qoauth->setConsumerKey(m_account->consumerKey().toLocal8Bit()); m_qoauth->setConsumerSecret(m_account->consumerSecret().toLocal8Bit()); QOAuth::ParamMap oAuthParams; oAuthParams.insert("oauth_callback", "oob"); QOAuth::ParamMap oAuthRequest = m_qoauth->requestToken(m_account->host() + "/oauth/request_token", QOAuth::GET, QOAuth::HMAC_SHA1, oAuthParams); if (m_qoauth->error() == QOAuth::NoError) { const QString token = oAuthRequest.value(QOAuth::tokenParameterName()); const QString tokenSecret = oAuthRequest.value(QOAuth::tokenSecretParameterName()); KUrl oAuthAuthorizeURL(m_account->host() + "/oauth/authorize"); oAuthAuthorizeURL.addQueryItem("oauth_token", token); Choqok::openUrl(oAuthAuthorizeURL); QString verifier = KInputDialog::getText( i18n("PIN"), i18n("Enter the verifier code received from " + m_account->host().toAscii())); QOAuth::ParamMap oAuthVerifierParams; oAuthVerifierParams.insert("oauth_verifier", verifier.toUtf8()); QOAuth::ParamMap oAuthVerifierRequest = m_qoauth->accessToken( m_account->host() + "/oauth/access_token", QOAuth::POST, token.toLocal8Bit(), tokenSecret.toLocal8Bit(), QOAuth::HMAC_SHA1, oAuthVerifierParams); if (m_qoauth->error() == QOAuth::NoError) { m_account->setToken(oAuthVerifierRequest.value(QOAuth::tokenParameterName())); m_account->setTokenSecret(oAuthVerifierRequest.value(QOAuth::tokenSecretParameterName())); if (isAuthenticated()) { KMessageBox::information(this, i18n("Choqok is authorized successfully."), i18n("Authorized")); } } else { kDebug() << "QOAuth error: " + Choqok::qoauthErrorText(m_qoauth->error()); } } else { kDebug() << "QOAuth error: " + Choqok::qoauthErrorText(m_qoauth->error()); } } void PumpIOEditAccountWidget::registerClient() { if (kcfg_webfingerid->text().contains('@')) { m_account->setHost("https://" + kcfg_webfingerid->text().split('@')[1]); KUrl url(m_account->host() + "/api/client/register"); QByteArray data("{" " \"type\": \"client_associate\", " " \"application_type\": \"native\", " " \"application_name\": \"Choqok\" " "}"); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); if ( !job ) { kDebug() << "Cannot create an http POST request!"; return; } job->addMetaData("content-type", "Content-Type: application/json"); QEventLoop loop; connect(job, SIGNAL(result(KJob*)), &loop, SLOT(quit())); job->start(); loop.exec(); if (job->error()) { kDebug() << "An error occurred in Job"; return; } else { KIO::StoredTransferJob *stj = qobject_cast(job); bool ok; QJson::Parser parser; QVariantMap result = parser.parse(stj->data(), &ok).toMap(); if (ok) { m_account->setConsumerKey(result.value("client_id").toString()); m_account->setConsumerSecret(result.value("client_secret").toString()); } else { kDebug() << "Cannot parse JSON reply"; } } } else { kDebug() << "webfingerID is not valid"; } } bool PumpIOEditAccountWidget::validateData() { if (kcfg_webfingerid->text().isEmpty() || !kcfg_webfingerid->text().contains('@') || !isAuthenticated()) { return false; } else { return true; } } bool PumpIOEditAccountWidget::isAuthenticated() { if (m_account->token().isEmpty() || m_account->tokenSecret().isEmpty()) { return false; } else { kcfg_authorize->setIcon(KIcon("object-unlocked")); kcfg_authenticateLed->setState(KLed::On); kcfg_authenticateStatus->setText(i18n("Authenticated")); return true; } } diff --git a/microblogs/pumpio/pumpiomessagedialog.cpp b/microblogs/pumpio/pumpiomessagedialog.cpp index 7185d681..c50bbbd5 100644 --- a/microblogs/pumpio/pumpiomessagedialog.cpp +++ b/microblogs/pumpio/pumpiomessagedialog.cpp @@ -1,160 +1,166 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2013 Andrea Scarpino 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 "pumpiomessagedialog.h" +#include + #include "pumpioaccount.h" #include "pumpiomicroblog.h" #include "pumpiopost.h" class PumpIOMessageDialog::Private { public: Choqok::Account *account; }; PumpIOMessageDialog::PumpIOMessageDialog(Choqok::Account* theAccount, QWidget* parent, Qt::WindowFlags flags) : KDialog(parent, flags) , d(new Private) { d->account = theAccount; setupUi(this); setMainWidget(widget); PumpIOAccount* acc = qobject_cast(theAccount); if (acc) { Q_FOREACH (const QVariant& list, acc->lists()) { QVariantMap l = list.toMap(); QListWidgetItem *item = new QListWidgetItem; item->setText(l.value("name").toString()); item->setData(Qt::UserRole, l.value("id").toString()); toList->addItem(item); ccList->addItem(item->clone()); } //Lists are not sorted toList->sortItems(); ccList->sortItems(); Q_FOREACH (const QString& username, acc->following()) { QListWidgetItem *item = new QListWidgetItem; item->setText(PumpIOMicroBlog::userNameFromAcct(username)); item->setData(Qt::UserRole, username); toList->addItem(item); ccList->addItem(item->clone()); } } connect(btnReload, SIGNAL(clicked(bool)), this, SLOT(fetchFollowing())); connect(this, SIGNAL(okClicked()), this, SLOT(sendPost())); } PumpIOMessageDialog::~PumpIOMessageDialog() { + delete d; } void PumpIOMessageDialog::fetchFollowing() { + kDebug(); toList->clear(); ccList->clear(); PumpIOMicroBlog *microblog = qobject_cast(d->account->microblog()); if (microblog) { microblog->fetchFollowing(d->account); connect(microblog, SIGNAL(followingFetched(Choqok::Account*)), this, SLOT(slotFetchFollowing(Choqok::Account*))); } } void PumpIOMessageDialog::slotFetchFollowing(Choqok::Account* theAccount) { + kDebug(); if (theAccount == d->account) { PumpIOAccount* acc = qobject_cast(theAccount); if (acc) { Q_FOREACH (const QVariant& list, acc->lists()) { QVariantMap l = list.toMap(); QListWidgetItem *item = new QListWidgetItem; item->setText(l.value("name").toString()); item->setData(Qt::UserRole, l.value("id").toString()); toList->addItem(item); ccList->addItem(item->clone()); } toList->sortItems(); ccList->sortItems(); Q_FOREACH (const QString& username, acc->following()) { QListWidgetItem *item = new QListWidgetItem; item->setText(PumpIOMicroBlog::userNameFromAcct(username)); item->setData(Qt::UserRole, username); toList->addItem(item); ccList->addItem(item->clone()); } } } } void PumpIOMessageDialog::sendPost() { + kDebug(); PumpIOAccount* acc = qobject_cast(d->account); if (acc) { if (acc->following().isEmpty() || txtMessage->toPlainText().isEmpty() || (toList->selectedItems().isEmpty() || ccList->selectedItems().isEmpty())) { return; } hide(); PumpIOMicroBlog* microblog = qobject_cast(d->account->microblog()); if (microblog) { PumpIOPost* post = new PumpIOPost; post->content = txtMessage->toPlainText(); QVariantList to; Q_FOREACH (QListWidgetItem *item, toList->selectedItems()) { QVariantMap user; QString id = item->data(Qt::UserRole).toString(); if (id.contains("acct:")) { user.insert("objectType", "person"); } else { user.insert("objectType", "collection"); } user.insert("id", id); to.append(user); } QVariantList cc; Q_FOREACH (QListWidgetItem *item, ccList->selectedItems()) { QVariantMap user; QString id = item->data(Qt::UserRole).toString(); if (id.contains("acct:")) { user.insert("objectType", "person"); } else { user.insert("objectType", "collection"); } user.insert("id", id); cc.append(user); } microblog->createPost(acc, post, to, cc); } } } diff --git a/microblogs/pumpio/pumpiomicroblog.cpp b/microblogs/pumpio/pumpiomicroblog.cpp index 5b373f6b..419ddea0 100644 --- a/microblogs/pumpio/pumpiomicroblog.cpp +++ b/microblogs/pumpio/pumpiomicroblog.cpp @@ -1,1084 +1,1264 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2013 Andrea Scarpino + Copyright (C) 2008-2012 Mehrdad Momeny 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 "pumpiomicroblog.h" #include #include #include #include #include #include #include #include +#include #include "accountmanager.h" #include "application.h" #include "choqokbehaviorsettings.h" #include "notifymanager.h" #include "pumpioaccount.h" +#include "pumpiocomposerwidget.h" #include "pumpioeditaccountwidget.h" #include "pumpiomessagedialog.h" #include "pumpiopost.h" #include "pumpiopostwidget.h" class PumpIOMicroBlog::Private { public: Private():countOfTimelinesToSave(0) {} int countOfTimelinesToSave; }; K_PLUGIN_FACTORY( MyPluginFactory, registerPlugin < PumpIOMicroBlog > (); ) K_EXPORT_PLUGIN( MyPluginFactory( "choqok_pumpio" ) ) const QString PumpIOMicroBlog::inboxActivity("/api/user/%1/inbox"); const QString PumpIOMicroBlog::outboxActivity("/api/user/%1/feed"); PumpIOMicroBlog::PumpIOMicroBlog(QObject* parent, const QVariantList& args): MicroBlog(MyPluginFactory::componentData(), parent), d(new Private) { Q_UNUSED(args) setServiceName("Pump.io"); setServiceHomepageUrl("http://pump.io"); QStringList timelineNames; timelineNames << "Activity" << "Favorites" << "Inbox" << "Outbox"; setTimelineNames(timelineNames); setTimelinesInfo(); } PumpIOMicroBlog::~PumpIOMicroBlog() { + delete d; } void PumpIOMicroBlog::abortAllJobs(Choqok::Account* theAccount) { Q_FOREACH (KJob *job, m_accountJobs.keys(theAccount)) { job->kill(KJob::EmitResult); } } void PumpIOMicroBlog::abortCreatePost(Choqok::Account* theAccount, Choqok::Post* post) { if (m_createPostJobs.isEmpty()) { return; } if (post) { m_createPostJobs.key(post)->kill(KJob::EmitResult); return; } Q_FOREACH (KJob *job, m_createPostJobs.keys()){ if( m_accountJobs[job] == theAccount) { job->kill(KJob::EmitResult); } } } void PumpIOMicroBlog::aboutToUnload() { Q_FOREACH (Choqok::Account* acc, Choqok::AccountManager::self()->accounts()) { if (acc->microblog() == this){ d->countOfTimelinesToSave += acc->timelineNames().count(); } } emit saveTimelines(); } QMenu* PumpIOMicroBlog::createActionsMenu(Choqok::Account* theAccount, QWidget* parent) { QMenu *menu = MicroBlog::createActionsMenu(theAccount, parent); KAction *directMessge = new KAction(KIcon("mail-message-new"), i18n("Send Private Message..."), menu); directMessge->setData(theAccount->alias()); connect(directMessge, SIGNAL(triggered(bool)), this, SLOT(showDirectMessageDialog())); menu->addAction(directMessge); return menu; } +Choqok::UI::ComposerWidget* PumpIOMicroBlog::createComposerWidget(Choqok::Account* account, QWidget* parent) +{ + return new PumpIOComposerWidget(account, parent); +} + ChoqokEditAccountWidget* PumpIOMicroBlog::createEditAccountWidget(Choqok::Account* account, QWidget* parent) { PumpIOAccount *acc = qobject_cast(account); if (acc || !account) { return new PumpIOEditAccountWidget(this, acc, parent); } else { kDebug() << "Account passed here was not a valid PumpIOAccount!"; return 0; } } Choqok::Account* PumpIOMicroBlog::createNewAccount(const QString& alias) { PumpIOAccount *acc = qobject_cast( Choqok::AccountManager::self()->findAccount(alias) ); if (!acc) { return new PumpIOAccount(this, alias); } else { kDebug() << "Cannot create a new PumpIOAccount!"; return 0; } } void PumpIOMicroBlog::createPost(Choqok::Account* theAccount, Choqok::Post* post) { QVariantList to; QVariantMap thePublic; thePublic.insert("objectType", "collection"); thePublic.insert("id", "http://activityschema.org/collection/public"); to.append(thePublic); createPost(theAccount, post, to); } void PumpIOMicroBlog::createPost(Choqok::Account* theAccount, Choqok::Post* post, const QVariantList& to, const QVariantList& cc) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { QVariantMap object; - object.insert("objectType", "note"); + if (!post->postId.isEmpty()) { + object.insert("id", post->postId); + } + object.insert("objectType", post->type); object.insert("content", QUrl::toPercentEncoding(post->content)); QVariantMap item; item.insert("verb", "post"); item.insert("object", object); item.insert("to", to); item.insert("cc", cc); QJson::Serializer serializer; const QByteArray data = serializer.serialize(item); KUrl url(acc->host()); url.addPath(outboxActivity.arg(acc->username())); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); job->addMetaData("content-type", "Content-Type: application/json"); job->addMetaData("customHTTPHeader", authorizationMetaData(acc, url, QOAuth::POST)); if (!job) { kDebug() << "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 { kDebug() << "theAccount is not a PumpIOAccount!"; } } +void PumpIOMicroBlog::createPostWithMedia(Choqok::Account* theAccount, Choqok::Post* post, + const QString& filePath) +{ + PumpIOAccount *acc = qobject_cast(theAccount); + if (acc) { + QFile media(filePath); + QByteArray data; + if (media.open(QIODevice::ReadOnly)) { + data = media.readAll(); + media.close(); + } else { + kDebug() << "Cannot read the file"; + return; + } + + KMimeType::Ptr mimetype = KMimeType::findByNameAndContent(filePath, data); + const QString mime = mimetype.data()->name(); + if (mime == "application/octet-stream") { + kDebug() << "Cannot retrieve file mimetype"; + return; + } + + KUrl url(acc->host()); + url.addPath(QString("/api/user/%1/uploads").arg(acc->username())); + KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); + job->addMetaData("content-type", "Content-Type: " + mime); + job->addMetaData("customHTTPHeader", authorizationMetaData(acc, url, QOAuth::POST)); + if (!job) { + kDebug() << "Cannot create an http POST request!"; + return; + } + m_accountJobs[job] = acc; + m_uploadJobs[job] = post; + connect(job, SIGNAL(result(KJob*)), this, SLOT(slotUpload(KJob*))); + job->start(); + } else { + kDebug() << "theAccount is not a PumpIOAccount!"; + } +} + Choqok::UI::PostWidget* PumpIOMicroBlog::createPostWidget(Choqok::Account* account, Choqok::Post* post, QWidget* parent) { return new PumpIOPostWidget(account, post, parent); } void PumpIOMicroBlog::fetchPost(Choqok::Account* theAccount, Choqok::Post* post) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { if (!post->link.startsWith(acc->host())) { kDebug() << "You can only fetch posts from your host!"; return; } KUrl url(post->link); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); if (!job) { kDebug() << "Cannot create an http GET request!"; return; } job->addMetaData("customHTTPHeader", authorizationMetaData(acc, url, QOAuth::GET)); m_accountJobs[job] = acc; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotFetchPost(KJob*))); job->start(); } else { kDebug() << "theAccount is not a PumpIOAccount!"; } } void PumpIOMicroBlog::removePost(Choqok::Account* theAccount, Choqok::Post* post) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { QVariantMap object; object.insert("id", post->postId.toString()); object.insert("objectType", post->type); QVariantMap item; item.insert("verb", "delete"); item.insert("object", object); QJson::Serializer serializer; const QByteArray data = serializer.serialize(item); KUrl url(acc->host()); url.addPath(outboxActivity.arg(acc->username())); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); job->addMetaData("content-type", "Content-Type: application/json"); job->addMetaData("customHTTPHeader", authorizationMetaData(acc, url, QOAuth::POST)); if (!job) { kDebug() << "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 { kDebug() << "theAccount is not a PumpIOAccount!"; } } QList< Choqok::Post* > PumpIOMicroBlog::loadTimeline(Choqok::Account* account, const QString& timelineName) { QList< Choqok::Post* > list; const QString fileName = Choqok::AccountManager::generatePostBackupFileName(account->alias(), timelineName); const KConfig postsBackup( "choqok/" + fileName, KConfig::NoGlobals, "data" ); const QStringList tmpList = postsBackup.groupList(); // don't load old archives if (tmpList.isEmpty() || !(QDateTime::fromString(tmpList.first()).isValid())) { return list; } QList groupList; Q_FOREACH (const QString& str, tmpList) { groupList.append(QDateTime::fromString(str)); } qSort(groupList); PumpIOPost *st; Q_FOREACH (const QDateTime& datetime, groupList) { st = new PumpIOPost; 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.location = grp.readEntry("authorLocation", QString()); st->author.description = grp.readEntry("authorDescription" , QString()); st->author.profileImageUrl = grp.readEntry("authorProfileImageUrl", QString()); st->author.homePageUrl = grp.readEntry("authorHomePageUrl", QString()); st->type = grp.readEntry("type", QString()); st->isRead = grp.readEntry("isRead", true); st->conversationId = grp.readEntry("conversationId", QString()); st->to = grp.readEntry("to", QStringList()); st->cc = grp.readEntry("cc", QStringList()); st->shares = grp.readEntry("shares", QStringList()); st->replies = grp.readEntry("replies", QString()); list.append(st); } if (!list.isEmpty()) { setLastTimelineId(account, timelineName, list.last()->conversationId); } return list; } QString PumpIOMicroBlog::postUrl(Choqok::Account* account, const QString& username, const QString& postId) const { Q_UNUSED(account); return QString(postId).replace("/api/", '/' + username + '/'); } QString PumpIOMicroBlog::profileUrl(Choqok::Account* account, const QString& username) const { Q_UNUSED(account) if (username.contains("acct:")) { return QString("https://%1/%2").arg(hostFromAcct(username)).arg(userNameFromAcct(username)); } else { return username; } } void PumpIOMicroBlog::saveTimeline(Choqok::Account* account, const QString& timelineName, const QList< Choqok::UI::PostWidget* >& timeline) { const QString fileName = Choqok::AccountManager::generatePostBackupFileName(account->alias(), timelineName); KConfig postsBackup("choqok/" + fileName, KConfig::NoGlobals, "data"); ///Clear previous data: Q_FOREACH (const QString& group, postsBackup.groupList()) { postsBackup.deleteGroup(group); } QList< Choqok::UI::PostWidget *>::const_iterator it, endIt = timeline.constEnd(); for (it = timeline.constBegin(); it != endIt; ++it) { PumpIOPost *post = dynamic_cast((*it)->currentPost()); KConfigGroup grp(&postsBackup, post->creationDateTime.toString()); grp.writeEntry("creationDateTime", post->creationDateTime); grp.writeEntry("postId", post->postId.toString()); 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.toString()); grp.writeEntry("authorRealName", post->author.realName); grp.writeEntry("authorUserName", post->author.userName); grp.writeEntry("authorLocation" , post->author.location); grp.writeEntry("authorDescription" , post->author.description); grp.writeEntry("authorProfileImageUrl", post->author.profileImageUrl); grp.writeEntry("authorHomePageUrl" , post->author.homePageUrl); grp.writeEntry("type" , post->type); grp.writeEntry("isRead", post->isRead); grp.writeEntry("conversationId", post->conversationId.toString()); grp.writeEntry("to", post->to); grp.writeEntry("cc", post->cc); grp.writeEntry("shares", post->shares); grp.writeEntry("replies", post->replies); } postsBackup.sync(); if (Choqok::Application::isShuttingDown()) { --d->countOfTimelinesToSave; if (d->countOfTimelinesToSave < 1) { emit readyForUnload(); } } } Choqok::TimelineInfo* PumpIOMicroBlog::timelineInfo(const QString& timelineName) { return m_timelinesInfos.value(timelineName); } void PumpIOMicroBlog::updateTimelines(Choqok::Account* theAccount) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { Q_FOREACH (const QString& timeline, acc->timelineNames()) { KUrl url(acc->host()); url.addPath(m_timelinesPaths[timeline].arg(acc->username())); QOAuth::ParamMap oAuthParams; const ChoqokId lastActivityId(lastTimelineId(theAccount, timeline)); if (!lastActivityId.isEmpty()) { oAuthParams.insert("count", QByteArray::number(200)); oAuthParams.insert("since", KUrl::toPercentEncoding(lastActivityId.toString())); } else { oAuthParams.insert("count", QByteArray::number(Choqok::BehaviorSettings::countOfPosts())); } KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); if (!job) { kDebug() << "Cannot create an http GET request!"; continue; } job->addMetaData("customHTTPHeader", authorizationMetaData(acc, url, QOAuth::GET, oAuthParams)); m_timelinesRequests[job] = timeline; m_accountJobs[job] = acc; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotUpdateTimeline(KJob*))); job->start(); } } else { kDebug() << "theAccount is not a PumpIOAccount!"; } } void PumpIOMicroBlog::fetchFollowing(Choqok::Account* theAccount) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { KUrl url(acc->host()); url.addPath(QString("/api/user/%1/following").arg(acc->username())); QOAuth::ParamMap oAuthParams; oAuthParams.insert("count", QByteArray::number(200)); if (!acc->following().isEmpty()) { oAuthParams.insert("since", KUrl::toPercentEncoding(acc->following().last())); } KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); if (!job) { kDebug() << "Cannot create an http GET request!"; return; } job->addMetaData("customHTTPHeader", authorizationMetaData(acc, url, QOAuth::GET, oAuthParams)); m_accountJobs[job] = acc; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotFollowing(KJob*))); job->start(); } else { kDebug() << "theAccount is not a PumpIOAccount!"; } } void PumpIOMicroBlog::fetchLists(Choqok::Account* theAccount) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { KUrl url(acc->host()); url.addPath(QString("/api/user/%1/lists/person").arg(acc->username())); QOAuth::ParamMap oAuthParams; oAuthParams.insert("count", QByteArray::number(200)); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); if (!job) { kDebug() << "Cannot create an http GET request!"; return; } job->addMetaData("customHTTPHeader", authorizationMetaData(acc, url, QOAuth::GET, oAuthParams)); m_accountJobs[job] = acc; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotLists(KJob*))); job->start(); } else { kDebug() << "theAccount is not a PumpIOAccount!"; } } void PumpIOMicroBlog::share(Choqok::Account* theAccount, Choqok::Post* post) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { QVariantMap object; object.insert("objectType", post->type); object.insert("id", post->postId.toString()); QVariantMap item; item.insert("verb", "share"); item.insert("object", object); QJson::Serializer serializer; const QByteArray data = serializer.serialize(item); KUrl url(acc->host()); url.addPath(outboxActivity.arg(acc->username())); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); job->addMetaData("content-type", "Content-Type: application/json"); job->addMetaData("customHTTPHeader", authorizationMetaData(acc, url, QOAuth::POST)); if (!job) { kDebug() << "Cannot create an http POST request!"; return; } m_accountJobs[job] = acc; m_shareJobs[job] = post; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotShare(KJob*))); job->start(); } else { kDebug() << "theAccount is not a PumpIOAccount!"; } } void PumpIOMicroBlog::toggleFavorite(Choqok::Account* theAccount, Choqok::Post* post) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { QVariantMap object; object.insert("objectType", post->type); object.insert("id", post->postId.toString()); QVariantMap item; item.insert("verb", post->isFavorited ? "unfavorite" : "favorite"); item.insert("object", object); QJson::Serializer serializer; const QByteArray data = serializer.serialize(item); KUrl url(acc->host()); url.addPath(outboxActivity.arg(acc->username())); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); job->addMetaData("content-type", "Content-Type: application/json"); job->addMetaData("customHTTPHeader", authorizationMetaData(acc, url, QOAuth::POST)); if (!job) { kDebug() << "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 { kDebug() << "theAccount is not a PumpIOAccount!"; } } void PumpIOMicroBlog::showDirectMessageDialog() { + kDebug(); const QString alias = qobject_cast(sender())->data().toString(); PumpIOAccount *theAccount = qobject_cast(Choqok::AccountManager::self()->findAccount(alias)); PumpIOMessageDialog *msg = new PumpIOMessageDialog(theAccount, Choqok::UI::Global::mainWindow()); msg->show(); } void PumpIOMicroBlog::slotCreatePost(KJob* job) { + kDebug(); if (!job) { kDebug() << "Job is null pointer"; return; } Choqok::Post *post = m_createPostJobs.take(job); Choqok::Account *theAccount = m_accountJobs.take(job); if (!post || !theAccount) { kDebug() << "Account or Post is NULL pointer"; return; } int ret = 1; if (job->error()) { kDebug() << "Job Error: " << job->errorString(); } else { KIO::StoredTransferJob* j = qobject_cast(job); bool ok; QJson::Parser parser; const QVariantMap reply = parser.parse(j->data(), &ok).toMap(); if (ok) { if (!reply["object"].toMap().value("id").toString().isEmpty()) { Choqok::NotifyManager::success(i18n("New post submitted successfully")); ret = 0; emit postCreated(theAccount, post); } } else { kDebug() << "Cannot parse JSON reply"; } } if (ret) { emit errorPost(theAccount, post, Choqok::MicroBlog::CommunicationError, i18n("Creating the new post failed. %1", job->errorString()), MicroBlog::Critical); } } void PumpIOMicroBlog::slotFavorite(KJob* job) { + kDebug(); if (!job) { kDebug() << "Job is null pointer"; return; } Choqok::Post *post = m_favoriteJobs.take(job); Choqok::Account *theAccount = m_accountJobs.take(job); if (!post || !theAccount) { kDebug() << "Account or Post is NULL pointer"; return; } if (job->error()) { kDebug() << "Job Error: " << job->errorString(); emit error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Cannot set the post as (un)favorite. %1", job->errorString())); } else { post->isFavorited = !post->isFavorited; emit favorite(theAccount, post); } } void PumpIOMicroBlog::slotFetchPost(KJob* job) { + kDebug(); if (!job) { kDebug() << "Job is null pointer"; return; } Choqok::Account *theAccount = m_accountJobs.take(job); if (!theAccount) { kDebug() << "Account or postId is NULL pointer"; return; } int ret = 1; if (job->error()) { kDebug() << "Job Error: " << job->errorString(); } else { KIO::StoredTransferJob* j = qobject_cast(job); bool ok; QJson::Parser parser; const QVariantMap reply = parser.parse(j->data(), &ok).toMap(); if (ok) { PumpIOPost* post = new PumpIOPost; readPost(reply, post); ret = 0; emit postFetched(theAccount, post); } else { kDebug() << "Cannot parse JSON reply"; } } if (ret) { emit error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Cannot fetch post. %1", job->errorString()), MicroBlog::Critical); } } void PumpIOMicroBlog::slotFetchReplies(KJob* job) { + kDebug(); if (!job) { kDebug() << "Job is null pointer"; return; } Choqok::Account *theAccount = m_accountJobs.take(job); if (!theAccount) { kDebug() << "Account or postId is NULL pointer"; return; } int ret = 1; if (job->error()) { kDebug() << "Job Error: " << job->errorString(); } else { KIO::StoredTransferJob* j = qobject_cast(job); bool ok; QJson::Parser parser; const QVariantMap reply = parser.parse(j->data(), &ok).toMap(); if (ok) { const QVariantList items = reply["items"].toList(); for (int i = items.size() - 1; i >= 0; i--) { QVariantMap item = items.at(i).toMap(); PumpIOPost* r = new PumpIOPost; readPost(item, r); r->replyToPostId = reply["url"].toString().remove("/replies"); emit postFetched(theAccount, r); } ret = 0; } else { kDebug() << "Cannot parse JSON reply"; } } if (ret) { emit error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Cannot fetch replies. %1", job->errorString()), MicroBlog::Critical); } } void PumpIOMicroBlog::slotFollowing(KJob* job) { + kDebug(); if (!job) { kDebug() << "Job is null pointer"; return; } Choqok::Account *theAccount = m_accountJobs.take(job); if (!theAccount) { kDebug() << "Account is NULL pointer"; return; } if (job->error()) { kDebug() << "Job Error: " << job->errorString(); } bool ret = 1; PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { Choqok::UI::Global::mainWindow()->showStatusMessage( i18n("Following list for account %1 has been updated.", acc->username())); KIO::StoredTransferJob* j = qobject_cast(job); bool ok; QJson::Parser parser; const QVariantList items = parser.parse(j->data(), &ok).toMap().value("items").toList(); if (ok) { QStringList following; Q_FOREACH (const QVariant& element, items) { following.append(element.toMap().value("id").toString()); } acc->setFollowing(following); ret = 0; emit followingFetched(acc); } else { kDebug() << "Cannot parse JSON reply"; } } else { kDebug() << "theAccount is not a PumpIOAccount!"; } if (ret) { emit error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Cannot retrieve the following list. %1", job->errorString())); } } void PumpIOMicroBlog::slotLists(KJob* job) { + kDebug(); if (!job) { kDebug() << "Job is null pointer"; return; } Choqok::Account *theAccount = m_accountJobs.take(job); if (!theAccount) { kDebug() << "Account is NULL pointer"; return; } if (job->error()) { kDebug() << "Job Error: " << job->errorString(); } bool ret = 1; PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { Choqok::UI::Global::mainWindow()->showStatusMessage( i18n("Lists for account %1 has been updated.", acc->username())); KIO::StoredTransferJob* j = qobject_cast(job); bool ok; QJson::Parser parser; const QVariantList items = parser.parse(j->data(), &ok).toMap().value("items").toList(); if (ok) { QVariantList lists; Q_FOREACH (const QVariant& element, items) { QVariantMap e = element.toMap(); QVariantMap list; list.insert("id", e.value("id").toString()); list.insert("name", e.value("displayName").toString()); lists.append(list); } acc->setLists(lists); ret = 0; emit listsFetched(acc); } else { kDebug() << "Cannot parse JSON reply"; } } else { kDebug() << "theAccount is not a PumpIOAccount!"; } if (ret) { emit error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Cannot retrieve the lists. %1", job->errorString())); } } void PumpIOMicroBlog::slotShare(KJob* job) { + kDebug(); if (!job) { kDebug() << "Job is null pointer"; return; } Choqok::Post *post = m_shareJobs.take(job); Choqok::Account *theAccount = m_accountJobs.take(job); if (!post || !theAccount) { kDebug() << "Account or Post is NULL pointer"; return; } int ret = 1; if (job->error()) { kDebug() << "Job Error: " << job->errorString(); } else { Choqok::UI::Global::mainWindow()->showStatusMessage( i18n("The post has been shared!.")); KIO::StoredTransferJob* j = qobject_cast(job); bool ok; QJson::Parser parser; const QVariantMap object = parser.parse(j->data(), &ok).toMap().value("object").toMap(); if (ok) { ret = 0; } else { kDebug() << "Cannot parse JSON reply"; } } if (ret) { emit error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Cannot share the post. %1", job->errorString())); } } void PumpIOMicroBlog::slotRemovePost(KJob* job) { + kDebug(); if (!job) { kDebug() << "Job is null pointer"; return; } Choqok::Post *post = m_removePostJobs.take(job); Choqok::Account *theAccount = m_accountJobs.take(job); if (!post || !theAccount) { kDebug() << "Account or Post is NULL pointer"; return; } int ret = 1; if (job->error()) { kDebug() << "Job Error: " << job->errorString(); } else { KIO::StoredTransferJob* j = qobject_cast(job); bool ok; QJson::Parser parser; const QVariantMap object = parser.parse(j->data(), &ok).toMap().value("object").toMap(); if (ok) { if (!object["deleted"].toString().isEmpty()) { Choqok::NotifyManager::success(i18n("Post removed successfully")); ret = 0; emit postRemoved(theAccount, post); } } else { kDebug() << "Cannot parse JSON reply"; } } if (ret) { emit errorPost(theAccount, post, Choqok::MicroBlog::CommunicationError, i18n("Removing the post failed. %1", job->errorString()), MicroBlog::Critical); } } +void PumpIOMicroBlog::slotUpdatePost(KJob* job) +{ + kDebug(); + if (!job) { + kDebug() << "Job is null pointer"; + return; + } + Choqok::Post *post = m_updateJobs.take(job); + Choqok::Account *account = m_accountJobs.take(job); + if (!account) { + kDebug() << "Account or Post is NULL pointer"; + return; + } + int ret = 1; + if (job->error()) { + kDebug() << "Job Error: " << job->errorString(); + } else { + KIO::StoredTransferJob* j = qobject_cast(job); + bool ok; + QJson::Parser parser; + const QVariantMap reply = parser.parse(j->data(), &ok).toMap(); + if (ok) { + ret = 0; + createPost(account, post); + } else { + kDebug() << "Cannot parse JSON reply"; + } + } + + if (ret) { + emit error(account, Choqok::MicroBlog::CommunicationError, + i18n("An error occurred when updating the post")); + } +} + void PumpIOMicroBlog::slotUpdateTimeline(KJob* job) { + kDebug(); if (!job) { kDebug() << "Job is null pointer"; return; } Choqok::Account *account = m_accountJobs.take(job); if (!account) { kDebug() << "Account or Post is NULL pointer"; return; } if (job->error()) { kDebug() << "Job Error: " << job->errorString(); emit error(account, Choqok::MicroBlog::CommunicationError, i18n("An error occurred when fetching the timeline")); } else { KIO::StoredTransferJob* j = qobject_cast(job); const QList list = readTimeline(j->data()); const QString timeline(m_timelinesRequests.take(job)); if (!list.isEmpty()) { setLastTimelineId(account, timeline, list.last()->conversationId); } emit timelineDataReceived(account, timeline, list); } } +void PumpIOMicroBlog::slotUpload(KJob* job) +{ + kDebug(); + if (!job) { + kDebug() << "Job is null pointer"; + return; + } + Choqok::Post *post = m_uploadJobs.take(job); + Choqok::Account *account = m_accountJobs.take(job); + if (!account) { + kDebug() << "Account or Post is NULL pointer"; + return; + } + int ret = 1; + if (job->error()) { + kDebug() << "Job Error: " << job->errorString(); + } else { + KIO::StoredTransferJob* j = qobject_cast(job); + bool ok; + QJson::Parser parser; + const QVariantMap reply = parser.parse(j->data(), &ok).toMap(); + if (ok) { + const QString id = reply["id"].toString(); + if (!id.isEmpty()) { + post->postId = id; + post->type = reply["objectType"].toString(); + ret = 0; + updatePost(account, post); + } + } else { + kDebug() << "Cannot parse JSON reply"; + } + + } + + if (ret) { + emit error(account, Choqok::MicroBlog::CommunicationError, + i18n("An error occurred when uploading the media")); + } +} + QString PumpIOMicroBlog::authorizationMetaData(PumpIOAccount* account, const KUrl& url, const QOAuth::HttpMethod& method, const QOAuth::ParamMap& paramMap) const { const QByteArray authorization = account->oAuth()->createParametersString(url.url(), method, account->token().toLocal8Bit(), account->tokenSecret().toLocal8Bit(), QOAuth::HMAC_SHA1, paramMap, QOAuth::ParseForHeaderArguments); return "Authorization: " + authorization; } void PumpIOMicroBlog::fetchReplies(Choqok::Account* theAccount, const QString& url) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { if (!url.startsWith(acc->host())) { kDebug() << "You can only fetch replies from your host!"; return; } KUrl u(url); KIO::StoredTransferJob *job = KIO::storedGet(u, KIO::Reload, KIO::HideProgressInfo); if (!job) { kDebug() << "Cannot create an http GET request!"; return; } job->addMetaData("customHTTPHeader", authorizationMetaData(acc, u, QOAuth::GET)); m_accountJobs[job] = acc; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotFetchReplies(KJob*))); job->start(); } else { kDebug() << "theAccount is not a PumpIOAccount!"; } } ChoqokId PumpIOMicroBlog::lastTimelineId(Choqok::Account* theAccount, const QString& timeline) const { kDebug() << "Latest ID for timeline " << timeline << m_timelinesLatestIds[theAccount][timeline]; return m_timelinesLatestIds[theAccount][timeline]; } Choqok::Post* PumpIOMicroBlog::readPost(const QVariantMap& var, Choqok::Post* post) { PumpIOPost *p = dynamic_cast< PumpIOPost* >(post); if (p) { QVariantMap object; if (var.value("verb").toString() == "post" || var.value("verb").toString() == "share") { object = var["object"].toMap(); } else { object = var; } QTextDocument content; if (!object["displayName"].isNull()) { content.setHtml(object["displayName"].toString()); p->content = content.toPlainText().trimmed(); p->content += '\n'; } content.setHtml(object["content"].toString()); p->content += content.toPlainText().trimmed(); if (!object["fullImage"].isNull()) { const QString imagePath = object["fullImage"].toMap().value("url").toString(); if (!imagePath.isEmpty()) { p->content += '\n' + imagePath; } } p->creationDateTime = QDateTime::fromString(var["published"].toString(), "yyyy-MM-dd'T'hh:mm:ss'Z'"); if (object["pump_io"].isNull()) { p->link = object["id"].toString(); } else { p->link = object["pump_io"].toMap().value("proxyURL").toString(); } p->type = object["objectType"].toString(); p->isFavorited = object["liked"].toBool(); if (p->isFavorited) { p->isRead = true; } p->postId = object["id"].toString(); p->conversationId = var["id"].toString(); QString author; var["author"].isNull() ? author = "actor" : author = "author"; QVariantMap actor; if (var.value("verb").toString() == "share") { actor = object["author"].toMap(); const QVariantList shares = object["shares"].toMap().value("items").toList(); Q_FOREACH (const QVariant& element, shares) { p->shares.append(element.toMap().value("id").toString()); } } else { actor = var[author].toMap(); } p->author.userId = actor["id"].toString(); p->author.userName = actor["preferredUsername"].toString(); p->author.realName = actor["displayName"].toString(); p->author.homePageUrl = actor["url"].toString(); p->author.location = actor["location"].toMap().value("displayName").toString(); p->author.description = actor["summary"].toString(); p->author.profileImageUrl = actor["image"].toMap().value("url").toString(); if (!var["generator"].isNull()) { p->source = var["generator"].toMap().value("displayName").toString(); } const QVariantList to = var["to"].toList(); Q_FOREACH (const QVariant& element, to) { p->to.append(element.toMap().value("id").toString()); } const QVariantList cc = var["cc"].toList(); Q_FOREACH (const QVariant& element, cc) { p->cc.append(element.toMap().value("id").toString()); } const QVariantMap replies = object["replies"].toMap(); if (replies.value("pump_io").isNull()) { p->replies = replies["url"].toString(); } else { p->replies = replies["pump_io"].toMap().value("proxyURL").toString(); } return p; } else { kDebug() << "post is not a PumpIOPost!"; return post; } } QList< Choqok::Post* > PumpIOMicroBlog::readTimeline(const QByteArray& buffer) { QList posts; bool ok; QJson::Parser parser; const QVariantList list = parser.parse(buffer, &ok).toMap().value("items").toList(); if (ok) { Q_FOREACH (const QVariant& element, list) { const QVariantMap elementMap = element.toMap(); if (!elementMap["object"].toMap().value("deleted").isNull()) { // Skip deleted posts continue; } posts.prepend(readPost(elementMap, new PumpIOPost)); } } else { kDebug() << "Cannot parse JSON reply"; } return posts; } void PumpIOMicroBlog::setLastTimelineId(Choqok::Account* theAccount, const QString& timeline, const ChoqokId& id) { m_timelinesLatestIds[theAccount][timeline] = id; } void PumpIOMicroBlog::setTimelinesInfo() { Choqok::TimelineInfo *t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Activity"); t->description = i18nc("Timeline description", "You and people you follow"); t->icon = "user-home"; m_timelinesInfos["Activity"] = t; m_timelinesPaths["Activity"] = inboxActivity + "/major"; t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Favorites"); t->description = i18nc("Timeline description", "Posts you favorited"); t->icon = "favorites"; m_timelinesInfos["Favorites"] = t; m_timelinesPaths["Favorites"] = "/api/user/%1/favorites"; t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Inbox"); t->description = i18nc("Timeline description", "Posts sent to you"); t->icon = "mail-folder-inbox"; m_timelinesInfos["Inbox"] = t; m_timelinesPaths["Inbox"] = inboxActivity + "/direct/major/"; t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Outbox"); t->description = i18nc("Timeline description", "Posts you sent"); t->icon = "mail-folder-outbox"; m_timelinesInfos["Outbox"] = t; m_timelinesPaths["Outbox"] = outboxActivity + "/major/"; } +void PumpIOMicroBlog::updatePost(Choqok::Account* theAccount, Choqok::Post* post) +{ + PumpIOAccount *acc = qobject_cast(theAccount); + if (acc) { + QVariantMap object; + object.insert("id", post->postId); + object.insert("objectType", post->type); + object.insert("content", QUrl::toPercentEncoding(post->content)); + + // https://github.com/e14n/pump.io/issues/885 + QVariantList to; + QVariantMap thePublic; + thePublic.insert("objectType", "collection"); + thePublic.insert("id", "http://activityschema.org/collection/public"); + to.append(thePublic); + + QVariantMap item; + item.insert("verb", "update"); + item.insert("object", object); + item.insert("to", to); + + QJson::Serializer serializer; + const QByteArray data = serializer.serialize(item); + + KUrl url(acc->host()); + url.addPath(outboxActivity.arg(acc->username())); + KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); + job->addMetaData("content-type", "Content-Type: application/json"); + job->addMetaData("customHTTPHeader", authorizationMetaData(acc, url, QOAuth::POST)); + if (!job) { + kDebug() << "Cannot create an http POST request!"; + return; + } + m_accountJobs[job] = acc; + m_updateJobs[job] = post; + connect(job, SIGNAL(result(KJob*)), this, SLOT(slotUpdatePost(KJob*))); + job->start(); + } else { + kDebug() << "theAccount is not a PumpIOAccount!"; + } +} + QString PumpIOMicroBlog::hostFromAcct(const QString& acct) { if (acct.contains("acct:")) { return acct.split(':')[1].split('@')[1]; } return acct; } QString PumpIOMicroBlog::userNameFromAcct(const QString& acct) { if (acct.contains("acct:")) { return acct.split(':')[1].split('@')[0]; } return acct; } diff --git a/microblogs/pumpio/pumpiomicroblog.h b/microblogs/pumpio/pumpiomicroblog.h index df94217f..0f16bc75 100644 --- a/microblogs/pumpio/pumpiomicroblog.h +++ b/microblogs/pumpio/pumpiomicroblog.h @@ -1,149 +1,161 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2013 Andrea Scarpino 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 PUMPIOMICROBLOG_H #define PUMPIOMICROBLOG_H #include "QtOAuth/qoauth_namespace.h" #include "microblog.h" class KJob; class KUrl; class PumpIOAccount; class PumpIOMicroBlog : public Choqok::MicroBlog { Q_OBJECT public: explicit PumpIOMicroBlog(QObject* parent, const QVariantList& args); virtual ~PumpIOMicroBlog(); virtual void abortAllJobs(Choqok::Account* theAccount); virtual void abortCreatePost(Choqok::Account* theAccount, Choqok::Post* post = 0); virtual void aboutToUnload(); virtual QMenu* createActionsMenu(Choqok::Account* theAccount, QWidget* parent = Choqok::UI::Global::mainWindow()); + virtual Choqok::UI::ComposerWidget* createComposerWidget(Choqok::Account* account, + QWidget* parent); + virtual ChoqokEditAccountWidget* createEditAccountWidget(Choqok::Account* account, QWidget* parent); virtual Choqok::Account* createNewAccount(const QString& alias); virtual void createPost(Choqok::Account* theAccount, Choqok::Post* post); virtual Choqok::UI::PostWidget* createPostWidget(Choqok::Account* account, Choqok::Post* post, QWidget* parent); virtual void fetchPost(Choqok::Account* theAccount, Choqok::Post* post); virtual QList loadTimeline(Choqok::Account* account, const QString& timelineName); virtual void removePost(Choqok::Account* theAccount, Choqok::Post* post); virtual QString postUrl(Choqok::Account* account, const QString& username, const QString& postId) const; virtual QString profileUrl(Choqok::Account* account, const QString& username) const; virtual void saveTimeline(Choqok::Account* account, const QString& timelineName, const QList< Choqok::UI::PostWidget* >& timeline); virtual Choqok::TimelineInfo* timelineInfo(const QString& timelineName); virtual void updateTimelines(Choqok::Account* theAccount); void createPost(Choqok::Account* theAccount, Choqok::Post* post, const QVariantList& to, const QVariantList& cc = QVariantList()); + void createPostWithMedia(Choqok::Account* theAccount, Choqok::Post* post, + const QString& filePath); + void fetchFollowing(Choqok::Account* theAccount); void fetchLists(Choqok::Account* theAccount); void share(Choqok::Account* theAccount, Choqok::Post* post); void toggleFavorite(Choqok::Account* theAccount, Choqok::Post* post); void fetchReplies(Choqok::Account* theAccount, const QString& url); static QString userNameFromAcct(const QString& acct); static QString hostFromAcct(const QString& acct); Q_SIGNALS: void favorite(Choqok::Account*, Choqok::Post*); void followingFetched(Choqok::Account*); void listsFetched(Choqok::Account*); protected Q_SLOTS: void showDirectMessageDialog(); void slotCreatePost(KJob* job); void slotFavorite(KJob* job); void slotFetchPost(KJob* job); void slotFetchReplies(KJob* job); void slotFollowing(KJob* job); void slotLists(KJob* job); void slotRemovePost(KJob* job); void slotShare(KJob* job); + void slotUpdatePost(KJob* job); void slotUpdateTimeline(KJob* job); + void slotUpload(KJob* job); protected: static const QString inboxActivity; static const QString outboxActivity; QString authorizationMetaData(PumpIOAccount* account, const KUrl& url, const QOAuth::HttpMethod& method, const QOAuth::ParamMap& map = QOAuth::ParamMap()) const; ChoqokId lastTimelineId(Choqok::Account* theAccount, const QString& timeline) const; Choqok::Post* readPost(const QVariantMap& var, Choqok::Post* post); QList readTimeline(const QByteArray& buffer); void setLastTimelineId(Choqok::Account* theAccount, const QString& timeline, const ChoqokId& id); void setTimelinesInfo(); + void updatePost(Choqok::Account* theAccount, Choqok::Post* post); + QMap m_accountJobs; QMap m_createPostJobs; QMap m_favoriteJobs; QMap m_removePostJobs; QMap m_shareJobs; + QMap m_uploadJobs; + QMap m_updateJobs; QMap m_timelinesInfos; QHash > m_timelinesLatestIds; QHash m_timelinesPaths; QMap m_timelinesRequests; private: class Private; Private * const d; }; #endif // PUMPIOMICROBLOG_H \ No newline at end of file diff --git a/microblogs/pumpio/pumpiopostwidget.cpp b/microblogs/pumpio/pumpiopostwidget.cpp index dc359cb0..050b6f30 100644 --- a/microblogs/pumpio/pumpiopostwidget.cpp +++ b/microblogs/pumpio/pumpiopostwidget.cpp @@ -1,214 +1,220 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2013 Andrea Scarpino + Copyright (C) 2008-2012 Mehrdad Momeny 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 "pumpiopostwidget.h" #include #include #include #include "mediamanager.h" #include "textbrowser.h" #include "pumpioaccount.h" #include "pumpiomicroblog.h" #include "pumpiopost.h" #include "pumpioshowthread.h" const KIcon PumpIOPostWidget::unFavIcon(Choqok::MediaManager::convertToGrayScale(KIcon("rating").pixmap(16))); class PumpIOPostWidget::Private { public: KPushButton *btnFavorite; }; PumpIOPostWidget::PumpIOPostWidget(Choqok::Account* account, Choqok::Post* post, QWidget* parent): PostWidget(account, post, parent), d(new Private) { mainWidget()->document()->addResource(QTextDocument::ImageResource, QUrl("icon://thread"), KIcon("go-top").pixmap(10)); } PumpIOPostWidget::~PumpIOPostWidget() { delete d; } void PumpIOPostWidget::checkAnchor(const QUrl& url) { if (url.scheme() == "thread") { PumpIOShowThread *thread = new PumpIOShowThread(currentAccount(), currentPost()); thread->resize(this->width(), thread->height() * 3); thread->show(); } else { Choqok::UI::PostWidget::checkAnchor(url); } } QString PumpIOPostWidget::generateSign() { QString ss; PumpIOPost *post = dynamic_cast(currentPost()); PumpIOAccount *account = qobject_cast(currentAccount()); PumpIOMicroBlog *microblog = qobject_cast(account->microblog()); if (post) { if (post->author.userName != account->username()) { ss += "author.realName + "\">" + post->author.userName + " - "; } ss += "postUrl(account, post->author.userName, post->postId) + "\" title=\"" + post->creationDateTime.toString(Qt::DefaultLocaleLongDate) + "\">%1"; if (!post->source.isEmpty()) { ss += " - " + post->source; } const QRegExp followers("/api/user/\\w+/followers"); if (!post->to.isEmpty()) { ss += " - "; ss += i18n("To:") + ' '; Q_FOREACH (const QString& id, post->to) { if (id == "http://activityschema.org/collection/public") { ss += i18n("Public") + ' '; } else if (followers.indexIn(id) != -1) { ss += "" + i18n("Followers") + " "; } else if (id == "acct:" + account->webfingerID()) { ss += i18n("You") + ' '; } else { ss += "profileUrl(account, id) + "\">" + PumpIOMicroBlog::userNameFromAcct(id) + " "; } } ss = ss.trimmed(); } if (!post->cc.isEmpty()) { ss += " - "; ss += i18n("Cc:") + ' '; Q_FOREACH (const QString& id, post->cc) { if (id == "http://activityschema.org/collection/public") { ss += i18n("Public") + ' '; } else if (followers.indexIn(id) != -1) { ss += "" + i18n("Followers") + " "; } else if (id == "acct:" + account->webfingerID()) { ss += i18n("You") + ' '; } else { ss += "profileUrl(account, id) + "\">" + PumpIOMicroBlog::userNameFromAcct(id) + " "; } } ss = ss.trimmed(); } if (!post->shares.isEmpty()) { ss += " - "; ss += i18n("Shared by:") + ' '; Q_FOREACH (const QString& id, post->shares) { if (id == "acct:" + account->webfingerID()) { ss += i18n("You") + ' '; } else { ss += "profileUrl(account, id) + "\">" + PumpIOMicroBlog::userNameFromAcct(id) + " "; } } ss = ss.trimmed(); } ss += " - "; ss += i18n("View replies"); ss += " "; } else { kDebug() << "post is not a PumpIOPost!"; } return ss; } void PumpIOPostWidget::initUi() { Choqok::UI::PostWidget::initUi(); d->btnFavorite = addButton("btnFavorite", i18nc("@info:tooltip", "Favorite"), "rating"); d->btnFavorite->setCheckable(true); connect(d->btnFavorite, SIGNAL(clicked(bool)), this, SLOT(toggleFavorite())); updateFavStat(); } void PumpIOPostWidget::toggleFavorite() { + kDebug(); setReadWithSignal(); PumpIOMicroBlog* microBlog = qobject_cast(currentAccount()->microblog()); connect(microBlog, SIGNAL(favorite(Choqok::Account*, Choqok::Post*)), this, SLOT(slotToggleFavorite(Choqok::Account*, Choqok::Post*))); microBlog->toggleFavorite(currentAccount(), currentPost()); } void PumpIOPostWidget::slotToggleFavorite(Choqok::Account*, Choqok::Post*) { + kDebug(); updateFavStat(); } void PumpIOPostWidget::slotPostError(Choqok::Account* theAccount, Choqok::Post* post, Choqok::MicroBlog::ErrorType error, const QString& errorMessage) { Q_UNUSED(error) + + kDebug(); if (theAccount == currentAccount() && post == currentPost()) { kDebug() << errorMessage; disconnect(currentAccount()->microblog(), SIGNAL(postRemoved(Choqok::Account*,Choqok::Post*)), this, SLOT(slotCurrentPostRemoved(Choqok::Account*,Choqok::Post*)) ); disconnect(currentAccount()->microblog(), SIGNAL(errorPost(Choqok::Account*,Choqok::Post*,Choqok::MicroBlog::ErrorType,QString,Choqok::MicroBlog::ErrorLevel)), this, SLOT(slotPostError(Choqok::Account*,Choqok::Post*,Choqok::MicroBlog::ErrorType,QString))); } } void PumpIOPostWidget::slotResendPost() { + kDebug(); setReadWithSignal(); PumpIOMicroBlog* microBlog = qobject_cast(currentAccount()->microblog()); microBlog->share(currentAccount(), currentPost()); } void PumpIOPostWidget::updateFavStat() { d->btnFavorite->setChecked(currentPost()->isFavorited); if (currentPost()->isFavorited){ d->btnFavorite->setIcon(KIcon("rating")); } else { d->btnFavorite->setIcon(unFavIcon); } } diff --git a/microblogs/pumpio/pumpioshowthread.cpp b/microblogs/pumpio/pumpioshowthread.cpp index a64bbce4..98254a21 100644 --- a/microblogs/pumpio/pumpioshowthread.cpp +++ b/microblogs/pumpio/pumpioshowthread.cpp @@ -1,83 +1,85 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2013 Andrea Scarpino 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 "pumpioshowthread.h" #include #include "pumpiomicroblog.h" #include "pumpiopost.h" #include "pumpiopostwidget.h" class PumpIOShowThread::Private { public: Choqok::Account* account; ChoqokId postId; }; PumpIOShowThread::PumpIOShowThread(Choqok::Account* account, Choqok::Post* post, QWidget* parent): QWidget(parent) , d(new Private) { d->account = account; d->postId = post->postId; setupUi(this); setWindowTitle("Choqok: " + post->author.userName + "'s thread"); connect(account->microblog(), SIGNAL(postFetched(Choqok::Account*,Choqok::Post*)), this, SLOT(slotAddPost(Choqok::Account*,Choqok::Post*))); PumpIOPost* p = dynamic_cast(post); if (p) { PumpIOPostWidget *widget = new PumpIOPostWidget(account, p, this); widget->initUi(); widget->setRead(); mainLayout->insertWidget(0, widget); PumpIOMicroBlog* microblog = dynamic_cast(account->microblog()); if (microblog) { microblog->fetchReplies(account, p->replies); } else { kDebug() << "Microblog is not a PumpIOMicroBlog"; } } else { kDebug() << "Post is not a PumpIOPost"; } } PumpIOShowThread::~PumpIOShowThread() { + delete d; } void PumpIOShowThread::slotAddPost(Choqok::Account* theAccount, Choqok::Post* post) { + kDebug(); if (theAccount == d->account && post->replyToPostId == d->postId) { PumpIOPostWidget *widget = new PumpIOPostWidget(theAccount, post, this); widget->initUi(); widget->setRead(); mainLayout->insertWidget(mainLayout->count() - 1, widget); } }