diff --git a/helperlibs/twitterapihelper/twitterapiaccount.cpp b/helperlibs/twitterapihelper/twitterapiaccount.cpp index 5a86d97e..7a312b34 100644 --- a/helperlibs/twitterapihelper/twitterapiaccount.cpp +++ b/helperlibs/twitterapihelper/twitterapiaccount.cpp @@ -1,294 +1,308 @@ /* This file is part of Choqok, the KDE micro-blogging client 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 "twitterapiaccount.h" #include #include #include "passwordmanager.h" #include "twitterapidebug.h" #include "twitterapimicroblog.h" class TwitterApiAccount::Private { public: Private() : api(QLatin1Char('/')), usingOauth(true), qoauth(0) {} QString userId; int count; QString host; QString api; QUrl apiUrl; QUrl homepageUrl; QStringList friendsList; + QStringList followersList; QStringList timelineNames; QByteArray oauthToken; QByteArray oauthTokenSecret; QByteArray oauthConsumerKey; QByteArray oauthConsumerSecret; bool usingOauth; QOAuth::Interface *qoauth; }; TwitterApiAccount::TwitterApiAccount(TwitterApiMicroBlog *parent, const QString &alias) : Account(parent, alias), d(new Private) { qCDebug(CHOQOK); d->usingOauth = configGroup()->readEntry("UsingOAuth", false); d->userId = configGroup()->readEntry("UserId", QString()); d->count = configGroup()->readEntry("CountOfPosts", 20); d->host = configGroup()->readEntry("Host", QString()); d->friendsList = configGroup()->readEntry("Friends", QStringList()); + d->friendsList = configGroup()->readEntry("Followers", QStringList()); d->timelineNames = configGroup()->readEntry("Timelines", QStringList()); d->oauthToken = configGroup()->readEntry("OAuthToken", QByteArray()); d->oauthConsumerKey = configGroup()->readEntry("OAuthConsumerKey", QByteArray()); d->oauthConsumerSecret = Choqok::PasswordManager::self()->readPassword( QStringLiteral("%1_consumerSecret").arg(alias)).toUtf8(); d->oauthTokenSecret = Choqok::PasswordManager::self()->readPassword( QStringLiteral("%1_tokenSecret").arg(alias)).toUtf8(); setApi(configGroup()->readEntry(QLatin1String("Api"), QString::fromLatin1("/"))); qCDebug(CHOQOK) << "UsingOAuth:" << d->usingOauth; if (d->usingOauth) { initQOAuthInterface(); } if (d->timelineNames.isEmpty()) { QStringList list = parent->timelineNames(); list.removeOne(QLatin1String("Public")); list.removeOne(QLatin1String("Favorite")); list.removeOne(QLatin1String("ReTweets")); d->timelineNames = list; } if (d->friendsList.isEmpty()) { parent->listFriendsUsername(this); //Result will set on TwitterApiMicroBlog! } setPostCharLimit(140); //TODO: See if we can ask twitter for the char limit and make it dynamic } TwitterApiAccount::~TwitterApiAccount() { d->qoauth->deleteLater(); delete d; } void TwitterApiAccount::writeConfig() { configGroup()->writeEntry("UsingOAuth", d->usingOauth); configGroup()->writeEntry("UserId", d->userId); configGroup()->writeEntry("CountOfPosts", d->count); configGroup()->writeEntry("Host", d->host); configGroup()->writeEntry("Api", d->api); configGroup()->writeEntry("Friends", d->friendsList); + configGroup()->writeEntry("Followers", d->followersList); configGroup()->writeEntry("Timelines", d->timelineNames); configGroup()->writeEntry("OAuthToken", d->oauthToken); configGroup()->writeEntry("OAuthConsumerKey", d->oauthConsumerKey); Choqok::PasswordManager::self()->writePassword(QStringLiteral("%1_consumerSecret").arg(alias()), QString::fromUtf8(d->oauthConsumerSecret)); Choqok::PasswordManager::self()->writePassword(QStringLiteral("%1_tokenSecret").arg(alias()), QString::fromUtf8(d->oauthTokenSecret)); Choqok::Account::writeConfig(); } QString TwitterApiAccount::userId() const { return d->userId; } void TwitterApiAccount::setUserId(const QString &id) { d->userId = id; } int TwitterApiAccount::countOfPosts() const { return d->count; } void TwitterApiAccount::setCountOfPosts(int count) { d->count = count; } QUrl TwitterApiAccount::apiUrl() const { return d->apiUrl; } QString TwitterApiAccount::host() const { return d->host; } void TwitterApiAccount::setApiUrl(const QUrl &apiUrl) { d->apiUrl = apiUrl; } QString TwitterApiAccount::api() const { return d->api; } void TwitterApiAccount::setApi(const QString &api) { d->api = api; generateApiUrl(); } void TwitterApiAccount::setHost(const QString &host) { d->host = host; generateApiUrl(); } QUrl TwitterApiAccount::homepageUrl() const { return d->homepageUrl; } void TwitterApiAccount::generateApiUrl() { if (!host().startsWith(QLatin1String("http"))) { //NOTE: This is for compatibility by prev versions. remove it after 1.0 release setHost(host().prepend(QLatin1String("http://"))); } QUrl url(host()); setHomepageUrl(url); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + (api())); setApiUrl(url); } void TwitterApiAccount::setHomepageUrl(const QUrl &homepageUrl) { d->homepageUrl = homepageUrl; } QStringList TwitterApiAccount::friendsList() const { return d->friendsList; } void TwitterApiAccount::setFriendsList(const QStringList &list) { d->friendsList = list; writeConfig(); } +QStringList TwitterApiAccount::followersList() const +{ + return d->followersList; +} + +void TwitterApiAccount::setFollowersList(const QStringList& list) +{ + d->followersList = list; + writeConfig(); +} + QStringList TwitterApiAccount::timelineNames() const { return d->timelineNames; } void TwitterApiAccount::setTimelineNames(const QStringList &list) { d->timelineNames.clear(); Q_FOREACH (const QString &name, list) { if (microblog()->timelineNames().contains(name)) { d->timelineNames << name; } } } QByteArray TwitterApiAccount::oauthToken() const { return d->oauthToken; } void TwitterApiAccount::setOauthToken(const QByteArray &token) { d->oauthToken = token; } QByteArray TwitterApiAccount::oauthTokenSecret() const { return d->oauthTokenSecret; } void TwitterApiAccount::setOauthTokenSecret(const QByteArray &tokenSecret) { d->oauthTokenSecret = tokenSecret; } QByteArray TwitterApiAccount::oauthConsumerKey() const { return d->oauthConsumerKey; } void TwitterApiAccount::setOauthConsumerKey(const QByteArray &consumerKey) { d->oauthConsumerKey = consumerKey; } QByteArray TwitterApiAccount::oauthConsumerSecret() const { return d->oauthConsumerSecret; } void TwitterApiAccount::setOauthConsumerSecret(const QByteArray &consumerSecret) { d->oauthConsumerSecret = consumerSecret; } bool TwitterApiAccount::usingOAuth() const { return d->usingOauth; } void TwitterApiAccount::setUsingOAuth(bool use) { if (use) { initQOAuthInterface(); } else { delete d->qoauth; d->qoauth = 0L; } d->usingOauth = use; } QOAuth::Interface *TwitterApiAccount::oauthInterface() { return d->qoauth; } void TwitterApiAccount::initQOAuthInterface() { qCDebug(CHOQOK); if (!d->qoauth) { d->qoauth = new QOAuth::Interface(new KIO::AccessManager(this), this); } d->qoauth->setConsumerKey(d->oauthConsumerKey); d->qoauth->setConsumerSecret(d->oauthConsumerSecret); d->qoauth->setRequestTimeout(20000); d->qoauth->setIgnoreSslErrors(true); } diff --git a/helperlibs/twitterapihelper/twitterapiaccount.h b/helperlibs/twitterapihelper/twitterapiaccount.h index a4d0aa6b..94a7f202 100644 --- a/helperlibs/twitterapihelper/twitterapiaccount.h +++ b/helperlibs/twitterapihelper/twitterapiaccount.h @@ -1,111 +1,115 @@ /* This file is part of Choqok, the KDE micro-blogging client 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/ */ #ifndef TWITTERAPIACCOUNT_H #define TWITTERAPIACCOUNT_H #include #include "account.h" #include "choqok_export.h" #include "twitterapimicroblog.h" namespace QOAuth { class Interface; } /** @author Mehrdad Momeny \ */ class CHOQOK_HELPER_EXPORT TwitterApiAccount : public Choqok::Account { Q_OBJECT public: TwitterApiAccount(TwitterApiMicroBlog *parent, const QString &alias); ~TwitterApiAccount(); virtual void writeConfig(); QString userId() const; void setUserId(const QString &id); int countOfPosts() const; void setCountOfPosts(int count); QString host() const; void setHost(const QString &host); /** @return api path It's defer from apiUrl. For example: in http://identi.ca/api/ identi.ca is @ref host() api is @ref api() http://identi.ca/api/ is @ref apiUrl() */ QString api() const; virtual void setApi(const QString &api); /** Combined from @ref host and @ref api to use for connections and queries */ QUrl apiUrl() const; virtual QUrl homepageUrl() const; QStringList friendsList() const; void setFriendsList(const QStringList &list); + QStringList followersList() const; + + void setFollowersList(const QStringList &list); + virtual QStringList timelineNames() const; virtual void setTimelineNames(const QStringList &list); QByteArray oauthToken() const; void setOauthToken(const QByteArray &token); QByteArray oauthTokenSecret() const; void setOauthTokenSecret(const QByteArray &tokenSecret); QByteArray oauthConsumerKey() const; void setOauthConsumerKey(const QByteArray &consumerKey); QByteArray oauthConsumerSecret() const; void setOauthConsumerSecret(const QByteArray &consumerSecret); bool usingOAuth() const; void setUsingOAuth(bool use = true); QOAuth::Interface *oauthInterface(); protected: void setApiUrl(const QUrl &apiUrl); void setHomepageUrl(const QUrl &homepageUrl); void generateApiUrl(); void initQOAuthInterface(); private: class Private; Private *const d; }; #endif // TWITTERACCOUNT_H diff --git a/helperlibs/twitterapihelper/twitterapidmessagedialog.cpp b/helperlibs/twitterapihelper/twitterapidmessagedialog.cpp index 3d02b99f..9b86a843 100644 --- a/helperlibs/twitterapihelper/twitterapidmessagedialog.cpp +++ b/helperlibs/twitterapihelper/twitterapidmessagedialog.cpp @@ -1,192 +1,192 @@ /* This file is part of Choqok, the KDE micro-blogging client 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 "twitterapidmessagedialog.h" #include #include #include #include #include #include #include #include #include "choqoktextedit.h" #include "microblog.h" #include "notifymanager.h" #include "twitterapiaccount.h" #include "twitterapidebug.h" #include "twitterapimicroblog.h" class TwitterApiDMessageDialog::Private { public: Private(TwitterApiAccount *theAccount) : account(theAccount) {} QComboBox *comboFriendsList; Choqok::UI::TextEdit *editor; TwitterApiAccount *account; Choqok::Post *sentPost; }; TwitterApiDMessageDialog::TwitterApiDMessageDialog(TwitterApiAccount *theAccount, QWidget *parent, Qt::WindowFlags flags) : QDialog(parent, flags), d(new Private(theAccount)) { setWindowTitle(i18n("Send Private Message")); setAttribute(Qt::WA_DeleteOnClose); setupUi(this); KConfigGroup grp(KSharedConfig::openConfig(), "TwitterApi"); resize(grp.readEntry("DMessageDialogSize", QSize(300, 200))); - QStringList list = theAccount->friendsList(); + QStringList list = theAccount->followersList(); if (list.isEmpty()) { reloadFriendslist(); } else { list.sort(); d->comboFriendsList->addItems(list); } } TwitterApiDMessageDialog::~TwitterApiDMessageDialog() { KConfigGroup grp(KSharedConfig::openConfig(), "TwitterApi"); grp.writeEntry("DMessageDialogSize", size()); grp.sync(); delete d; } void TwitterApiDMessageDialog::setupUi(QWidget *mainWidget) { QLabel *lblTo = new QLabel(i18nc("Send message to", "To:"), this); d->comboFriendsList = new QComboBox(this); d->comboFriendsList->setDuplicatesEnabled(false); QPushButton *btnReload = new QPushButton(this); btnReload->setToolTip(i18n("Reload friends list")); btnReload->setIcon(QIcon::fromTheme(QLatin1String("view-refresh"))); btnReload->setMaximumWidth(25); connect(btnReload, SIGNAL(clicked(bool)), SLOT(reloadFriendslist())); QVBoxLayout *mainLayout = new QVBoxLayout(mainWidget); QHBoxLayout *toLayout = new QHBoxLayout; toLayout->addWidget(lblTo); toLayout->addWidget(d->comboFriendsList); toLayout->addWidget(btnReload); mainLayout->addLayout(toLayout); d->editor = new Choqok::UI::TextEdit(d->account->postCharLimit()); connect(d->editor, SIGNAL(returnPressed(QString)), SLOT(submitPost(QString))); mainLayout->addWidget(d->editor); d->editor->setFocus(); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); okButton->setText(i18nc("Send private message", "Send")); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(buttonBox); } void TwitterApiDMessageDialog::setFriends(const QStringList friends) { d->comboFriendsList->clear(); d->comboFriendsList->addItems(friends); } void TwitterApiDMessageDialog::reloadFriendslist() { d->comboFriendsList->clear(); TwitterApiMicroBlog *blog = qobject_cast(d->account->microblog()); if (blog) { - connect(blog, SIGNAL(friendsUsernameListed(TwitterApiAccount*,QStringList)), - this, SLOT(friendsUsernameListed(TwitterApiAccount*,QStringList))); - blog->listFriendsUsername(d->account); + connect(blog, SIGNAL(followersUsernameListed(TwitterApiAccount*,QStringList)), + this, SLOT(followersUsernameListed(TwitterApiAccount*,QStringList))); + blog->listFollowersUsername(d->account); d->comboFriendsList->setCurrentText(i18n("Please wait...")); } } void TwitterApiDMessageDialog::accept() { submitPost(d->editor->toPlainText()); } void TwitterApiDMessageDialog::submitPost(QString text) { if (d->account->friendsList().isEmpty() || text.isEmpty() || d->comboFriendsList->currentText().isEmpty()) { return; } hide(); connect(d->account->microblog(), SIGNAL(errorPost(Choqok::Account *, Choqok::Post *, Choqok::MicroBlog::ErrorType, QString, Choqok::MicroBlog::ErrorLevel)), this, SLOT(errorPost(Choqok::Account *, Choqok::Post *, Choqok::MicroBlog::ErrorType, QString, Choqok::MicroBlog::ErrorLevel))); connect(d->account->microblog(), SIGNAL(postCreated(Choqok::Account*,Choqok::Post*)), this, SLOT(postCreated(Choqok::Account*,Choqok::Post*))); d->sentPost = new Choqok::Post; d->sentPost->isPrivate = true; d->sentPost->replyToUserName = d->comboFriendsList->currentText(); d->sentPost->content = text; d->account->microblog()->createPost(d->account, d->sentPost); } -void TwitterApiDMessageDialog::friendsUsernameListed(TwitterApiAccount *theAccount, QStringList list) +void TwitterApiDMessageDialog::followersUsernameListed(TwitterApiAccount *theAccount, QStringList list) { if (theAccount == d->account) { d->comboFriendsList->clear(); list.sort(); d->comboFriendsList->addItems(list); } } void TwitterApiDMessageDialog::postCreated(Choqok::Account *theAccount, Choqok::Post *thePost) { if (theAccount == d->account && thePost == d->sentPost) { qCDebug(CHOQOK); accept(); } } void TwitterApiDMessageDialog::errorPost(Choqok::Account *theAccount, Choqok::Post *thePost, Choqok::MicroBlog::ErrorType , QString , Choqok::MicroBlog::ErrorLevel) { if (theAccount == d->account && thePost == d->sentPost) { qCDebug(CHOQOK); show(); } } void TwitterApiDMessageDialog::setTo(const QString &username) { d->comboFriendsList->setCurrentText(username); } diff --git a/helperlibs/twitterapihelper/twitterapidmessagedialog.h b/helperlibs/twitterapihelper/twitterapidmessagedialog.h index a76c0653..f60a3259 100644 --- a/helperlibs/twitterapihelper/twitterapidmessagedialog.h +++ b/helperlibs/twitterapihelper/twitterapidmessagedialog.h @@ -1,66 +1,66 @@ /* This file is part of Choqok, the KDE micro-blogging client 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/ */ #ifndef TWITTERAPIDMESSAGEDIALOG_H #define TWITTERAPIDMESSAGEDIALOG_H #include #include "microblog.h" namespace Choqok { class Account; struct Post; } class TwitterApiAccount; class CHOQOK_HELPER_EXPORT TwitterApiDMessageDialog : public QDialog { Q_OBJECT public: explicit TwitterApiDMessageDialog(TwitterApiAccount *theAccount, QWidget *parent = 0, Qt::WindowFlags flags = 0); ~TwitterApiDMessageDialog(); void setTo(const QString &username); protected Q_SLOTS: virtual void accept(); - void friendsUsernameListed(TwitterApiAccount *, QStringList); + void followersUsernameListed(TwitterApiAccount *, QStringList); void submitPost(QString); void reloadFriendslist(); void postCreated(Choqok::Account *, Choqok::Post *); void errorPost(Choqok::Account *, Choqok::Post *, Choqok::MicroBlog::ErrorType, QString, Choqok::MicroBlog::ErrorLevel); protected: virtual void setupUi(QWidget *mainWidget); void setFriends(const QStringList friends); private: class Private; Private *const d; }; #endif // TWITTERAPIDMESSAGEDIALOG_H diff --git a/helperlibs/twitterapihelper/twitterapimicroblog.cpp b/helperlibs/twitterapihelper/twitterapimicroblog.cpp index 281dcd7b..1c717675 100644 --- a/helperlibs/twitterapihelper/twitterapimicroblog.cpp +++ b/helperlibs/twitterapihelper/twitterapimicroblog.cpp @@ -1,1496 +1,1605 @@ /* This file is part of Choqok, the KDE micro-blogging client 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 "twitterapimicroblog.h" #include #include #include #include #include #include #include #include #include "account.h" #include "accountmanager.h" #include "application.h" #include "choqokappearancesettings.h" #include "choqokbehaviorsettings.h" #include "choqokuiglobal.h" #include "editaccountwidget.h" #include "microblogwidget.h" #include "notifymanager.h" #include "postwidget.h" #include "timelinewidget.h" #include "twitterapiaccount.h" #include "twitterapicomposerwidget.h" #include "twitterapidebug.h" #include "twitterapidmessagedialog.h" #include "twitterapipostwidget.h" #include "twitterapisearch.h" #include "twitterapisearchdialog.h" #include "twitterapisearchtimelinewidget.h" class TwitterApiMicroBlog::Private { public: Private(): countOfTimelinesToSave(0), friendsCursor(QLatin1String("-1")) { monthes[QLatin1String("Jan")] = 1; monthes[QLatin1String("Feb")] = 2; monthes[QLatin1String("Mar")] = 3; monthes[QLatin1String("Apr")] = 4; monthes[QLatin1String("May")] = 5; monthes[QLatin1String("Jun")] = 6; monthes[QLatin1String("Jul")] = 7; monthes[QLatin1String("Aug")] = 8; monthes[QLatin1String("Sep")] = 9; monthes[QLatin1String("Oct")] = 10; monthes[QLatin1String("Nov")] = 11; monthes[QLatin1String("Dec")] = 12; } int countOfTimelinesToSave; QString friendsCursor; + QString followersCursor; QMap monthes; }; TwitterApiMicroBlog::TwitterApiMicroBlog(const QString &componentName, QObject *parent) : MicroBlog(componentName, parent), d(new Private) { qCDebug(CHOQOK); format = QLatin1String("json"); QStringList timelineTypes; timelineTypes << QLatin1String("Home") << QLatin1String("Reply") << QLatin1String("Inbox") << QLatin1String("Outbox") << QLatin1String("Favorite") << QLatin1String("ReTweets") << QLatin1String("Public"); setTimelineNames(timelineTypes); timelineApiPath[QLatin1String("Home")] = QLatin1String("/statuses/home_timeline.%1"); timelineApiPath[QLatin1String("Reply")] = QLatin1String("/statuses/replies.%1"); timelineApiPath[QLatin1String("Inbox")] = QLatin1String("/direct_messages.%1"); timelineApiPath[QLatin1String("Outbox")] = QLatin1String("/direct_messages/sent.%1"); timelineApiPath[QLatin1String("Favorite")] = QLatin1String("/favorites/list.%1"); timelineApiPath[QLatin1String("ReTweets")] = QLatin1String("/statuses/retweets_of_me.%1"); timelineApiPath[QLatin1String("Public")] = QLatin1String("/statuses/public_timeline.%1"); setTimelineInfos(); } void TwitterApiMicroBlog::setTimelineInfos() { Choqok::TimelineInfo *t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Home"); t->description = i18nc("Timeline description", "You and your friends"); t->icon = QLatin1String("user-home"); mTimelineInfos[QLatin1String("Home")] = t; t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Reply"); t->description = i18nc("Timeline description", "Replies to you"); t->icon = QLatin1String("edit-undo"); mTimelineInfos[QLatin1String("Reply")] = t; t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Inbox"); t->description = i18nc("Timeline description", "Your incoming private messages"); t->icon = QLatin1String("mail-folder-inbox"); mTimelineInfos[QLatin1String("Inbox")] = t; t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Outbox"); t->description = i18nc("Timeline description", "Private messages you have sent"); t->icon = QLatin1String("mail-folder-outbox"); mTimelineInfos[QLatin1String("Outbox")] = t; t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Favorite"); t->description = i18nc("Timeline description", "Your favorites"); t->icon = QLatin1String("favorites"); mTimelineInfos[QLatin1String("Favorite")] = t; t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Public"); t->description = i18nc("Timeline description", "Public timeline"); t->icon = QLatin1String("folder-green"); mTimelineInfos[QLatin1String("Public")] = t; t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "ReTweets"); t->description = i18nc("Timeline description", "Your posts that were ReTweeted by others"); t->icon = QLatin1String("folder-red"); mTimelineInfos[QLatin1String("ReTweets")] = t; } TwitterApiMicroBlog::~TwitterApiMicroBlog() { qDeleteAll(mTimelineInfos); delete d; } QMenu *TwitterApiMicroBlog::createActionsMenu(Choqok::Account *theAccount, QWidget *parent) { QMenu *menu = MicroBlog::createActionsMenu(theAccount, parent); QAction *directMessge = new QAction(QIcon::fromTheme(QLatin1String("mail-message-new")), i18n("Send Private Message..."), menu); directMessge->setData(theAccount->alias()); connect(directMessge, SIGNAL(triggered(bool)), SLOT(showDirectMessageDialog())); menu->addAction(directMessge); QAction *search = new QAction(QIcon::fromTheme(QLatin1String("edit-find")), i18n("Search..."), menu); search->setData(theAccount->alias()); connect(search, SIGNAL(triggered(bool)), SLOT(showSearchDialog())); menu->addAction(search); QAction *updateFriendsList = new QAction(QIcon::fromTheme(QLatin1String("arrow-down")), i18n("Update Friends List"), menu); updateFriendsList->setData(theAccount->alias()); connect(updateFriendsList, SIGNAL(triggered(bool)), SLOT(slotUpdateFriendsList())); menu->addAction(updateFriendsList); return menu; } QList< Choqok::Post * > TwitterApiMicroBlog::loadTimeline(Choqok::Account *account, const QString &timelineName) { QList< Choqok::Post * > list; if (timelineName.compare(QLatin1String("Favorite")) == 0) { return list; //NOTE Won't cache favorites, and this is for compatibility with older versions! } qCDebug(CHOQOK) << timelineName; QString fileName = Choqok::AccountManager::generatePostBackupFileName(account->alias(), timelineName); KConfig postsBackup(fileName, KConfig::NoGlobals, QStandardPaths::DataLocation); QStringList tmpList = postsBackup.groupList(); /// to 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); int count = groupList.count(); if (count) { Choqok::Post *st = 0; for (int i = 0; i < count; ++i) { st = new Choqok::Post; KConfigGroup grp(&postsBackup, groupList[i].toString()); st->creationDateTime = grp.readEntry("creationDateTime", QDateTime::currentDateTime()); st->postId = grp.readEntry("postId", QString()); st->content = grp.readEntry("text", QString()); st->source = grp.readEntry("source", QString()); st->replyToPostId = grp.readEntry("inReplyToPostId", QString()); st->replyToUserId = grp.readEntry("inReplyToUserId", QString()); st->isFavorited = grp.readEntry("favorited", false); st->replyToUserName = grp.readEntry("inReplyToUserName", QString()); st->author.userId = grp.readEntry("authorId", QString()); st->author.userName = grp.readEntry("authorUserName", QString()); st->author.realName = grp.readEntry("authorRealName", QString()); st->author.profileImageUrl = grp.readEntry("authorProfileImageUrl", QString()); st->author.description = grp.readEntry("authorDescription" , QString()); st->author.isProtected = grp.readEntry("isProtected", false); st->isPrivate = grp.readEntry("isPrivate" , false); st->author.location = grp.readEntry("authorLocation", QString()); st->link = postUrl(account, st->author.userName, st->postId); st->isRead = grp.readEntry("isRead", true); st->repeatedFromUsername = grp.readEntry("repeatedFrom", QString()); st->repeatedPostId = grp.readEntry("repeatedPostId", QString()); st->conversationId = grp.readEntry("conversationId", QString()); st->media = grp.readEntry("mediaUrl", QString()); st->mediaSizeWidth = grp.readEntry("mediaWidth", 0); st->mediaSizeHeight = grp.readEntry("mediaHeight", 0); list.append(st); } mTimelineLatestId[account][timelineName] = st->postId; } return list; } void TwitterApiMicroBlog::saveTimeline(Choqok::Account *account, const QString &timelineName, const QList< Choqok::UI::PostWidget * > &timeline) { if (timelineName.compare(QLatin1String("Favorite")) != 0) { qCDebug(CHOQOK); QString fileName = Choqok::AccountManager::generatePostBackupFileName(account->alias(), timelineName); KConfig postsBackup(fileName, KConfig::NoGlobals, QStandardPaths::DataLocation); ///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) { const Choqok::Post *post = ((*it)->currentPost()); KConfigGroup grp(&postsBackup, post->creationDateTime.toString()); grp.writeEntry("creationDateTime", post->creationDateTime); grp.writeEntry("postId", post->postId); grp.writeEntry("text", post->content); grp.writeEntry("source", post->source); grp.writeEntry("inReplyToPostId", post->replyToPostId); grp.writeEntry("inReplyToUserId", post->replyToUserId); grp.writeEntry("favorited", post->isFavorited); grp.writeEntry("inReplyToUserName", post->replyToUserName); grp.writeEntry("authorId", post->author.userId); grp.writeEntry("authorUserName", post->author.userName); grp.writeEntry("authorRealName", post->author.realName); grp.writeEntry("authorProfileImageUrl", post->author.profileImageUrl); grp.writeEntry("authorDescription" , post->author.description); grp.writeEntry("isPrivate" , post->isPrivate); grp.writeEntry("authorLocation" , post->author.location); grp.writeEntry("isProtected" , post->author.isProtected); grp.writeEntry("isRead" , post->isRead); grp.writeEntry("repeatedFrom", post->repeatedFromUsername); grp.writeEntry("repeatedPostId", post->repeatedPostId); grp.writeEntry("conversationId", post->conversationId); grp.writeEntry("mediaUrl", post->media); grp.writeEntry("mediaWidth", post->mediaSizeWidth); grp.writeEntry("mediaHeight", post->mediaSizeHeight); } postsBackup.sync(); } if (Choqok::Application::isShuttingDown()) { --d->countOfTimelinesToSave; if (d->countOfTimelinesToSave < 1) { Q_EMIT readyForUnload(); } } } Choqok::UI::ComposerWidget *TwitterApiMicroBlog::createComposerWidget(Choqok::Account *account, QWidget *parent) { return new TwitterApiComposerWidget(account, parent); } TwitterApiSearchTimelineWidget *TwitterApiMicroBlog::createSearchTimelineWidget(Choqok::Account *theAccount, QString name, const SearchInfo &info, QWidget *parent) { return new TwitterApiSearchTimelineWidget(theAccount, name, info, parent); } void TwitterApiMicroBlog::createPost(Choqok::Account *theAccount, Choqok::Post *post) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QByteArray data; QOAuth::ParamMap params; 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; } if (!post->isPrivate) { ///Status Update QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/statuses/update.%1").arg(format)); params.insert("status", QUrl::toPercentEncoding(post->content)); if (!post->replyToPostId.isEmpty()) { params.insert("in_reply_to_status_id", post->replyToPostId.toLocal8Bit()); } data = "status="; data += QUrl::toPercentEncoding(post->content); if (!post->replyToPostId.isEmpty()) { data += "&in_reply_to_status_id="; data += post->replyToPostId.toLocal8Bit(); } if (!account->usingOAuth()) { data += "&source=Choqok"; } KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; return; } job->addMetaData(QStringLiteral("content-type"), QStringLiteral("Content-Type: application/x-www-form-urlencoded")); job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, url, QOAuth::POST, params))); mCreatePostMap[ job ] = post; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotCreatePost(KJob*))); job->start(); } else {///Direct message QString recipientScreenName = post->replyToUserName; QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/direct_messages/new.%1").arg(format)); params.insert("user", recipientScreenName.toLocal8Bit()); params.insert("text", QUrl::toPercentEncoding(post->content)); data = "user="; data += recipientScreenName.toLocal8Bit(); data += "&text="; data += QUrl::toPercentEncoding(post->content); if (!account->usingOAuth()) { data += "&source=Choqok"; } KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; // QString errMsg = i18n ( "Creating the new post failed. Cannot create an http POST request. Please check your KDE installation." ); // emit errorPost ( theAccount, post, Choqok::MicroBlog::OtherError, errMsg, MicroBlog::Critical ); return; } job->addMetaData(QStringLiteral("content-type"), QStringLiteral("Content-Type: application/x-www-form-urlencoded")); job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, url, QOAuth::POST, params))); mCreatePostMap[ job ] = post; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotCreatePost(KJob*))); job->start(); } } void TwitterApiMicroBlog::repeatPost(Choqok::Account *theAccount, const QString &postId) { qCDebug(CHOQOK); if (postId.isEmpty()) { qCCritical(CHOQOK) << "ERROR: PostId is empty!"; return; } TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/statuses/retweet/%1.%2").arg(postId).arg(format)); QByteArray data; KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; return; } job->addMetaData(QStringLiteral("content-type"), QStringLiteral("Content-Type: application/x-www-form-urlencoded")); job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, url, QOAuth::POST))); Choqok::Post *post = new Choqok::Post; post->postId = postId; mCreatePostMap[ job ] = post; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotCreatePost(KJob*))); job->start(); } void TwitterApiMicroBlog::slotCreatePost(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer"; return; } Choqok::Post *post = mCreatePostMap.take(job); Choqok::Account *theAccount = mJobsAccount.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 errorPost(theAccount, post, Choqok::MicroBlog::CommunicationError, i18n("Creating the new post failed. %1", job->errorString()), MicroBlog::Critical); } else { KIO::StoredTransferJob *stj = qobject_cast< KIO::StoredTransferJob * > (job); if (!post->isPrivate) { readPost(theAccount, stj->data(), post); if (post->isError) { QString errorMsg; errorMsg = checkForError(stj->data()); if (errorMsg.isEmpty()) { // ???? If empty, why is there an error? qCCritical(CHOQOK) << "Creating post: JSON parsing error:" << stj->data() ; Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ParsingError, i18n("Creating the new post failed. The result data could not be parsed."), MicroBlog::Critical); } else { qCCritical(CHOQOK) << "Server Error:" << errorMsg ; Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ServerError, i18n("Creating the new post failed, with error:%1", errorMsg), MicroBlog::Critical); } } else { Choqok::NotifyManager::success(i18n("New post submitted successfully")); Q_EMIT postCreated(theAccount, post); } } else { Choqok::NotifyManager::success(i18n("Private message sent successfully")); Q_EMIT postCreated(theAccount, post); } } } void TwitterApiMicroBlog::abortAllJobs(Choqok::Account *theAccount) { Q_FOREACH (KJob *job, mJobsAccount.keys(theAccount)) { job->kill(KJob::EmitResult); } } void TwitterApiMicroBlog::abortCreatePost(Choqok::Account *theAccount, Choqok::Post *post) { if (mCreatePostMap.isEmpty()) { return; } if (post) { mCreatePostMap.key(post)->kill(KJob::EmitResult); } else { Q_FOREACH (KJob *job, mCreatePostMap.keys()) { if (mJobsAccount[job] == theAccount) { job->kill(KJob::EmitResult); } } } } void TwitterApiMicroBlog::fetchPost(Choqok::Account *theAccount, Choqok::Post *post) { qCDebug(CHOQOK); if (!post || post->postId.isEmpty()) { return; } TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/statuses/show/%1.%2").arg(post->postId).arg(format)); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; // QString errMsg = i18n ( "Fetching the new post failed. Cannot create an HTTP GET request." // "Please check your KDE installation." ); // emit errorPost ( theAccount, post, Choqok::MicroBlog::OtherError, errMsg, Low ); return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, url, QOAuth::GET))); mFetchPostMap[ job ] = post; mJobsAccount[ job ] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotFetchPost(KJob*))); job->start(); } void TwitterApiMicroBlog::slotFetchPost(KJob *job) { qCDebug(CHOQOK); if (!job) { qCWarning(CHOQOK) << "NULL Job returned"; return; } Choqok::Post *post = mFetchPostMap.take(job); Choqok::Account *theAccount = mJobsAccount.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Fetching the new post failed. %1", job->errorString()), Low); } else { KIO::StoredTransferJob *stj = qobject_cast (job); readPost(theAccount, stj->data(), post); if (post->isError) { QString errorMsg; errorMsg = checkForError(stj->data()); if (errorMsg.isEmpty()) { qCDebug(CHOQOK) << "Parsing Error"; Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ParsingError, i18n("Fetching new post failed. The result data could not be parsed."), Low); } else { qCCritical(CHOQOK) << "Fetching post: Server Error:" << errorMsg; Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ServerError, i18n("Fetching new post failed, with error:%1", errorMsg), Low); } } else { post->isError = true; Q_EMIT postFetched(theAccount, post); } } } void TwitterApiMicroBlog::removePost(Choqok::Account *theAccount, Choqok::Post *post) { qCDebug(CHOQOK); if (!post->postId.isEmpty()) { TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); if (!post->isPrivate) { url.setPath(url.path() + QStringLiteral("/statuses/destroy/%1.%2").arg(post->postId).arg(format)); } else { url.setPath(url.path() + QStringLiteral("/direct_messages/destroy/%1.%2").arg(post->postId).arg(format)); } KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; // QString errMsg = i18n ( "Removing the post failed. Cannot create an HTTP POST request. Please check your KDE installation." ); // emit errorPost ( theAccount, post, Choqok::MicroBlog::OtherError, errMsg, MicroBlog::Critical ); return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, url, QOAuth::POST))); mRemovePostMap[job] = post; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotRemovePost(KJob*))); job->start(); } } void TwitterApiMicroBlog::slotRemovePost(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer."; return; } Choqok::Post *post = mRemovePostMap.take(job); Choqok::Account *theAccount = mJobsAccount.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT errorPost(theAccount, post, CommunicationError, i18n("Removing the post failed. %1", job->errorString()), MicroBlog::Critical); } else { KIO::StoredTransferJob *stj = qobject_cast(job); QString errMsg = checkForError(stj->data()); if (errMsg.isEmpty()) { Q_EMIT postRemoved(theAccount, post); } else { qCCritical(CHOQOK) << "Server error on removing post:" << errMsg; Q_EMIT errorPost(theAccount, post, ServerError, i18n("Removing the post failed. %1", errMsg), MicroBlog::Critical); } } } void TwitterApiMicroBlog::createFavorite(Choqok::Account *theAccount, const QString &postId) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/favorites/create.%1").arg(format)); QUrl tmp(url); QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("id"), postId); url.setQuery(urlQuery); QOAuth::ParamMap params; params.insert("id", postId.toLatin1()); KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; // QString errMsg = i18n ( "The Favorite creation failed. Cannot create an http POST request. " // "Please check your KDE installation." ); // emit error ( theAccount, OtherError, errMsg ); return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, tmp, QOAuth::POST, params))); mFavoriteMap[job] = postId; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotCreateFavorite(KJob*))); job->start(); } void TwitterApiMicroBlog::slotCreateFavorite(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer."; return; } Choqok::Account *theAccount = mJobsAccount.take(job); QString postId = mFavoriteMap.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, CommunicationError, i18n("Favorite creation failed. %1", job->errorString())); } else { KIO::StoredTransferJob *stJob = qobject_cast(job); QString err = checkForError(stJob->data()); if (!err.isEmpty()) { Q_EMIT error(theAccount, ServerError, err, Critical); return; } else { Q_EMIT favoriteCreated(theAccount, postId); } } } void TwitterApiMicroBlog::removeFavorite(Choqok::Account *theAccount, const QString &postId) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/favorites/destroy.%1").arg(format)); QUrl tmp(url); QUrlQuery tmpUrlQuery; tmpUrlQuery.addQueryItem(QLatin1String("id"), postId); url.setQuery(tmpUrlQuery); QOAuth::ParamMap params; params.insert("id", postId.toLatin1()); KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; // QString errMsg = i18n ( "Removing the favorite failed. Cannot create an http POST request. " // "Please check your KDE installation." ); // emit error ( theAccount, OtherError, errMsg ); return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, tmp, QOAuth::POST, params))); mFavoriteMap[job] = postId; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotRemoveFavorite(KJob*))); job->start(); } void TwitterApiMicroBlog::slotRemoveFavorite(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer."; return; } QString id = mFavoriteMap.take(job); Choqok::Account *theAccount = mJobsAccount.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, CommunicationError, i18n("Removing the favorite failed. %1", job->errorString())); } else { KIO::StoredTransferJob *stJob = qobject_cast(job); QString err = checkForError(stJob->data()); if (!err.isEmpty()) { Q_EMIT error(theAccount, ServerError, err, Critical); return; } else { Q_EMIT favoriteRemoved(theAccount, id); } } } void TwitterApiMicroBlog::listFriendsUsername(TwitterApiAccount *theAccount, bool active) { friendsList.clear(); d->friendsCursor = QLatin1String("-1"); if (theAccount) { requestFriendsScreenName(theAccount, active); } } void TwitterApiMicroBlog::requestFriendsScreenName(TwitterApiAccount *theAccount, bool active) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + (QStringLiteral("/friends/list.%1").arg(format))); QUrl tmpUrl(url); QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("cursor"), d->friendsCursor); urlQuery.addQueryItem(QLatin1String("count"), QLatin1String("200")); url.setQuery(urlQuery); QOAuth::ParamMap params; params.insert("cursor", d->friendsCursor.toLatin1()); params.insert("count", QStringLiteral("200").toLatin1()); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, tmpUrl, QOAuth::GET, params))); mJobsAccount[job] = theAccount; if (active) { connect(job, SIGNAL(result(KJob*)), this, SLOT(slotRequestFriendsScreenNameActive(KJob*))); } else { connect(job, SIGNAL(result(KJob*)), this, SLOT(slotRequestFriendsScreenNamePassive(KJob*))); } job->start(); Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Updating friends list for account %1...", theAccount->username())); } void TwitterApiMicroBlog::slotRequestFriendsScreenNameActive(KJob *job) { finishRequestFriendsScreenName(job, true); } void TwitterApiMicroBlog::slotRequestFriendsScreenNamePassive(KJob *job) { finishRequestFriendsScreenName(job, false); } void TwitterApiMicroBlog::finishRequestFriendsScreenName(KJob *job, bool active) { qCDebug(CHOQOK); TwitterApiAccount *theAccount = qobject_cast(mJobsAccount.take(job)); KIO::StoredTransferJob *stJob = qobject_cast(job); Choqok::MicroBlog::ErrorLevel level = active ? Critical : Low; if (stJob->error()) { Q_EMIT error(theAccount, ServerError, i18n("Friends list for account %1 could not be updated:\n%2", theAccount->username(), stJob->errorString()), level); return; } - QStringList newList = readUsersScreenName(theAccount, stJob->data()); + QStringList newList = readFriendsScreenName(theAccount, stJob->data()); newList.removeDuplicates(); if (! checkForError(stJob->data()).isEmpty()) { // if an error occurred, do not replace the friends list. theAccount->setFriendsList(friendsList); Q_EMIT friendsUsernameListed(theAccount, friendsList); } else if (QString::compare(d->friendsCursor, QLatin1String("0"))) { // if the cursor is not "0", there is more friends data to be had friendsList << newList; requestFriendsScreenName(theAccount, active); } else { friendsList << newList; theAccount->setFriendsList(friendsList); Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Friends list for account %1 has been updated.", theAccount->username())); Q_EMIT friendsUsernameListed(theAccount, friendsList); } } +void TwitterApiMicroBlog::listFollowersUsername(TwitterApiAccount* theAccount, bool active) +{ + followersList.clear(); + d->followersCursor = QLatin1String("-1"); + if ( theAccount ) { + requestFollowersScreenName(theAccount, active); + } +} + +void TwitterApiMicroBlog::requestFollowersScreenName(TwitterApiAccount* theAccount, bool active) +{ + qCDebug(CHOQOK); + TwitterApiAccount* account = qobject_cast(theAccount); + QUrl url = account->apiUrl(); + url = url.adjusted(QUrl::StripTrailingSlash); + url.setPath(url.path() + (QStringLiteral("/followers/list.%1").arg(format))); + QUrl tmpUrl(url); + QUrlQuery urlQuery; + urlQuery.addQueryItem(QLatin1String("cursor"), d->followersCursor); + urlQuery.addQueryItem(QLatin1String("count"), QLatin1String("200")); + url.setQuery(urlQuery); + QOAuth::ParamMap params; + params.insert("cursor", d->followersCursor.toLatin1()); + params.insert("count", QStringLiteral("200").toLatin1()); + + KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); + if (!job) { + qCDebug(CHOQOK) << "Cannot create an http GET request!"; + return; + } + job->addMetaData(QStringLiteral("customHTTPHeader"), + QStringLiteral("Authorization: ") + + QLatin1String(authorizationHeader(account, tmpUrl, QOAuth::GET, params))); + mJobsAccount[job] = theAccount; + if (active) { + connect( job, SIGNAL( result( KJob* ) ), this, SLOT( slotRequestFollowersScreenNameActive(KJob*) ) ); + } else { + connect( job, SIGNAL( result( KJob* ) ), this, SLOT( slotRequestFollowersScreenNamePassive(KJob*) ) ); + } + job->start(); + Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Updating followers list for account %1...", theAccount->username())); +} + +void TwitterApiMicroBlog::slotRequestFollowersScreenNameActive(KJob* job) +{ + finishRequestFollowersScreenName(job, true); +} + +void TwitterApiMicroBlog::slotRequestFollowersScreenNamePassive(KJob* job) +{ + finishRequestFollowersScreenName(job, false); +} + +void TwitterApiMicroBlog::finishRequestFollowersScreenName(KJob* job, bool active) +{ + qCDebug(CHOQOK); + TwitterApiAccount *theAccount = qobject_cast( mJobsAccount.take(job) ); + KIO::StoredTransferJob* stJob = qobject_cast( job ); + Choqok::MicroBlog::ErrorLevel level = active ? Critical : Low; + if (stJob->error()) { + Q_EMIT error(theAccount, ServerError, i18n("Followers list for account %1 could not be updated:\n%2", + theAccount->username(), stJob->errorString()), level); + return; + } + QStringList newList = readFollowersScreenName(theAccount, stJob->data()); + newList.removeDuplicates(); + if (!checkForError(stJob->data()).isEmpty()) { // if an error occurred, do not replace the friends list. + theAccount->setFollowersList(followersList); + Q_EMIT followersUsernameListed(theAccount, followersList); + } else if (QString::compare(d->followersCursor, QLatin1String("0"))) { // if the cursor is not "0", there is more friends data to be had + followersList << newList; + requestFollowersScreenName(theAccount, active); + } else { + followersList << newList; + theAccount->setFollowersList(followersList); + Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Followers list for account %1 has been updated.", + theAccount->username()) ); + Q_EMIT followersUsernameListed(theAccount, followersList); + } +} + void TwitterApiMicroBlog::updateTimelines(Choqok::Account *theAccount) { qCDebug(CHOQOK); Q_FOREACH (const QString &tm, theAccount->timelineNames()) { requestTimeLine(theAccount, tm, mTimelineLatestId[theAccount][tm]); } } void TwitterApiMicroBlog::requestTimeLine(Choqok::Account *theAccount, QString type, QString latestStatusId, int page, QString maxId) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + timelineApiPath[type].arg(format)); QUrl tmpUrl(url); int countOfPost = Choqok::BehaviorSettings::countOfPosts(); QOAuth::ParamMap params; // needed because lists have different parameter names but // returned timelines have the same JSON format if (timelineApiPath[type].contains(QLatin1String("lists/statuses"))) { QUrlQuery urlQuery; // type contains @username/timelinename const QString slug = type.mid(type.indexOf(QLatin1String("/")) + 1); urlQuery.addQueryItem(QLatin1String("slug"), slug); params.insert("slug", slug.toLatin1()); const QString owner = type.mid(1, type.indexOf(QLatin1String("/")) - 1); urlQuery.addQueryItem(QLatin1String("owner_screen_name"), owner); params.insert("owner_screen_name", owner.toLatin1()); url.setQuery(urlQuery); } else { QUrlQuery urlQuery; if (account->usingOAuth()) { //TODO: Check if needed if (!latestStatusId.isEmpty()) { params.insert("since_id", latestStatusId.toLatin1()); countOfPost = 200; } params.insert("count", QByteArray::number(countOfPost)); if (!maxId.isEmpty()) { params.insert("max_id", maxId.toLatin1()); } if (page) { params.insert("page", QByteArray::number(page)); } } if (!latestStatusId.isEmpty()) { urlQuery.addQueryItem(QLatin1String("since_id"), latestStatusId); countOfPost = 200; } urlQuery.addQueryItem(QLatin1String("count"), QString::number(countOfPost)); if (!maxId.isEmpty()) { urlQuery.addQueryItem(QLatin1String("max_id"), maxId); } if (page) { urlQuery.addQueryItem(QLatin1String("page"), QString::number(page)); } url.setQuery(urlQuery); } qCDebug(CHOQOK) << "Latest" << type << "Id:" << latestStatusId;// << "apiReq:" << url; KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; // QString errMsg = i18n ( "Cannot create an http GET request. Please check your KDE installation." ); // emit error ( theAccount, OtherError, errMsg, Low ); return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, tmpUrl, QOAuth::GET, params))); mRequestTimelineMap[job] = type; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotRequestTimeline(KJob*))); job->start(); } void TwitterApiMicroBlog::slotRequestTimeline(KJob *job) { qCDebug(CHOQOK);//TODO Add error detection for XML "checkXmlForError()" and JSON if (!job) { qCDebug(CHOQOK) << "Job is null pointer"; return; } Choqok::Account *theAccount = mJobsAccount.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, CommunicationError, i18n("Timeline update failed: %1", job->errorString()), Low); return; } QString type = mRequestTimelineMap.take(job); if (isValidTimeline(type)) { KIO::StoredTransferJob *j = qobject_cast(job); QList list; if (type == QLatin1String("Inbox") || type == QLatin1String("Outbox")) { list = readDirectMessages(theAccount, j->data()); } else { list = readTimeline(theAccount, j->data()); } if (!list.isEmpty()) { mTimelineLatestId[theAccount][type] = list.last()->postId; Q_EMIT timelineDataReceived(theAccount, type, list); } } } QByteArray TwitterApiMicroBlog::authorizationHeader(TwitterApiAccount *theAccount, const QUrl &requestUrl, QOAuth::HttpMethod method, QOAuth::ParamMap params) { QByteArray auth; if (theAccount->usingOAuth()) { auth = theAccount->oauthInterface()->createParametersString(requestUrl.url(), method, theAccount->oauthToken(), theAccount->oauthTokenSecret(), QOAuth::HMAC_SHA1, params, QOAuth::ParseForHeaderArguments); } else { auth = theAccount->username().toUtf8() + ':' + theAccount->password().toUtf8(); auth = auth.toBase64().prepend("Basic "); } return auth; } void TwitterApiMicroBlog::setRepeatedOfInfo(Choqok::Post *post, Choqok::Post *repeatedPost) { if (Choqok::AppearanceSettings::showRetweetsInChoqokWay()) { post->content = repeatedPost->content; post->replyToPostId = repeatedPost->replyToPostId; post->replyToUserId = repeatedPost->replyToUserId; post->replyToUserName = repeatedPost->replyToUserName; post->repeatedFromUsername = repeatedPost->author.userName; post->repeatedPostId = repeatedPost->postId; } else { post->content = repeatedPost->content; post->replyToPostId = repeatedPost->replyToPostId; post->replyToUserId = repeatedPost->replyToUserId; post->replyToUserName = repeatedPost->replyToUserName; post->repeatedFromUsername = post->author.userName; post->author = repeatedPost->author; post->repeatedPostId = repeatedPost->postId; } //post->creationDateTime = repeatedPost->creationDateTime; } QDateTime TwitterApiMicroBlog::dateFromString(const QString &date) { char s[10]; int year, day, hours, minutes, seconds, tz; sscanf(qPrintable(date), "%*s %s %d %d:%d:%d %d %d", s, &day, &hours, &minutes, &seconds, &tz, &year); int month = d->monthes[QLatin1String(s)]; QDateTime recognized(QDate(year, month, day), QTime(hours, minutes, seconds)); if (tz == 0) { //tz is the timezone, in Twitter it's always UTC(0) in Identica it's local +/-NUMBER recognized.setTimeSpec(Qt::UTC); } return recognized.toLocalTime(); } void TwitterApiMicroBlog::aboutToUnload() { d->countOfTimelinesToSave = 0; Q_FOREACH (Choqok::Account *acc, Choqok::AccountManager::self()->accounts()) { if (acc->microblog() == this) { d->countOfTimelinesToSave += acc->timelineNames().count(); } } Q_EMIT saveTimelines(); } void TwitterApiMicroBlog::showDirectMessageDialog(TwitterApiAccount *theAccount/* = 0*/, const QString &toUsername/* = QString()*/) { qCDebug(CHOQOK); if (!theAccount) { QAction *act = qobject_cast(sender()); theAccount = qobject_cast( Choqok::AccountManager::self()->findAccount(act->data().toString())); } TwitterApiDMessageDialog *dmsg = new TwitterApiDMessageDialog(theAccount, Choqok::UI::Global::mainWindow()); if (!toUsername.isEmpty()) { dmsg->setTo(toUsername); } dmsg->show(); } Choqok::TimelineInfo *TwitterApiMicroBlog::timelineInfo(const QString &timelineName) { if (isValidTimeline(timelineName)) { return mTimelineInfos.value(timelineName); } else { return 0; } } void TwitterApiMicroBlog::showSearchDialog(TwitterApiAccount *theAccount) { if (!theAccount) { QAction *act = qobject_cast(sender()); theAccount = qobject_cast( Choqok::AccountManager::self()->findAccount(act->data().toString())); } QPointer searchDlg = new TwitterApiSearchDialog(theAccount, Choqok::UI::Global::mainWindow()); searchDlg->show(); } void TwitterApiMicroBlog::slotUpdateFriendsList() { QAction *act = qobject_cast(sender()); TwitterApiAccount *theAccount = qobject_cast( Choqok::AccountManager::self()->findAccount(act->data().toString())); listFriendsUsername(theAccount, true); } void TwitterApiMicroBlog::createFriendship(Choqok::Account *theAccount, const QString &username) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/friendships/create.%1").arg(format)); QUrl tmp(url); QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("screen_name"), username); url.setQuery(urlQuery); QOAuth::ParamMap params; params.insert("screen_name", username.toLatin1()); KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; qCDebug(CHOQOK) << url; if (!job) { qCCritical(CHOQOK) << "Cannot create an http POST request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, tmp, QOAuth::POST, params))); mJobsAccount[job] = theAccount; mFriendshipMap[ job ] = username; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotCreateFriendship(KJob*))); job->start(); } void TwitterApiMicroBlog::slotCreateFriendship(KJob *job) { qCDebug(CHOQOK); if (!job) { qCCritical(CHOQOK) << "Job is a null Pointer!"; return; } TwitterApiAccount *theAccount = qobject_cast(mJobsAccount.take(job)); QString username = mFriendshipMap.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, CommunicationError, i18n("Creating friendship with %1 failed. %2", username, job->errorString())); return; } KIO::StoredTransferJob *stj = qobject_cast(job); Choqok::User *user = readUserInfo(stj->data()); if (user /*&& user->userName.compare(username, Qt::CaseInsensitive)*/) { Q_EMIT friendshipCreated(theAccount, username); Choqok::NotifyManager::success(i18n("You are now listening to %1's posts.", username)); theAccount->setFriendsList(QStringList()); listFriendsUsername(theAccount); } else { QString errorMsg = checkForError(stj->data()); if (errorMsg.isEmpty()) { qCDebug(CHOQOK) << "Parse Error:" << stj->data(); Q_EMIT error(theAccount, ParsingError, i18n("Creating friendship with %1 failed: the server returned invalid data.", username)); } else { qCDebug(CHOQOK) << "Server error:" << errorMsg; Q_EMIT error(theAccount, ServerError, i18n("Creating friendship with %1 failed: %2", username, errorMsg)); } } } void TwitterApiMicroBlog::destroyFriendship(Choqok::Account *theAccount, const QString &username) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/friendships/destroy.%1").arg(format)); QUrl tmp(url); QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("screen_name"), username); url.setQuery(urlQuery); QOAuth::ParamMap params; params.insert("screen_name", username.toLatin1()); KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; if (!job) { qCCritical(CHOQOK) << "Cannot create an http POST request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, tmp, QOAuth::POST, params))); mJobsAccount[job] = theAccount; mFriendshipMap[ job ] = username; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotDestroyFriendship(KJob*))); job->start(); } void TwitterApiMicroBlog::slotDestroyFriendship(KJob *job) { qCDebug(CHOQOK); if (!job) { qCCritical(CHOQOK) << "Job is a null Pointer!"; return; } TwitterApiAccount *theAccount = qobject_cast(mJobsAccount.take(job)); QString username = mFriendshipMap.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, CommunicationError, i18n("Destroying friendship with %1 failed. %2", username, job->errorString())); return; } KIO::StoredTransferJob *stj = qobject_cast(job); Choqok::User *user = readUserInfo(stj->data()); if (user /*&& user->userName.compare( username, Qt::CaseInsensitive )*/) { Q_EMIT friendshipDestroyed(theAccount, username); Choqok::NotifyManager::success(i18n("You will not receive %1's updates.", username)); theAccount->setFriendsList(QStringList()); listFriendsUsername(theAccount); } else { QString errorMsg = checkForError(stj->data()); if (errorMsg.isEmpty()) { qCDebug(CHOQOK) << "Parse Error:" << stj->data(); Q_EMIT error(theAccount, ParsingError, i18n("Destroying friendship with %1 failed: the server returned invalid data.", username)); } else { qCDebug(CHOQOK) << "Server error:" << errorMsg; Q_EMIT error(theAccount, ServerError, i18n("Destroying friendship with %1 failed: %2", username, errorMsg)); } } } void TwitterApiMicroBlog::blockUser(Choqok::Account *theAccount, const QString &username) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/blocks/create.%1").arg(format)); QUrl tmp(url); QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("screen_name"), username); url.setQuery(urlQuery); QOAuth::ParamMap params; params.insert("screen_name", username.toLatin1()); KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; if (!job) { qCCritical(CHOQOK) << "Cannot create an http POST request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, tmp, QOAuth::POST, params))); mJobsAccount[job] = theAccount; mFriendshipMap[ job ] = username; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotBlockUser(KJob*))); job->start(); } void TwitterApiMicroBlog::reportUserAsSpam(Choqok::Account *theAccount, const QString &username) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QStringLiteral("/users/report_spam.%1").arg(format)); QUrl tmp(url); QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("screen_name"), username); url.setQuery(urlQuery); QOAuth::ParamMap params; params.insert("screen_name", username.toLatin1()); KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; if (!job) { qCCritical(CHOQOK) << "Cannot create an http POST request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, tmp, QOAuth::POST, params))); mJobsAccount[job] = theAccount; mFriendshipMap[ job ] = username; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotReportUser(KJob*))); job->start(); } void TwitterApiMicroBlog::slotBlockUser(KJob *job) { qCDebug(CHOQOK); if (!job) { qCCritical(CHOQOK) << "Job is a null Pointer!"; return; } Choqok::Account *theAccount = mJobsAccount.take(job); QString username = mFriendshipMap.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, CommunicationError, i18n("Blocking %1 failed. %2", username, job->errorString())); return; } Choqok::User *user = readUserInfo(qobject_cast(job)->data()); if (user /*&& user->userName.compare( username, Qt::CaseInsensitive )*/) { Q_EMIT userBlocked(theAccount, username); Choqok::NotifyManager::success(i18n("You will no longer be disturbed by %1.", username)); } else { qCDebug(CHOQOK) << "Parse Error:" << qobject_cast(job)->data(); Q_EMIT error(theAccount, ParsingError, i18n("Blocking %1 failed: the server returned invalid data.", username)); } //TODO Check for failor! } void TwitterApiMicroBlog::slotReportUser(KJob *job) { qCDebug(CHOQOK); if (!job) { qCCritical(CHOQOK) << "Job is a null Pointer!"; return; } Choqok::Account *theAccount = mJobsAccount.take(job); QString username = mFriendshipMap.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, CommunicationError, i18n("Reporting %1 failed. %2", username, job->errorString())); return; } Choqok::User *user = readUserInfo(qobject_cast(job)->data()); if (user) { Choqok::NotifyManager::success(i18n("Report sent successfully")); } else { qCDebug(CHOQOK) << "Parse Error:" << qobject_cast(job)->data(); Q_EMIT error(theAccount, ParsingError, i18n("Reporting %1 failed: the server returned invalid data.", username)); } } ///=================================================================== QString TwitterApiMicroBlog::checkForError(const QByteArray &buffer) { const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { const QVariantMap map = json.toVariant().toMap(); if (map.contains(QLatin1String("errors"))) { QStringList errors; Q_FOREACH (const QVariant &msg, map[QLatin1String("errors")].toList()) { errors.append(msg.toMap()[QLatin1String("message")].toString()); qCCritical(CHOQOK) << "Error:" << errors.last(); } return errors.join(QLatin1Char(';')); } } return QString(); } QList< Choqok::Post * > TwitterApiMicroBlog::readTimeline(Choqok::Account *theAccount, const QByteArray &buffer) { QList postList; const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { QVariantList list = json.toVariant().toList(); QVariantList::const_iterator it = list.constBegin(); QVariantList::const_iterator endIt = list.constEnd(); for (; it != endIt; ++it) { postList.prepend(readPost(theAccount, it->toMap(), new Choqok::Post)); } } else { QString err = checkForError(buffer); if (err.isEmpty()) { qCCritical(CHOQOK) << "JSON parsing failed.\nBuffer was: \n" << buffer; Q_EMIT error(theAccount, ParsingError, i18n("Could not parse the data that has been received from the server.")); } else { Q_EMIT error(theAccount, ServerError, err); } return postList; } return postList; } Choqok::Post *TwitterApiMicroBlog::readPost(Choqok::Account *theAccount, const QByteArray &buffer, Choqok::Post *post) { const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { return readPost(theAccount, json.toVariant().toMap(), post); } else { if (!post) { qCCritical(CHOQOK) << "TwitterApiMicroBlog::readPost: post is NULL!"; post = new Choqok::Post; } Q_EMIT errorPost(theAccount, post, ParsingError, i18n("Could not parse the data that has been received from the server.")); qCCritical(CHOQOK) << "JSon parsing failed. Buffer was:" << buffer; post->isError = true; return post; } } Choqok::Post *TwitterApiMicroBlog::readPost(Choqok::Account *theAccount, const QVariantMap &var, Choqok::Post *post) { if (!post) { qCCritical(CHOQOK) << "TwitterApiMicroBlog::readPost: post is NULL!"; return 0; } post->content = var[QLatin1String("text")].toString(); post->creationDateTime = dateFromString(var[QLatin1String("created_at")].toString()); post->isFavorited = var[QLatin1String("favorited")].toBool(); post->postId = var[QLatin1String("id")].toString(); post->replyToPostId = var[QLatin1String("in_reply_to_status_id")].toString(); post->replyToUserId = var[QLatin1String("in_reply_to_user_id")].toString(); post->replyToUserName = var[QLatin1String("in_reply_to_screen_name")].toString(); post->source = var[QLatin1String("source")].toString(); QVariantMap userMap = var[QLatin1String("user")].toMap(); post->author.description = userMap[QLatin1String("description")].toString(); post->author.location = userMap[QLatin1String("location")].toString(); post->author.realName = userMap[QLatin1String("name")].toString(); post->author.userId = userMap[QLatin1String("id")].toString(); post->author.userName = userMap[QLatin1String("screen_name")].toString(); post->author.profileImageUrl = userMap[QLatin1String("profile_image_url")].toString(); QVariantMap entities = var[QLatin1String("entities")].toMap(); QVariantMap mediaMap; QVariantList media = entities[QLatin1String("media")].toList(); if (media.size() > 0) { mediaMap = media.at(0).toMap(); post->media = mediaMap[QLatin1String("media_url")].toString() + QLatin1String(":small"); QVariantMap sizes = mediaMap[QLatin1String("sizes")].toMap(); QVariantMap w = sizes[QLatin1String("small")].toMap(); post->mediaSizeWidth = w[QLatin1String("w")].toInt() != 0 ? w[QLatin1String("w")].toInt() : 0; post->mediaSizeHeight = w[QLatin1String("h")].toInt() != 0 ? w[QLatin1String("h")].toInt() : 0; } else { post->media = QString(); post->mediaSizeHeight = 0; post->mediaSizeWidth = 0; } Choqok::Post *repeatedPost = 0; QVariantMap retweetedMap = var[QLatin1String("retweeted_status")].toMap(); if (!retweetedMap.isEmpty()) { repeatedPost = readPost(theAccount, retweetedMap, new Choqok::Post); setRepeatedOfInfo(post, repeatedPost); delete repeatedPost; } post->link = postUrl(theAccount, post->author.userName, post->postId); post->isRead = post->isFavorited || (post->repeatedFromUsername.compare(theAccount->username(), Qt::CaseInsensitive) == 0); return post; } QList< Choqok::Post * > TwitterApiMicroBlog::readDirectMessages(Choqok::Account *theAccount, const QByteArray &buffer) { QList postList; const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { QVariantList list = json.toVariant().toList(); QVariantList::const_iterator it = list.constBegin(); QVariantList::const_iterator endIt = list.constEnd(); for (; it != endIt; ++it) { postList.prepend(readDirectMessage(theAccount, it->toMap())); } } else { QString err = checkForError(buffer); if (err.isEmpty()) { qCCritical(CHOQOK) << "JSON parsing failed.\nBuffer was: \n" << buffer; Q_EMIT error(theAccount, ParsingError, i18n("Could not parse the data that has been received from the server.")); } else { Q_EMIT error(theAccount, ServerError, err); } return postList; } return postList; } Choqok::Post *TwitterApiMicroBlog::readDirectMessage(Choqok::Account *theAccount, const QByteArray &buffer) { const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { return readDirectMessage(theAccount, json.toVariant().toMap()); } else { Choqok::Post *post = new Choqok::Post; post->isError = true; return post; } } Choqok::Post *TwitterApiMicroBlog::readDirectMessage(Choqok::Account *theAccount, const QVariantMap &var) { Choqok::Post *msg = new Choqok::Post; msg->isPrivate = true; QString senderId, recipientId, timeStr, senderScreenName, recipientScreenName, senderProfileImageUrl, senderName, senderDescription, recipientProfileImageUrl, recipientName, recipientDescription; msg->creationDateTime = dateFromString(var[QLatin1String("created_at")].toString()); msg->content = var[QLatin1String("text")].toString(); msg->postId = var[QLatin1String("id")].toString();; senderId = var[QLatin1String("sender_id")].toString(); recipientId = var[QLatin1String("recipient_id")].toString(); senderScreenName = var[QLatin1String("sender_screen_name")].toString(); recipientScreenName = var[QLatin1String("recipient_screen_name")].toString(); QVariantMap sender = var[QLatin1String("sender")].toMap(); senderProfileImageUrl = sender[QLatin1String("profile_image_url")].toString(); senderName = sender[QLatin1String("name")].toString(); senderDescription = sender[QLatin1String("description")].toString(); QVariantMap recipient = var[QLatin1String("recipient")].toMap(); recipientProfileImageUrl = recipient[QLatin1String("profile_image_url")].toString(); recipientName = recipient[QLatin1String("name")].toString(); recipientDescription = recipient[QLatin1String("description")].toString(); if (senderScreenName.compare(theAccount->username(), Qt::CaseInsensitive) == 0) { msg->author.description = recipientDescription; msg->author.userName = recipientScreenName; msg->author.profileImageUrl = recipientProfileImageUrl; msg->author.realName = recipientName; msg->author.userId = recipientId; msg->replyToUserId = recipientId; msg->replyToUserName = recipientScreenName; msg->isRead = true; } else { msg->author.description = senderDescription; msg->author.userName = senderScreenName; msg->author.profileImageUrl = senderProfileImageUrl; msg->author.realName = senderName; msg->author.userId = senderId; msg->replyToUserId = recipientId; msg->replyToUserName = recipientScreenName; } return msg; } Choqok::User *TwitterApiMicroBlog::readUserInfo(const QByteArray &buffer) { Choqok::User *user; const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { Choqok::User u(readUser(0, json.toVariant().toMap())); user = &u; } else { QString err = i18n("Retrieving the friends list failed. The data returned from the server is corrupted."); qCDebug(CHOQOK) << "JSON parse error:the buffer is: \n" << buffer; Q_EMIT error(0, ParsingError, err, Critical); } return user; } -QStringList TwitterApiMicroBlog::readUsersScreenName(Choqok::Account *theAccount, +QStringList TwitterApiMicroBlog::readFriendsScreenName(Choqok::Account *theAccount, const QByteArray &buffer) { QStringList list; const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { const QVariantMap map = json.toVariant().toMap(); QVariantList jsonList = map[QLatin1String("users")].toList(); QString nextCursor = map[QLatin1String("next_cursor_str")].toString(); if (nextCursor.isEmpty()) { nextCursor = QLatin1String("0"); // we probably ran the rate limit; stop bugging the server already } - QVariantList::const_iterator it = jsonList.constBegin(); - QVariantList::const_iterator endIt = jsonList.constEnd(); - for (; it != endIt; ++it) { - list << it->toMap()[QLatin1String("screen_name")].toString(); + Q_FOREACH (const QVariant &user, jsonList) { + list << user.toMap()[QLatin1String("screen_name")].toString(); } d->friendsCursor = nextCursor; } else { QString err = i18n("Retrieving the friends list failed. The data returned from the server is corrupted."); qCDebug(CHOQOK) << "JSON parse error:the buffer is: \n" << buffer; Q_EMIT error(theAccount, ParsingError, err, Critical); } + + return list; +} + +QStringList TwitterApiMicroBlog::readFollowersScreenName(Choqok::Account *theAccount, + const QByteArray &buffer) +{ + QStringList list; + const QJsonDocument json = QJsonDocument::fromJson(buffer); + if (!json.isNull()) { + const QVariantMap map = json.toVariant().toMap(); + QVariantList jsonList = map[QLatin1String("users")].toList(); + QString nextCursor = map[QLatin1String("next_cursor_str")].toString(); + + if (nextCursor.isEmpty()) { + nextCursor = QLatin1String("0"); // we probably ran the rate limit; stop bugging the server already + } + + Q_FOREACH (const QVariant &user, jsonList) { + list << user.toMap()[QLatin1String("screen_name")].toString(); + } + + d->followersCursor = nextCursor; + } else { + QString err = i18n("Retrieving the followers list failed. The data returned from the server is corrupted."); + qCDebug(CHOQOK) << "JSON parse error:the buffer is: \n" << buffer; + Q_EMIT error(theAccount, ParsingError, err, Critical); + } + return list; } Choqok::User TwitterApiMicroBlog::readUser(Choqok::Account *theAccount, const QVariantMap &map) { Q_UNUSED(theAccount); Choqok::User u; u.description = map[QLatin1String("description")].toString(); u.followersCount = map[QLatin1String("followers_count")].toUInt(); u.homePageUrl = map[QLatin1String("url")].toString(); u.isProtected = map[QLatin1String("protected")].toBool(); u.location = map[QLatin1String("location")].toString(); u.profileImageUrl = map[QLatin1String("profile_image_url")].toString(); u.realName = map[QLatin1String("name")].toString(); u.userId = map[QLatin1String("id_str")].toString(); u.userName = map[QLatin1String("screen_name")].toString(); return u; } diff --git a/helperlibs/twitterapihelper/twitterapimicroblog.h b/helperlibs/twitterapihelper/twitterapimicroblog.h index dd3848b8..b2536e73 100644 --- a/helperlibs/twitterapihelper/twitterapimicroblog.h +++ b/helperlibs/twitterapihelper/twitterapimicroblog.h @@ -1,249 +1,258 @@ /* This file is part of Choqok, the KDE micro-blogging client 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/ */ #ifndef TWITTERAPIMICROBLOG_H #define TWITTERAPIMICROBLOG_H #include #include #include #include "microblog.h" #include "twitterapisearch.h" class TwitterApiSearchTimelineWidget; class TwitterApiAccount; class KJob; /** @author Mehrdad Momeny \ */ class CHOQOK_HELPER_EXPORT TwitterApiMicroBlog : public Choqok::MicroBlog { Q_OBJECT public: ~TwitterApiMicroBlog(); virtual QMenu *createActionsMenu(Choqok::Account *theAccount, QWidget *parent = Choqok::UI::Global::mainWindow()); virtual QList< Choqok::Post * > loadTimeline(Choqok::Account *accountAlias, const QString &timelineName); virtual void saveTimeline(Choqok::Account *account, const QString &timelineName, const QList< Choqok::UI::PostWidget * > &timeline); virtual Choqok::UI::ComposerWidget *createComposerWidget(Choqok::Account *account, QWidget *parent); /** \brief Create a new post @see postCreated() @see abortCreatePost() */ virtual void createPost(Choqok::Account *theAccount, Choqok::Post *post); /** \brief Abort all requests! */ virtual void abortAllJobs(Choqok::Account *theAccount); /** \brief Abort all of createPost requests! */ virtual void abortCreatePost(Choqok::Account *theAccount, Choqok::Post *post = 0); /** \brief Fetch a post @see postFetched() */ virtual void fetchPost(Choqok::Account *theAccount, Choqok::Post *post); /** \brief Remove a post @see postRemoved() */ virtual void removePost(Choqok::Account *theAccount, Choqok::Post *post); /** \brief Repeat/Retweet a post using the API */ virtual void repeatPost(Choqok::Account *theAccount, const QString &postId); /** Request to update all timelines of account! They will arrive in several signals! with timelineDataReceived() signal! @see timelineDataReceived() */ virtual void updateTimelines(Choqok::Account *theAccount); /** add post with Id @p postId to @p theAccount favorite list */ virtual void createFavorite(Choqok::Account *theAccount, const QString &postId); /** remove post with Id @p postId from @p theAccount favorite list */ virtual void removeFavorite(Choqok::Account *theAccount, const QString &postId); /** Create friendship, or Follow/Subscribe to user with username or screen name @p username i.e. Follow / Subscribe */ virtual void createFriendship(Choqok::Account *theAccount, const QString &username); /** Destroy friendship with user with username or screen name @p username i.e. Un Follow / UnSubscribe */ virtual void destroyFriendship(Choqok::Account *theAccount, const QString &username); /** Block user with username or screen name @p username */ virtual void blockUser(Choqok::Account *theAccount, const QString &username); /** * Report user as a spam with username or screen name @p username */ virtual void reportUserAsSpam(Choqok::Account *theAccount, const QString &username); virtual void aboutToUnload(); virtual void listFriendsUsername(TwitterApiAccount *theAccount, bool active = false); + virtual void listFollowersUsername(TwitterApiAccount *theAccount, bool active = false); + virtual Choqok::TimelineInfo *timelineInfo(const QString &timelineName); /** Return search backend to use for search. Should be implemented on sub classes */ virtual TwitterApiSearch *searchBackend() = 0; virtual TwitterApiSearchTimelineWidget *createSearchTimelineWidget(Choqok::Account *theAccount, QString name, const SearchInfo &info, QWidget *parent); QDateTime dateFromString(const QString &date); /** * The text to add under repeated posts, to notice user about it. */ virtual QString generateRepeatedByUserTooltip(const QString &username) = 0; /** * The question will show to confirm repeat post. */ virtual QString repeatQuestion() = 0; virtual QByteArray authorizationHeader(TwitterApiAccount *theAccount, const QUrl &requestUrl, QOAuth::HttpMethod method, QOAuth::ParamMap params = QOAuth::ParamMap()); public Q_SLOTS: /** Launch a dialog to send direct message. There are 2 ways to use this function: 1. Calling with theAccount option 2. Get called by a signal from a QAction (Microblog menu) */ virtual void showDirectMessageDialog(TwitterApiAccount *theAccount = 0, const QString &toUsername = QString()); void showSearchDialog(TwitterApiAccount *theAccount = 0); Q_SIGNALS: void favoriteCreated(Choqok::Account *theAccount, const QString &postId); void favoriteRemoved(Choqok::Account *theAccount, const QString &postId); void friendsUsernameListed(TwitterApiAccount *theAccount, const QStringList &friendsList); + void followersUsernameListed(TwitterApiAccount *theAccount, const QStringList &friendsList); void friendshipCreated(Choqok::Account *theAccount, const QString &newFriendUsername); void friendshipDestroyed(Choqok::Account *theAccount, const QString &username); void userBlocked(Choqok::Account *theAccount, const QString &blockedUsername); protected Q_SLOTS: virtual void slotCreatePost(KJob *job); virtual void slotFetchPost(KJob *job); virtual void slotRemovePost(KJob *job); virtual void slotCreateFavorite(KJob *job); virtual void slotRemoveFavorite(KJob *job); virtual void slotRequestTimeline(KJob *job); virtual void requestFriendsScreenName(TwitterApiAccount *theAccount, bool active); void slotRequestFriendsScreenNameActive(KJob *job); void slotRequestFriendsScreenNamePassive(KJob *job); + virtual void requestFollowersScreenName(TwitterApiAccount *theAccount, bool active); + void slotRequestFollowersScreenNameActive(KJob *job); + void slotRequestFollowersScreenNamePassive(KJob *job); virtual void slotCreateFriendship(KJob *job); virtual void slotDestroyFriendship(KJob *job); virtual void slotBlockUser(KJob *job); virtual void slotReportUser(KJob *job); virtual void slotUpdateFriendsList(); protected: TwitterApiMicroBlog(const QString &componentName, QObject *parent = 0); /** Request update for @p timelineName timeline. timelineName should be a valid, previously created timeline. */ virtual void requestTimeLine(Choqok::Account *theAccount, QString timelineName, QString sincePostId, int page = 1, QString maxId = QString()); virtual void setTimelineInfos(); void setRepeatedOfInfo(Choqok::Post *post, Choqok::Post *repeatedPost); virtual Choqok::Post *readPost(Choqok::Account *theAccount, const QVariantMap &var, Choqok::Post *post); virtual Choqok::Post *readPost(Choqok::Account *theAccount, const QByteArray &buffer, Choqok::Post *post); virtual QList readTimeline(Choqok::Account *theAccount, const QByteArray &buffer); virtual Choqok::Post *readDirectMessage(Choqok::Account *theAccount, const QByteArray &buffer); virtual Choqok::Post *readDirectMessage(Choqok::Account *theAccount, const QVariantMap &var); virtual QList readDirectMessages(Choqok::Account *theAccount, const QByteArray &buffer); - virtual QStringList readUsersScreenName(Choqok::Account *theAccount, const QByteArray &buffer); + virtual QStringList readFriendsScreenName(Choqok::Account *theAccount, const QByteArray &buffer); + virtual QStringList readFollowersScreenName(Choqok::Account *theAccount, const QByteArray &buffer); virtual Choqok::User *readUserInfo(const QByteArray &buffer); virtual Choqok::User readUser(Choqok::Account *theAccount, const QVariantMap &map); /** Checks json returned from server for error, and return error string, Or an empty string if nothing found! */ virtual QString checkForError(const QByteArray &buffer); void finishRequestFriendsScreenName(KJob *job, bool active); + void finishRequestFollowersScreenName(KJob *job, bool active); ///========================================== QHash timelineApiPath;//TimelineType, path QMap mTimelineInfos; //timelineName, Info QMap mFavoriteMap; //Job, postId QMap mRemovePostMap; QMap mCreatePostMap; //Job, post QMap mFetchPostMap; QMap mRequestTimelineMap; //Job, TimelineType QHash< Choqok::Account *, QMap > mTimelineLatestId; //TimelineType, LatestId QMap mJobsAccount; QMap mFriendshipMap; QString format; QStringList friendsList; + QStringList followersList; private: class Private; Private *const d; }; #endif diff --git a/microblogs/laconica/laconicamicroblog.cpp b/microblogs/laconica/laconicamicroblog.cpp index 3a69114f..69993346 100644 --- a/microblogs/laconica/laconicamicroblog.cpp +++ b/microblogs/laconica/laconicamicroblog.cpp @@ -1,449 +1,449 @@ /* This file is part of Choqok, the KDE micro-blogging client 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 "laconicamicroblog.h" #include #include #include #include #include #include #include #include #include "account.h" #include "accountmanager.h" #include "choqokappearancesettings.h" #include "composerwidget.h" #include "editaccountwidget.h" #include "mediamanager.h" #include "microblogwidget.h" #include "postwidget.h" #include "timelinewidget.h" #include "twitterapimicroblogwidget.h" #include "twitterapipostwidget.h" #include "twitterapitimelinewidget.h" #include "laconicaaccount.h" #include "laconicacomposerwidget.h" #include "laconicadebug.h" #include "laconicadmessagedialog.h" #include "laconicaeditaccount.h" #include "laconicapostwidget.h" #include "laconicasearch.h" K_PLUGIN_FACTORY_WITH_JSON(LaconicaFactory, "choqok_laconica.json", registerPlugin < LaconicaMicroBlog > ();) LaconicaMicroBlog::LaconicaMicroBlog(QObject *parent, const QVariantList &) : TwitterApiMicroBlog(QLatin1String("choqok_laconica"), parent), friendsPage(1) { qCDebug(CHOQOK); setServiceName(QLatin1String("GNU social")); mTimelineInfos[QLatin1String("ReTweets")]->name = i18nc("Timeline name", "Repeated"); mTimelineInfos[QLatin1String("ReTweets")]->description = i18nc("Timeline description", "Your posts that were repeated by others"); } LaconicaMicroBlog::~LaconicaMicroBlog() { qCDebug(CHOQOK); } Choqok::Account *LaconicaMicroBlog::createNewAccount(const QString &alias) { LaconicaAccount *acc = qobject_cast(Choqok::AccountManager::self()->findAccount(alias)); if (!acc) { return new LaconicaAccount(this, alias); } else { return 0; } } ChoqokEditAccountWidget *LaconicaMicroBlog::createEditAccountWidget(Choqok::Account *account, QWidget *parent) { qCDebug(CHOQOK); LaconicaAccount *acc = qobject_cast(account); if (acc || !account) { return new LaconicaEditAccountWidget(this, acc, parent); } else { qCDebug(CHOQOK) << "Account passed here is not a LaconicaAccount!"; return 0L; } } Choqok::UI::MicroBlogWidget *LaconicaMicroBlog::createMicroBlogWidget(Choqok::Account *account, QWidget *parent) { return new TwitterApiMicroBlogWidget(account, parent); } Choqok::UI::TimelineWidget *LaconicaMicroBlog::createTimelineWidget(Choqok::Account *account, const QString &timelineName, QWidget *parent) { return new TwitterApiTimelineWidget(account, timelineName, parent); } Choqok::UI::PostWidget *LaconicaMicroBlog::createPostWidget(Choqok::Account *account, Choqok::Post *post, QWidget *parent) { return new LaconicaPostWidget(account, post, parent); } Choqok::UI::ComposerWidget *LaconicaMicroBlog::createComposerWidget(Choqok::Account *account, QWidget *parent) { return new LaconicaComposerWidget(account, parent); } Choqok::Post *LaconicaMicroBlog::readPost(Choqok::Account *account, const QVariantMap &var, Choqok::Post *post) { if (!post) { qCCritical(CHOQOK) << "post is NULL!"; return 0; } post = TwitterApiMicroBlog::readPost(account, var, post); post->link = var[QLatin1String("external_url")].toString(); return post; } QString LaconicaMicroBlog::profileUrl(Choqok::Account *account, const QString &username) const { if (username.contains(QLatin1Char('@'))) { const QStringList lst = username.split(QLatin1Char('@'), QString::SkipEmptyParts); if (lst.count() == 2) { return QStringLiteral("https://%1/%2").arg(lst[1]).arg(lst[0]); } } TwitterApiAccount *acc = qobject_cast(account); if (acc) { return QString(acc->homepageUrl().toDisplayString() + QLatin1Char('/') + username) ; } else { return QString(); } } QString LaconicaMicroBlog::postUrl(Choqok::Account *account, const QString &username, const QString &postId) const { Q_UNUSED(username) TwitterApiAccount *acc = qobject_cast(account); if (acc) { QUrl url(acc->homepageUrl()); url.setPath(url.path() + QStringLiteral("/notice/%1").arg(postId)); return url.toDisplayString(); } else { return QString(); } } TwitterApiSearch *LaconicaMicroBlog::searchBackend() { if (!mSearchBackend) { mSearchBackend = new LaconicaSearch(this); } return mSearchBackend; } void LaconicaMicroBlog::createPostWithAttachment(Choqok::Account *theAccount, Choqok::Post *post, const QString &mediumToAttach) { if (mediumToAttach.isEmpty()) { TwitterApiMicroBlog::createPost(theAccount, post); } else { const QUrl picUrl = QUrl::fromUserInput(mediumToAttach); KIO::StoredTransferJob *picJob = KIO::storedGet(picUrl, KIO::Reload, KIO::HideProgressInfo); picJob->exec(); if (picJob->error()) { qCCritical(CHOQOK) << "Job error:" << picJob->errorString(); KMessageBox::detailedError(Choqok::UI::Global::mainWindow(), i18n("Uploading medium failed: cannot read the medium file."), picJob->errorString()); return; } const QByteArray picData = picJob->data(); if (picData.count() == 0) { qCCritical(CHOQOK) << "Cannot read the media file, please check if it exists."; KMessageBox::error(Choqok::UI::Global::mainWindow(), i18n("Uploading medium failed: cannot read the medium file.")); return; } ///Documentation: http://identi.ca/notice/17779990 TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/statuses/update.%1").arg(format)); const QMimeDatabase db; QByteArray fileContentType = db.mimeTypeForUrl(picUrl).name().toUtf8(); QMap formdata; formdata[QLatin1String("status")] = post->content.toUtf8(); formdata[QLatin1String("in_reply_to_status_id")] = post->replyToPostId.toLatin1(); formdata[QLatin1String("source")] = QCoreApplication::applicationName().toLatin1(); QMap mediafile; mediafile[QLatin1String("name")] = "media"; mediafile[QLatin1String("filename")] = picUrl.fileName().toUtf8(); mediafile[QLatin1String("mediumType")] = fileContentType; mediafile[QLatin1String("medium")] = picData; QList< QMap > listMediafiles; listMediafiles.append(mediafile); QByteArray data = Choqok::MediaManager::createMultipartFormData(formdata, listMediafiles); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; if (!job) { qCCritical(CHOQOK) << "Cannot create a http POST request!"; return; } job->addMetaData(QStringLiteral("content-type"), QStringLiteral("Content-Type: multipart/form-data; boundary=AaB03x")); job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, url, QOAuth::POST))); mCreatePostMap[ job ] = post; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), SLOT(slotCreatePost(KJob*))); job->start(); } } QString LaconicaMicroBlog::generateRepeatedByUserTooltip(const QString &username) { if (Choqok::AppearanceSettings::showRetweetsInChoqokWay()) { return i18n("Repeat of %1", username); } else { return i18n("Repeated by %1", username); } } QString LaconicaMicroBlog::repeatQuestion() { return i18n("Repeat this notice?"); } void LaconicaMicroBlog::listFriendsUsername(TwitterApiAccount *theAccount, bool active) { Q_UNUSED(active); friendsList.clear(); if (theAccount) { doRequestFriendsScreenName(theAccount, 1); } } -QStringList LaconicaMicroBlog::readUsersScreenName(Choqok::Account *theAccount, const QByteArray &buffer) +QStringList LaconicaMicroBlog::readFriendsScreenName(Choqok::Account *theAccount, const QByteArray &buffer) { QStringList list; const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { Q_FOREACH (const QJsonValue &u, json.array()) { const QJsonObject user = u.toObject(); if (user.contains(QStringLiteral("statusnet_profile_url"))) { list.append(user.value(QLatin1String("statusnet_profile_url")).toString()); } } } else { QString err = i18n("Retrieving the friends list failed. The data returned from the server is corrupted."); qCDebug(CHOQOK) << "JSON parse error:the buffer is: \n" << buffer; Q_EMIT error(theAccount, ParsingError, err, Critical); } return list; } void LaconicaMicroBlog::requestFriendsScreenName(TwitterApiAccount *theAccount, bool active) { Q_UNUSED(active); doRequestFriendsScreenName(theAccount, 1); } void LaconicaMicroBlog::showDirectMessageDialog(TwitterApiAccount *theAccount, const QString &toUsername) { qCDebug(CHOQOK); if (!theAccount) { QAction *act = qobject_cast(sender()); theAccount = qobject_cast(Choqok::AccountManager::self()->findAccount(act->data().toString())); } LaconicaDMessageDialog *dmsg = new LaconicaDMessageDialog(theAccount, Choqok::UI::Global::mainWindow()); if (!toUsername.isEmpty()) { dmsg->setTo(toUsername); } dmsg->show(); } void LaconicaMicroBlog::doRequestFriendsScreenName(TwitterApiAccount *theAccount, int page) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QStringLiteral("/statuses/friends.%1").arg(format)); QOAuth::ParamMap params; if (page > 1) { params.insert("page", QByteArray::number(page)); QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("page"), QString::number(page)); url.setQuery(urlQuery); } KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, url, QOAuth::GET, params))); mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotRequestFriendsScreenName(KJob*))); job->start(); } void LaconicaMicroBlog::slotRequestFriendsScreenName(KJob *job) { qCDebug(CHOQOK); TwitterApiAccount *theAccount = qobject_cast(mJobsAccount.take(job)); if (job->error()) { Q_EMIT error(theAccount, ServerError, i18n("Friends list for account %1 could not be updated:\n%2", theAccount->username(), job->errorString()), Normal); return; } KIO::StoredTransferJob *stJob = qobject_cast(job); - QStringList newList = readUsersScreenName(theAccount, stJob->data()); + QStringList newList = readFriendsScreenName(theAccount, stJob->data()); friendsList << newList; if (newList.count() == 100) { doRequestFriendsScreenName(theAccount, ++friendsPage); } else { friendsList.removeDuplicates(); theAccount->setFriendsList(friendsList); Q_EMIT friendsUsernameListed(theAccount, friendsList); } } /*QStringList LaconicaMicroBlog::readUsersScreenNameFromXml(Choqok::Account* theAccount, const QByteArray& buffer) { qCDebug(CHOQOK); QStringList list; QDomDocument document; document.setContent( buffer ); QDomElement root = document.documentElement(); if ( root.tagName() != "users" ) { QString err = checkXmlForError(buffer); if(!err.isEmpty()){ Q_EMIT error(theAccount, ServerError, err, Critical); } else { err = i18n( "Retrieving the friends list failed. The data returned from the server is corrupted." ); qCDebug(CHOQOK) << "there's no users tag in XML\t the XML is: \n" << buffer; Q_EMIT error(theAccount, ParsingError, err, Critical); list<(theAccount); QUrl url = account->apiUrl(); url.setPath(QStringLiteral("/statusnet/conversation/%1.%2").arg(conversationId).arg(format)); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + QLatin1String(authorizationHeader(account, url, QOAuth::GET))); mFetchConversationMap[ job ] = conversationId; mJobsAccount[ job ] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotFetchConversation(KJob*))); job->start(); } QString LaconicaMicroBlog::usernameFromProfileUrl(const QString &profileUrl) { // Remove the initial slash from path return QUrl(profileUrl).path().remove(0, 1); } QString LaconicaMicroBlog::hostFromProfileUrl(const QString &profileUrl) { return QUrl(profileUrl).host(); } void LaconicaMicroBlog::slotFetchConversation(KJob *job) { qCDebug(CHOQOK); if (!job) { qCWarning(CHOQOK) << "NULL Job returned"; return; } QList posts; QString conversationId = mFetchConversationMap.take(job); Choqok::Account *theAccount = mJobsAccount.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Fetching conversation failed. %1", job->errorString()), Normal); } else { KIO::StoredTransferJob *stj = qobject_cast (job); //if(format=="json"){ posts = readTimeline(theAccount, stj->data()); //} else { // posts = readTimelineFromXml ( theAccount, stj->data() ); //} if (!posts.isEmpty()) { Q_EMIT conversationFetched(theAccount, conversationId, posts); } } } #include "laconicamicroblog.moc" diff --git a/microblogs/laconica/laconicamicroblog.h b/microblogs/laconica/laconicamicroblog.h index fc1a759a..e580b891 100644 --- a/microblogs/laconica/laconicamicroblog.h +++ b/microblogs/laconica/laconicamicroblog.h @@ -1,97 +1,97 @@ /* This file is part of Choqok, the KDE micro-blogging client 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/ */ #ifndef LACONICAMICROBLOGPLUGIN_H #define LACONICAMICROBLOGPLUGIN_H #include #include "twitterapimicroblog.h" class LaconicaSearch; class ChoqokEditAccountWidget; class KJob; /** This plugin is to GNU social service. @Note GNU social was called StatusNet and Laconcia previously, So I just renamed it on UI :D @author Mehrdad Momeny \ */ class LaconicaMicroBlog : public TwitterApiMicroBlog { Q_OBJECT public: LaconicaMicroBlog(QObject *parent, const QVariantList &args); ~LaconicaMicroBlog(); virtual Choqok::Account *createNewAccount(const QString &alias); virtual ChoqokEditAccountWidget *createEditAccountWidget(Choqok::Account *account, QWidget *parent); virtual Choqok::UI::MicroBlogWidget *createMicroBlogWidget(Choqok::Account *account, QWidget *parent); virtual Choqok::UI::TimelineWidget *createTimelineWidget(Choqok::Account *account, const QString &timelineName, QWidget *parent); virtual Choqok::UI::PostWidget *createPostWidget(Choqok::Account *account, Choqok::Post *post, QWidget *parent); virtual Choqok::UI::ComposerWidget *createComposerWidget(Choqok::Account *account, QWidget *parent); virtual QString profileUrl(Choqok::Account *account, const QString &username) const; virtual QString postUrl(Choqok::Account *account, const QString &username, const QString &postId) const; virtual TwitterApiSearch *searchBackend(); virtual void createPostWithAttachment(Choqok::Account *theAccount, Choqok::Post *post, const QString &mediumToAttach = QString()); virtual QString generateRepeatedByUserTooltip(const QString &username); virtual QString repeatQuestion(); virtual void fetchConversation(Choqok::Account *theAccount, const QString &conversationId); virtual void requestFriendsScreenName(TwitterApiAccount *theAccount, bool active); virtual void showDirectMessageDialog(TwitterApiAccount *theAccount = 0, const QString &toUsername = QString()); static QString usernameFromProfileUrl(const QString &profileUrl); static QString hostFromProfileUrl(const QString &profileUrl); Q_SIGNALS: void conversationFetched(Choqok::Account *theAccount, const QString &conversationId, QList posts); protected: using TwitterApiMicroBlog::readPost; virtual Choqok::Post *readPost(Choqok::Account *account, const QVariantMap &var, Choqok::Post *post); virtual void listFriendsUsername(TwitterApiAccount *theAccount, bool active = false); - virtual QStringList readUsersScreenName(Choqok::Account *theAccount, const QByteArray &buffer); + virtual QStringList readFriendsScreenName(Choqok::Account *theAccount, const QByteArray &buffer); protected Q_SLOTS: virtual void slotFetchConversation(KJob *job); void slotRequestFriendsScreenName(KJob *job); private: void doRequestFriendsScreenName(TwitterApiAccount *theAccount, int page); QMap mFetchConversationMap; QPointer mSearchBackend; int friendsPage; }; #endif