1 /*
2 This file is part of Choqok, the KDE micro-blogging client
3
4 Copyright (C) 2008-2012 Mehrdad Momeny <mehrdad.momeny@gmail.com>
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of
9 the License or (at your option) version 3 or any later version
10 accepted by the membership of KDE e.V. (or its successor approved
11 by the membership of KDE e.V.), which shall act as a proxy
12 defined in Section 14 of version 3 of the license.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, see http://www.gnu.org/licenses/
21
22 */
23
24 #include "twitterpostwidget.h"
25
26 #include <QAction>
27 #include <QMenu>
28 #include <QPushButton>
29
30 #include <KLocalizedString>
31
32 #include "choqokbehaviorsettings.h"
33 #include "choqoktools.h"
34 #include "mediamanager.h"
35 #include "textbrowser.h"
36
37 #include "twitterapiaccount.h"
38 #include "twitterapimicroblog.h"
39 #include "twitterapiwhoiswidget.h"
40
41 #include "twittersearch.h"
42
43 const QRegExp TwitterPostWidget::mTwitterUserRegExp(QLatin1String("([\\s\\W]|^)@([a-z0-9_]+){1,20}"), Qt::CaseInsensitive);
44 const QRegExp TwitterPostWidget::mTwitterTagRegExp(QLatin1String("([\\s]|^)#([\\w_\\.\\-]+)"), Qt::CaseInsensitive);
45 const QString TwitterPostWidget::mQuotedTextBase(QLatin1String("<div style=\"padding-top:5px;padding-bottom:3px;\"><table style=\"%4 border-style:groove;\" border=\"1\"><tr><td style=\"padding-top:4px;padding-bottom:4px;padding-left:3px;padding-right:3px;\"><table> <tr><td width=\"40\" style=\"padding-right:5px;\"><img src=\"img://quotedProfileImage\" width=\"40\" height=\"40\" /></td><td dir=\"%2\"><p><b><a>%3:</b> %1</p></td></tr></table></td></tr></table></div>"));
46 const QUrl TwitterPostWidget::mQuotedAvatarResourceUrl(QLatin1String("img://quotedProfileImage"));
TwitterPostWidget(Choqok::Account * account,Choqok::Post * post,QWidget * parent)47 TwitterPostWidget::TwitterPostWidget(Choqok::Account *account, Choqok::Post *post, QWidget *parent): TwitterApiPostWidget(account, post, parent)
48 {
49 }
50
~TwitterPostWidget()51 TwitterPostWidget::~TwitterPostWidget()
52 {
53 }
54
initUi()55 void TwitterPostWidget::initUi()
56 {
57 TwitterApiPostWidget::initUi();
58
59 if ( ! currentPost()->quotedPost.content.isEmpty() ) {
60 if( !setupQuotedAvatar() ){
61 _mainWidget->document()->addResource(QTextDocument::ImageResource, mQuotedAvatarResourceUrl,
62 Choqok::MediaManager::self()->defaultImage());
63 }
64
65 auto dir = getDirection(currentPost()->quotedPost.content);
66 auto text = prepareStatus(currentPost()->quotedPost.content);
67 QString user = QStringLiteral("<a href='user://%1'>%1</a>").arg(currentPost()->quotedPost.user.userName);
68 QString quoteText = mQuotedTextBase.arg(text, dir, user, QLatin1String("background-color:%1;"));
69 setExtraContents(quoteText.arg(getBackgroundColor()));
70 updateUi();
71 }
72
73 QPushButton *btn = buttons().value(QLatin1String("btnResend"));
74
75 if (btn) {
76 QMenu *menu = new QMenu(btn);
77 QAction *resend = new QAction(i18n("Manual ReSend"), menu);
78 connect(resend, &QAction::triggered, this, &TwitterPostWidget::slotResendPost);
79 QAction *repeat = new QAction(i18n("Retweet"), menu);
80 repeat->setToolTip(i18n("Retweet post using API"));
81 connect(repeat, &QAction::triggered, this, &TwitterPostWidget::repeatPost);
82 // If person protects their acc, we will use simple adding RT before message
83 if (!currentPost()->author.isProtected) {
84 menu->addAction(repeat);
85 }
86 menu->addAction(resend);
87 btn->setMenu(menu);
88 }
89 }
90
prepareStatus(const QString & text)91 QString TwitterPostWidget::prepareStatus(const QString &text)
92 {
93 QString res = TwitterApiPostWidget::prepareStatus(text);
94 res.replace(mTwitterUserRegExp, QLatin1String("\\1@<a href='user://\\2'>\\2</a>"));
95 res.replace(mTwitterTagRegExp, QLatin1String("\\1#<a href='tag://\\2'>\\2</a>"));
96 return res;
97 }
98
isRemoveAvailable()99 bool TwitterPostWidget::isRemoveAvailable()
100 {
101 if (currentAccount()->username().compare(currentPost()->author.userName, Qt::CaseInsensitive) == 0) {
102 return true;
103 } else if (currentPost()->isPrivate) {
104 return true;
105 } else {
106 return false;
107 }
108 }
109
slotReplyToAll()110 void TwitterPostWidget::slotReplyToAll()
111 {
112 QStringList nicks;
113 nicks.append(currentPost()->author.userName);
114
115 QString txt = QStringLiteral("@%1 ").arg(currentPost()->author.userName);
116
117 int pos = 0;
118 while ((pos = mTwitterUserRegExp.indexIn(currentPost()->content, pos)) != -1) {
119 if (mTwitterUserRegExp.cap(2).toLower() != currentAccount()->username() &&
120 mTwitterUserRegExp.cap(2).toLower() != currentPost()->author.userName &&
121 !nicks.contains(mTwitterUserRegExp.cap(2).toLower())) {
122 nicks.append(mTwitterUserRegExp.cap(2));
123 txt += QStringLiteral("@%1 ").arg(mTwitterUserRegExp.cap(2));
124 }
125 pos += mTwitterUserRegExp.matchedLength();
126 }
127
128 txt.chop(1);
129
130 Q_EMIT reply(txt, currentPost()->postId, currentPost()->author.userName);
131 }
132
checkAnchor(const QUrl & url)133 void TwitterPostWidget::checkAnchor(const QUrl &url)
134 {
135 QString scheme = url.scheme();
136 TwitterApiMicroBlog *blog = qobject_cast<TwitterApiMicroBlog *>(currentAccount()->microblog());
137 TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(currentAccount());
138 if (scheme == QLatin1String("tag")) {
139 blog->searchBackend()->requestSearchResults(currentAccount(),
140 QUrl::fromAce(url.host().toUtf8()),
141 (int)TwitterSearch::ReferenceHashtag);
142 } else if (scheme == QLatin1String("user")) {
143 QMenu menu;
144 QAction *info = new QAction(QIcon::fromTheme(QLatin1String("user-identity")), i18nc("Who is user", "Who is %1", url.host()),
145 &menu);
146 QAction *from = new QAction(QIcon::fromTheme(QLatin1String("edit-find-user")), i18nc("Posts from user", "Posts from %1", url.host()),
147 &menu);
148 QAction *to = new QAction(QIcon::fromTheme(QLatin1String("meeting-attending")), i18nc("Replies to user", "Replies to %1",
149 url.host()),
150 &menu);
151 QAction *cont = new QAction(QIcon::fromTheme(QLatin1String("user-properties")), i18nc("Including user name", "Including %1",
152 url.host()),
153 &menu);
154 QAction *openInBrowser = new QAction(QIcon::fromTheme(QLatin1String("internet-services")),
155 i18nc("Open profile page in browser",
156 "Open profile in browser"), &menu);
157 from->setData(TwitterSearch::FromUser);
158 to->setData(TwitterSearch::ToUser);
159 cont->setData(TwitterSearch::ReferenceUser);
160 menu.addAction(info);
161 menu.addAction(from);
162 menu.addAction(to);
163 menu.addAction(cont);
164 menu.addAction(openInBrowser);
165
166 //Subscribe/UnSubscribe/Block
167 bool isSubscribe = false;
168 QString accountUsername = currentAccount()->username().toLower();
169 QString postUsername = url.host().toLower();
170 QAction *subscribe = nullptr, *block = nullptr, *replyTo = nullptr, *dMessage = nullptr, *reportSpam = nullptr;
171 if (accountUsername != postUsername) {
172 menu.addSeparator();
173 QMenu *actionsMenu = menu.addMenu(QIcon::fromTheme(QLatin1String("applications-system")), i18n("Actions"));
174 replyTo = new QAction(QIcon::fromTheme(QLatin1String("edit-undo")), i18nc("Write a message to user attention", "Write to %1",
175 url.host()), actionsMenu);
176 actionsMenu->addAction(replyTo);
177 if (account->friendsList().contains(url.host(),
178 Qt::CaseInsensitive)) {
179 dMessage = new QAction(QIcon::fromTheme(QLatin1String("mail-message-new")), i18nc("Send direct message to user",
180 "Send private message to %1",
181 url.host()), actionsMenu);
182 actionsMenu->addAction(dMessage);
183 isSubscribe = false;//It's UnSubscribe
184 subscribe = new QAction(QIcon::fromTheme(QLatin1String("list-remove-user")),
185 i18nc("Unfollow user",
186 "Unfollow %1", url.host()), actionsMenu);
187 } else {
188 isSubscribe = true;
189 subscribe = new QAction(QIcon::fromTheme(QLatin1String("list-add-user")),
190 i18nc("Follow user",
191 "Follow %1", url.host()), actionsMenu);
192 }
193 block = new QAction(QIcon::fromTheme(QLatin1String("dialog-cancel")),
194 i18nc("Block user",
195 "Block %1", url.host()), actionsMenu);
196 reportSpam = new QAction(QIcon::fromTheme(QLatin1String("irc-voice")),
197 i18nc("Report user",
198 "Report %1 as spam", url.host()), actionsMenu);
199
200 actionsMenu->addAction(subscribe);
201 actionsMenu->addAction(block);
202 actionsMenu->addAction(reportSpam);
203 }
204
205 QAction *ret = menu.exec(QCursor::pos());
206 if (ret == nullptr) {
207 return;
208 }
209 if (ret == info) {
210 TwitterApiWhoisWidget *wd = new TwitterApiWhoisWidget(account, url.host(), *currentPost(), this);
211 wd->show(QCursor::pos());
212 return;
213 } else if (ret == subscribe) {
214 if (isSubscribe) {
215 blog->createFriendship(currentAccount(), url.host());
216 } else {
217 blog->destroyFriendship(currentAccount(), url.host());
218 }
219 return;
220 } else if (ret == block) {
221 blog->blockUser(currentAccount(), url.host());
222 return;
223 } else if (ret == reportSpam) {
224 blog->reportUserAsSpam(currentAccount(), url.host());
225 return;
226 } else if (ret == openInBrowser) {
227 Choqok::openUrl(currentAccount()->microblog()->profileUrl(currentAccount(), url.host()));
228 return;
229 } else if (ret == replyTo) {
230 Q_EMIT reply(QStringLiteral("@%1").arg(url.host()), QString(), url.host());
231 return;
232 } else if (ret == dMessage) {
233 blog->showDirectMessageDialog(account, url.host());
234 return;
235 }
236 int type = ret->data().toInt();
237 blog->searchBackend()->requestSearchResults(currentAccount(),
238 url.host(), type, QString(), Choqok::BehaviorSettings::countOfPosts());
239 } else {
240 TwitterApiPostWidget::checkAnchor(url);
241 }
242 }
243
setupQuotedAvatar()244 bool TwitterPostWidget::setupQuotedAvatar()
245 {
246 QPixmap pix = Choqok::MediaManager::self()->fetchImage(currentPost()->quotedPost.user.profileImageUrl,
247 Choqok::MediaManager::Async);
248 if (!pix.isNull()) {
249 quotedAvatarFetched(currentPost()->quotedPost.user.profileImageUrl, pix);
250 return true;
251 } else {
252 connect(Choqok::MediaManager::self(), &Choqok::MediaManager::imageFetched,
253 this, &TwitterPostWidget::quotedAvatarFetched);
254 connect(Choqok::MediaManager::self(), &Choqok::MediaManager::fetchError,
255 this, &TwitterPostWidget::quotedAvatarFetchError);
256 return false;
257 }
258 }
259
quotedAvatarFetched(const QUrl & remoteUrl,const QPixmap & pixmap)260 void TwitterPostWidget::quotedAvatarFetched(const QUrl &remoteUrl, const QPixmap &pixmap)
261 {
262 if (remoteUrl == currentPost()->quotedPost.user.profileImageUrl) {
263 _mainWidget->document()->addResource(QTextDocument::ImageResource, mQuotedAvatarResourceUrl, pixmap);
264 disconnect(Choqok::MediaManager::self(), &Choqok::MediaManager::imageFetched,
265 this, &TwitterPostWidget::quotedAvatarFetched);
266 disconnect(Choqok::MediaManager::self(), &Choqok::MediaManager::fetchError,
267 this, &TwitterPostWidget::quotedAvatarFetchError);
268 }
269 }
270
quotedAvatarFetchError(const QUrl & remoteUrl,const QString & errMsg)271 void TwitterPostWidget::quotedAvatarFetchError(const QUrl &remoteUrl, const QString &errMsg)
272 {
273 Q_UNUSED(errMsg);
274 if (remoteUrl == currentPost()->quotedPost.user.profileImageUrl) {
275 ///Avatar fetching is failed! but will not disconnect to get the img if it fetches later!
276 _mainWidget->document()->addResource(QTextDocument::ImageResource, mQuotedAvatarResourceUrl,
277 QIcon::fromTheme(QLatin1String("image-missing")).pixmap(40));
278 }
279 }
280
getBackgroundColor()281 QString TwitterPostWidget::getBackgroundColor()
282 {
283 QString style = styleSheet();
284 QLatin1String str{ "background-color:rgb(" };
285 int idx = style.indexOf(str);
286 if(idx != -1){
287 idx += str.size();
288 int endIdx = style.indexOf(QLatin1String(");"), idx);
289 if( endIdx != -1 ){
290 QStringList rgb = style.mid(idx, endIdx-idx).split(QLatin1Char(','));
291 if( rgb.size() == 3 ){
292 return QStringLiteral("#%1%2%3").arg( rgb[0].toInt() - 20, 2, 16, QLatin1Char('0') )
293 .arg( rgb[1].toInt() - 20, 2, 16, QLatin1Char('0') )
294 .arg( rgb[2].toInt() - 20, 2, 16, QLatin1Char('0') );
295 }
296 }
297 }
298
299 return QLatin1String("#ffffff");
300 }
301