File indexing completed on 2024-05-05 04:57:28

0001 /*
0002     This file is part of Choqok, the KDE micro-blogging client
0003 
0004     SPDX-FileCopyrightText: 2008-2012 Mehrdad Momeny <mehrdad.momeny@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #include "twittermicroblog.h"
0010 
0011 #include <QAction>
0012 #include <QJsonDocument>
0013 #include <QMenu>
0014 #include <QMimeDatabase>
0015 
0016 #include <KIO/StoredTransferJob>
0017 #include <KLocalizedString>
0018 #include <KMessageBox>
0019 #include <KPluginFactory>
0020 
0021 #include "account.h"
0022 #include "accountmanager.h"
0023 #include "choqokappearancesettings.h"
0024 #include "choqokbehaviorsettings.h"
0025 #include "choqoktypes.h"
0026 #include "composerwidget.h"
0027 #include "editaccountwidget.h"
0028 #include "mediamanager.h"
0029 #include "postwidget.h"
0030 #include "timelinewidget.h"
0031 
0032 #include "twitterapimicroblogwidget.h"
0033 
0034 #include "twitteraccount.h"
0035 #include "twittercomposerwidget.h"
0036 #include "twitterdebug.h"
0037 #include "twitterdmessagedialog.h"
0038 #include "twittereditaccount.h"
0039 #include "twitterlistdialog.h"
0040 #include "twitterpostwidget.h"
0041 #include "twittersearch.h"
0042 #include "twittertimelinewidget.h"
0043 
0044 K_PLUGIN_CLASS_WITH_JSON(TwitterMicroBlog, "choqok_twitter.json")
0045 
0046 TwitterMicroBlog::TwitterMicroBlog(QObject *parent, const QVariantList &)
0047     : TwitterApiMicroBlog(QLatin1String("choqok_twitter"), parent)
0048 {
0049     qCDebug(CHOQOK);
0050     setServiceName(QLatin1String("Twitter"));
0051     setServiceHomepageUrl(QLatin1String("https://twitter.com/"));
0052     timelineApiPath[QLatin1String("Reply")] = QLatin1String("/statuses/mentions_timeline.json");
0053     setTimelineInfos();
0054 }
0055 void TwitterMicroBlog::setTimelineInfos()
0056 {
0057 //   hange description of replies to mentions
0058     Choqok::TimelineInfo *t = mTimelineInfos[QLatin1String("Reply")];
0059     t->name = i18nc("Timeline Name", "Mentions");
0060     t->description = i18nc("Timeline description", "Mentions of you");
0061 }
0062 
0063 TwitterMicroBlog::~TwitterMicroBlog()
0064 {
0065     qCDebug(CHOQOK);
0066 }
0067 
0068 Choqok::Account *TwitterMicroBlog::createNewAccount(const QString &alias)
0069 {
0070     TwitterAccount *acc = qobject_cast<TwitterAccount *>(Choqok::AccountManager::self()->findAccount(alias));
0071     if (!acc) {
0072         return new TwitterAccount(this, alias);
0073     } else {
0074         return nullptr;
0075     }
0076 }
0077 
0078 ChoqokEditAccountWidget *TwitterMicroBlog::createEditAccountWidget(Choqok::Account *account, QWidget *parent)
0079 {
0080     qCDebug(CHOQOK);
0081     TwitterAccount *acc = qobject_cast<TwitterAccount *>(account);
0082     if (acc || !account) {
0083         return new TwitterEditAccountWidget(this, acc, parent);
0084     } else {
0085         qCDebug(CHOQOK) << "Account passed here is not a TwitterAccount!";
0086         return nullptr;
0087     }
0088 }
0089 
0090 Choqok::UI::MicroBlogWidget *TwitterMicroBlog::createMicroBlogWidget(Choqok::Account *account, QWidget *parent)
0091 {
0092     return new TwitterApiMicroBlogWidget(account, parent);
0093 }
0094 
0095 Choqok::UI::TimelineWidget *TwitterMicroBlog::createTimelineWidget(Choqok::Account *account,
0096         const QString &timelineName, QWidget *parent)
0097 {
0098     return new TwitterTimelineWidget(account, timelineName, parent);
0099 }
0100 
0101 Choqok::UI::PostWidget *TwitterMicroBlog::createPostWidget(Choqok::Account *account,
0102         Choqok::Post *post, QWidget *parent)
0103 {
0104     return new TwitterPostWidget(account, post, parent);
0105 }
0106 
0107 Choqok::UI::ComposerWidget *TwitterMicroBlog::createComposerWidget(Choqok::Account *account, QWidget *parent)
0108 {
0109     return new TwitterComposerWidget(account, parent);
0110 }
0111 
0112 QUrl TwitterMicroBlog::profileUrl(Choqok::Account *account, const QString &username) const
0113 {
0114     Q_UNUSED(account)
0115     return QUrl::fromUserInput(QStringLiteral("https://twitter.com/%1").arg(username));
0116 }
0117 
0118 QUrl TwitterMicroBlog::postUrl(Choqok::Account *, const QString &username,
0119                                   const QString &postId) const
0120 {
0121     return QUrl::fromUserInput(QStringLiteral("https://twitter.com/%1/status/%2").arg(username).arg(postId));
0122 }
0123 
0124 TwitterApiSearch *TwitterMicroBlog::searchBackend()
0125 {
0126     if (!mSearchBackend) {
0127         mSearchBackend = new TwitterSearch(this);
0128     }
0129     return mSearchBackend;
0130 }
0131 
0132 void TwitterMicroBlog::createPostWithAttachment(Choqok::Account *theAccount, Choqok::Post *post,
0133         const QString &mediumToAttach)
0134 {
0135     if (mediumToAttach.isEmpty()) {
0136         TwitterApiMicroBlog::createPost(theAccount, post);
0137     } else {
0138         const QUrl picUrl = QUrl::fromUserInput(mediumToAttach);
0139         KIO::StoredTransferJob *picJob = KIO::storedGet(picUrl, KIO::Reload, KIO::HideProgressInfo);
0140         picJob->exec();
0141         if (picJob->error()) {
0142             qCCritical(CHOQOK) << "Job error:" << picJob->errorString();
0143             KMessageBox::detailedError(Choqok::UI::Global::mainWindow(),
0144                                        i18n("Uploading medium failed: cannot read the medium file."),
0145                                        picJob->errorString());
0146             return;
0147         }
0148         const QByteArray picData = picJob->data();
0149         if (picData.count() == 0) {
0150             qCCritical(CHOQOK) << "Cannot read the media file, please check if it exists.";
0151             KMessageBox::error(Choqok::UI::Global::mainWindow(),
0152                                i18n("Uploading medium failed: cannot read the medium file."));
0153             return;
0154         }
0155 
0156         TwitterAccount *account = qobject_cast<TwitterAccount *>(theAccount);
0157         QUrl url = account->uploadUrl();
0158         url.setPath(url.path() + QLatin1String("/statuses/update_with_media.json"));
0159         const QMimeDatabase db;
0160         QByteArray fileContentType = db.mimeTypeForUrl(picUrl).name().toUtf8();
0161 
0162         QMap<QString, QByteArray> formdata;
0163         formdata[QLatin1String("status")] = post->content.toUtf8();
0164         if (!post->replyToPostId.isEmpty()) {
0165             formdata[QLatin1String("in_reply_to_status_id")] = post->replyToPostId.toLatin1();
0166         }
0167         formdata[QLatin1String("source")] = QCoreApplication::applicationName().toLatin1();
0168 
0169         QMap<QString, QByteArray> mediafile;
0170         mediafile[QLatin1String("name")] = "media[]";
0171         mediafile[QLatin1String("filename")] = picUrl.fileName().toUtf8();
0172         mediafile[QLatin1String("mediumType")] = fileContentType;
0173         mediafile[QLatin1String("medium")] = picData;
0174         QList< QMap<QString, QByteArray> > listMediafiles;
0175         listMediafiles.append(mediafile);
0176 
0177         QByteArray data = Choqok::MediaManager::createMultipartFormData(formdata, listMediafiles);
0178 
0179         KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ;
0180         if (!job) {
0181             qCCritical(CHOQOK) << "Cannot create a http POST request!";
0182             return;
0183         }
0184         job->addMetaData(QStringLiteral("content-type"),
0185                          QStringLiteral("Content-Type: multipart/form-data; boundary=AaB03x"));
0186         job->addMetaData(QStringLiteral("customHTTPHeader"),
0187                          QStringLiteral("Authorization: ") +
0188                          QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation)));
0189         mCreatePostMap[ job ] = post;
0190         mJobsAccount[job] = theAccount;
0191         connect(job, &KIO::StoredTransferJob::result, this, &TwitterMicroBlog::slotCreatePost);
0192         job->start();
0193     }
0194 }
0195 
0196 void TwitterMicroBlog::verifyCredentials(TwitterAccount *theAccount)
0197 {
0198     qCDebug(CHOQOK);
0199     QUrl url = theAccount->apiUrl();
0200     url.setPath(url.path() + QStringLiteral("/account/verify_credentials.json"));
0201 
0202     KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ;
0203     if (!job) {
0204         qCDebug(CHOQOK) << "Cannot create an http GET request!";
0205         return;
0206     }
0207     job->addMetaData(QStringLiteral("customHTTPHeader"),
0208                      QStringLiteral("Authorization: ") +
0209                      QLatin1String(authorizationHeader(theAccount, url, QNetworkAccessManager::GetOperation)));
0210     mJobsAccount[ job ] = theAccount;
0211     connect(job, &KIO::StoredTransferJob::result, this, &TwitterMicroBlog::slotFetchVerifyCredentials);
0212     job->start();
0213 }
0214 
0215 void TwitterMicroBlog::slotFetchVerifyCredentials(KJob *job)
0216 {
0217     if (!job) {
0218         qCWarning(CHOQOK) << "NULL Job returned";
0219         return;
0220     }
0221     TwitterAccount *theAccount = qobject_cast<TwitterAccount *>(mJobsAccount.take(job));
0222     if (job->error()) {
0223         qCDebug(CHOQOK) << "Job Error:" << job->errorString();
0224         Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError,
0225                      i18n("Verify credentials failed. %1", job->errorString()), Low);
0226     } else {
0227         KIO::StoredTransferJob *stj = qobject_cast<KIO::StoredTransferJob *> (job);
0228         const QJsonDocument json = QJsonDocument::fromJson(stj->data());
0229         if (!json.isNull()) {
0230             theAccount->setUsername(json.object()[QLatin1String("screen_name")].toString());
0231             theAccount->setUserId(json.object()[QLatin1String("id_str")].toString());
0232         }
0233     }
0234 }
0235 
0236 void TwitterMicroBlog::showDirectMessageDialog(TwitterApiAccount *theAccount, const QString &toUsername)
0237 {
0238     qCDebug(CHOQOK);
0239     if (!theAccount) {
0240         QAction *act = qobject_cast<QAction *>(sender());
0241         theAccount = qobject_cast<TwitterApiAccount *>(
0242                          Choqok::AccountManager::self()->findAccount(act->data().toString()));
0243     }
0244     TwitterDMessageDialog *dmsg = new TwitterDMessageDialog(theAccount, Choqok::UI::Global::mainWindow());
0245     if (!toUsername.isEmpty()) {
0246         dmsg->setTo(toUsername);
0247     }
0248     dmsg->show();
0249 }
0250 
0251 QString TwitterMicroBlog::generateRepeatedByUserTooltip(const QString &username)
0252 {
0253     if (Choqok::AppearanceSettings::showRetweetsInChoqokWay()) {
0254         return i18n("Retweet of %1", username);
0255     } else {
0256         return i18n("Retweeted by %1", username);
0257     }
0258 }
0259 
0260 QString TwitterMicroBlog::repeatQuestion()
0261 {
0262     return i18n("Retweet to your followers?");
0263 }
0264 
0265 QMenu *TwitterMicroBlog::createActionsMenu(Choqok::Account *theAccount, QWidget *parent)
0266 {
0267     QMenu *menu = TwitterApiMicroBlog::createActionsMenu(theAccount, parent);
0268 
0269     QAction *lists = new QAction(i18n("Add User List..."), menu);
0270     lists->setData(theAccount->alias());
0271     connect(lists, SIGNAL(triggered(bool)), SLOT(showListDialog()));
0272     menu->addAction(lists);
0273 
0274     return menu;
0275 }
0276 
0277 void TwitterMicroBlog::showListDialog(TwitterApiAccount *theAccount)
0278 {
0279     if (!theAccount) {
0280         QAction *act = qobject_cast<QAction *>(sender());
0281         theAccount = qobject_cast<TwitterApiAccount *>(
0282                          Choqok::AccountManager::self()->findAccount(act->data().toString()));
0283     }
0284     QPointer<TwitterListDialog> listDlg = new TwitterListDialog(theAccount,
0285             Choqok::UI::Global::mainWindow());
0286     listDlg->show();
0287 }
0288 
0289 void TwitterMicroBlog::fetchUserLists(TwitterAccount *theAccount, const QString &username)
0290 {
0291     qCDebug(CHOQOK);
0292     if (!theAccount) {
0293         return;
0294     }
0295     QUrl url = theAccount->apiUrl();
0296     url.setPath(url.path() + QLatin1String("/lists/ownerships.json"));
0297 
0298     QUrlQuery urlQuery;
0299     urlQuery.addQueryItem(QLatin1String("screen_name"), username);
0300     url.setQuery(urlQuery);
0301 
0302     KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ;
0303     if (!job) {
0304         qCCritical(CHOQOK) << "TwitterMicroBlog::loadUserLists: Cannot create an http GET request!";
0305         return;
0306     }
0307 
0308     job->addMetaData(QStringLiteral("customHTTPHeader"),
0309                      QStringLiteral("Authorization: ") +
0310                      QLatin1String(authorizationHeader(theAccount, url, QNetworkAccessManager::GetOperation)));
0311     mFetchUsersListMap[ job ] = username;
0312     mJobsAccount[ job ] = theAccount;
0313     connect(job, &KIO::StoredTransferJob::result, this, &TwitterMicroBlog::slotFetchUserLists);
0314     job->start();
0315 }
0316 
0317 void TwitterMicroBlog::slotFetchUserLists(KJob *job)
0318 {
0319     qCDebug(CHOQOK);
0320     if (!job) {
0321         qCWarning(CHOQOK) << "NULL Job returned";
0322         return;
0323     }
0324     QString username = mFetchUsersListMap.take(job);
0325     Choqok::Account *theAccount = mJobsAccount.take(job);
0326     if (job->error()) {
0327         qCDebug(CHOQOK) << "Job Error:" << job->errorString();
0328         Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError,
0329                      i18n("Fetching %1's lists failed. %2", username, job->errorString()), Critical);
0330     } else {
0331         KIO::StoredTransferJob *stj = qobject_cast<KIO::StoredTransferJob *> (job);
0332         QByteArray buffer = stj->data();
0333         QList<Twitter::List> list = readUserListsFromJson(theAccount, buffer);
0334         if (list.isEmpty()) {
0335             qCDebug(CHOQOK) << buffer;
0336             QString errorMsg;
0337             errorMsg = checkForError(buffer);
0338             if (errorMsg.isEmpty()) {
0339                 KMessageBox::information(choqokMainWindow, i18n("There is no list record for user %1", username));
0340             } else {
0341                 Q_EMIT error(theAccount, ServerError, errorMsg, Critical);
0342             }
0343         } else {
0344             Q_EMIT userLists(theAccount, username, list);
0345         }
0346     }
0347 }
0348 
0349 Choqok::Post *TwitterMicroBlog::readDirectMessage(Choqok::Account *theAccount, const QVariantMap &var)
0350 {
0351     qCDebug(CHOQOK);
0352 
0353     Choqok::Post *post = TwitterApiMicroBlog::readDirectMessage(theAccount, var);
0354 
0355     if (!post) {
0356         qCCritical(CHOQOK) << "post is NULL!";
0357         return nullptr;
0358     }
0359 
0360     post->postId = var[QLatin1String("id_str")].toString();
0361 
0362     return post;
0363 }
0364 
0365 void TwitterMicroBlog::addListTimeline(TwitterAccount *theAccount, const QString &username,
0366                                        const QString &listname)
0367 {
0368     qCDebug(CHOQOK);
0369     QStringList tms = theAccount->timelineNames();
0370     QString name = QStringLiteral("@%1/%2").arg(username).arg(listname);
0371     tms.append(name);
0372     addTimelineName(name);
0373     theAccount->setTimelineNames(tms);
0374     theAccount->writeConfig();
0375     timelineApiPath[name] = QLatin1String("/lists/statuses.json");
0376     updateTimelines(theAccount);
0377 }
0378 
0379 // TODO: Change to new API
0380 void TwitterMicroBlog::setListTimelines(TwitterAccount *theAccount, const QStringList &lists)
0381 {
0382     qCDebug(CHOQOK) << lists;
0383     QStringList tms = theAccount->timelineNames();
0384     for (const QString &name: lists) {
0385         tms.append(name);
0386         addTimelineName(name);
0387         timelineApiPath[name] = QLatin1String("/lists/statuses.json");
0388     }
0389     tms.removeDuplicates();
0390     theAccount->setTimelineNames(tms);
0391 }
0392 
0393 Choqok::TimelineInfo *TwitterMicroBlog::timelineInfo(const QString &timelineName)
0394 {
0395     if (timelineName.startsWith(QLatin1Char('@'))) {
0396         if (mListsInfo.contains(timelineName)) {
0397             return mListsInfo.value(timelineName);
0398         } else {
0399             Choqok::TimelineInfo *info = new Choqok::TimelineInfo;
0400             info->description = info->name = timelineName;
0401             info->icon = QLatin1String("format-list-unordered");
0402             mListsInfo.insert(timelineName, info);
0403             return info;
0404         }
0405     } else {
0406         return TwitterApiMicroBlog::timelineInfo(timelineName);
0407     }
0408 }
0409 
0410 QList< Twitter::List > TwitterMicroBlog::readUserListsFromJson(Choqok::Account *theAccount, QByteArray buffer)
0411 {
0412     QList<Twitter::List> twitterList;
0413     const QJsonDocument json = QJsonDocument::fromJson(buffer);
0414     if (!json.isNull()) {
0415         const QVariantMap map = json.toVariant().toMap();
0416         if (map.contains(QLatin1String("lists"))) {
0417             for (const QVariant &list: map[QLatin1String("lists")].toList()) {
0418                 twitterList.append(readListFromJsonMap(theAccount, list.toMap()));
0419             }
0420         }
0421     }
0422     return twitterList;
0423 }
0424 
0425 Twitter::List TwitterMicroBlog::readListFromJsonMap(Choqok::Account *theAccount, QVariantMap map)
0426 {
0427     Twitter::List l;
0428     l.author = readUser(theAccount, map[QLatin1String("user")].toMap());
0429     l.description = map[QLatin1String("description")].toString();
0430     l.fullname = map[QLatin1String("full_name")].toString();
0431     l.isFollowing = map[QLatin1String("following")].toBool();
0432     l.listId = map[QLatin1String("id")].toString();
0433     l.memberCount = map[QLatin1String("member_count")].toInt();
0434     l.mode = (map[QLatin1String("mode")].toString() == QLatin1String("public") ? Twitter::Public : Twitter::Private);
0435     l.name = map[QLatin1String("name")].toString();
0436     l.slug = map[QLatin1String("slug")].toString();
0437     l.subscriberCount = map[QLatin1String("subscriber_count")].toInt();
0438     l.uri = map[QLatin1String("uri")].toString();
0439     return l;
0440 }
0441 
0442 Choqok::Post *TwitterMicroBlog::readPost(Choqok::Account *account, const QVariantMap &var, Choqok::Post *post)
0443 {
0444     if (!post) {
0445         qCCritical(CHOQOK) << "TwitterMicroBlog::readPost: post is NULL!";
0446         return nullptr;
0447     }
0448 
0449     post = TwitterApiMicroBlog::readPost(account, var, post);
0450 
0451     post->postId = var[QLatin1String("id_str")].toString();
0452     post->replyToPostId = var[QLatin1String("in_reply_to_status_id_str")].toString();
0453     post->replyToUser.userId = var[QLatin1String("in_reply_to_user_id_str")].toString();
0454 
0455     // Support for extended tweet_mode
0456     if (var.contains(QLatin1String("full_text")) && post->repeatedPostId.isEmpty()) {
0457         post->content = var[QLatin1String("full_text")].toString();
0458     }
0459 
0460     //postId is changed, regenerate link url
0461     if (!post->repeatedPostId.isEmpty()) {
0462         post->link = postUrl(account, post->author.userName, post->repeatedPostId);
0463     } else {
0464         post->link = postUrl(account, post->author.userName, post->postId);
0465     }
0466 
0467     QVariantMap userMap = var[QLatin1String("user")].toMap();
0468     post->author.userId = userMap[QLatin1String("id_str")].toString();
0469 
0470     return post;
0471 }
0472 
0473 void TwitterMicroBlog::fetchPost(Choqok::Account *theAccount, Choqok::Post *post)
0474 {
0475     qCDebug(CHOQOK);
0476     if (!post || post->postId.isEmpty()) {
0477         return;
0478     }
0479     TwitterAccount *account = qobject_cast<TwitterAccount *>(theAccount);
0480     QUrl url = account->apiUrl();
0481     url.setPath(url.path() + QStringLiteral("/statuses/show/%1.json").arg(post->postId));
0482 
0483     QUrlQuery urlQuery;
0484     urlQuery.addQueryItem(QLatin1String("tweet_mode"), QLatin1String("extended"));
0485     url.setQuery(urlQuery);
0486 
0487     KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ;
0488     if (!job) {
0489         qCDebug(CHOQOK) << "Cannot create an http GET request!";
0490 //         QString errMsg = i18n ( "Fetching the new post failed. Cannot create an HTTP GET request."
0491 //                                 "Please check your KDE installation." );
0492 //         emit errorPost ( theAccount, post, Choqok::MicroBlog::OtherError, errMsg, Low );
0493         return;
0494     }
0495     job->addMetaData(QStringLiteral("customHTTPHeader"),
0496                      QStringLiteral("Authorization: ") +
0497                      QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::GetOperation)));
0498     mFetchPostMap[ job ] = post;
0499     mJobsAccount[ job ] = theAccount;
0500     connect(job, &KIO::StoredTransferJob::result, this, &TwitterMicroBlog::slotFetchPost);
0501     job->start();
0502 }
0503 
0504 void TwitterMicroBlog::requestTimeLine(Choqok::Account *theAccount, QString type,
0505         QString latestStatusId, int page, QString maxId)
0506 {
0507     qCDebug(CHOQOK);
0508     TwitterAccount *account = qobject_cast<TwitterAccount *>(theAccount);
0509     QUrl url = account->apiUrl();
0510     url.setPath(url.path() + timelineApiPath[type]);
0511 
0512     QUrlQuery urlQuery;
0513     // needed because lists have different parameter names but
0514     // returned timelines have the same JSON format
0515     if (timelineApiPath[type].contains(QLatin1String("lists/statuses"))) {
0516 
0517         // type contains @username/timelinename
0518         const QString slug = type.mid(type.indexOf(QLatin1String("/")) + 1);
0519         urlQuery.addQueryItem(QLatin1String("slug"), slug);
0520 
0521         const QString owner = type.mid(1, type.indexOf(QLatin1String("/")) - 1);
0522         urlQuery.addQueryItem(QLatin1String("owner_screen_name"), owner);
0523     } else {
0524         int countOfPost = Choqok::BehaviorSettings::countOfPosts();
0525         if (!latestStatusId.isEmpty()) {
0526             urlQuery.addQueryItem(QLatin1String("since_id"), latestStatusId);
0527             countOfPost = 200;
0528         }
0529 
0530         urlQuery.addQueryItem(QLatin1String("count"), QString::number(countOfPost));
0531 
0532         if (!maxId.isEmpty()) {
0533             urlQuery.addQueryItem(QLatin1String("max_id"), maxId);
0534         }
0535 
0536         if (page) {
0537             urlQuery.addQueryItem(QLatin1String("page"), QString::number(page));
0538         }
0539     }
0540 
0541     urlQuery.addQueryItem(QLatin1String("tweet_mode"), QLatin1String("extended"));
0542 
0543     url.setQuery(urlQuery);
0544 
0545     qCDebug(CHOQOK) << "Latest" << type << "Id:" << latestStatusId;// << "apiReq:" << url;
0546 
0547     KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ;
0548     if (!job) {
0549         qCDebug(CHOQOK) << "Cannot create an http GET request!";
0550 //         QString errMsg = i18n ( "Cannot create an http GET request. Please check your KDE installation." );
0551 //         emit error ( theAccount, OtherError, errMsg, Low );
0552         return;
0553     }
0554     job->addMetaData(QStringLiteral("customHTTPHeader"),
0555                      QStringLiteral("Authorization: ")
0556                      + QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::GetOperation)));
0557     mRequestTimelineMap[job] = type;
0558     mJobsAccount[job] = theAccount;
0559     connect(job, &KIO::StoredTransferJob::result, this, &TwitterMicroBlog::slotRequestTimeline);
0560     job->start();
0561 }
0562 
0563 #include "moc_twittermicroblog.cpp"
0564 #include "twittermicroblog.moc"