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"