Changeset View
Changeset View
Standalone View
Standalone View
src/kaboutapplicationpersonmodel_p.cpp
Show All 11 Lines | 1 | /* This file is part of the KDE libraries | |||
---|---|---|---|---|---|
12 | Lesser General Public License for more details. | 12 | Lesser General Public License for more details. | ||
13 | 13 | | |||
14 | You should have received a copy of the GNU Lesser General Public | 14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library. If not, see <http://www.gnu.org/licenses/>. | 15 | License along with this library. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | 16 | */ | ||
17 | 17 | | |||
18 | #include "kaboutapplicationpersonmodel_p.h" | 18 | #include "kaboutapplicationpersonmodel_p.h" | ||
19 | #include "debug.h" | 19 | #include "debug.h" | ||
20 | #if HAVE_ATTICA | | |||
21 | #include <attica/person.h> | | |||
22 | #endif //HAVE_ATTICA | | |||
23 | 20 | | |||
24 | #include <kaboutdata.h> | 21 | #include <kaboutdata.h> | ||
25 | #include <klocalizedstring.h> | | |||
26 | | ||||
27 | #include <QNetworkAccessManager> | | |||
28 | #include <QNetworkRequest> | | |||
29 | | ||||
30 | 22 | | |||
31 | namespace KDEPrivate | 23 | namespace KDEPrivate | ||
32 | { | 24 | { | ||
33 | 25 | | |||
34 | KAboutApplicationPersonModel::KAboutApplicationPersonModel(const QList< KAboutPerson > &personList, | 26 | KAboutApplicationPersonModel::KAboutApplicationPersonModel(const QList< KAboutPerson > &personList, | ||
35 | const QString &providerUrl, | | |||
36 | QObject *parent) | 27 | QObject *parent) | ||
37 | : QAbstractListModel(parent) | 28 | : QAbstractListModel(parent) | ||
38 | , m_personList(personList) | 29 | , m_personList(personList) | ||
39 | , m_hasAvatarPixmaps(false) | | |||
40 | , m_providerUrl(providerUrl) | | |||
41 | { | 30 | { | ||
42 | if (m_providerUrl.isEmpty()) { | | |||
43 | m_providerUrl = QStringLiteral("https://api.kde-look.org/ocs/v1/"); | | |||
44 | } | | |||
45 | | ||||
46 | bool hasOcsUsernames = false; | | |||
47 | for (const auto &person : qAsConst(m_personList)) { | 31 | for (const auto &person : qAsConst(m_personList)) { | ||
48 | if (!person.ocsUsername().isEmpty()) { | | |||
49 | hasOcsUsernames = true; | | |||
50 | } | | |||
51 | | ||||
52 | KAboutApplicationPersonProfile profile = | 32 | KAboutApplicationPersonProfile profile = | ||
53 | KAboutApplicationPersonProfile(person.name(), | 33 | KAboutApplicationPersonProfile(person.name(), | ||
54 | person.task(), | 34 | person.task(), | ||
55 | person.emailAddress(), | 35 | person.emailAddress()); | ||
56 | person.ocsUsername()); | | |||
57 | profile.setHomepage(QUrl(person.webAddress())); | 36 | profile.setHomepage(QUrl(person.webAddress())); | ||
58 | m_profileList.append(profile); | 37 | m_profileList.append(profile); | ||
59 | } | 38 | } | ||
60 | | ||||
61 | m_ocsLinkIcons.insert(KAboutApplicationPersonProfileOcsLink::Other, QIcon::fromTheme(QStringLiteral("internet-services")).pixmap(16)); | | |||
62 | m_ocsLinkIcons.insert(KAboutApplicationPersonProfileOcsLink::Blog, QIcon::fromTheme(QStringLiteral("internet-services")).pixmap(16)); | | |||
63 | m_ocsLinkIcons.insert(KAboutApplicationPersonProfileOcsLink::Homepage, QIcon::fromTheme(QStringLiteral("internet-servicesinternet-services")).pixmap(16)); | | |||
64 | | ||||
65 | #if HAVE_ATTICA | | |||
66 | connect(&m_providerManager, SIGNAL(defaultProvidersLoaded()), | | |||
67 | SLOT(onProvidersLoaded())); | | |||
68 | if (hasOcsUsernames) { | | |||
69 | m_providerManager.loadDefaultProviders(); | | |||
70 | } | | |||
71 | #endif //HAVE_ATTICA | | |||
72 | } | 39 | } | ||
73 | 40 | | |||
74 | int KAboutApplicationPersonModel::rowCount(const QModelIndex &parent) const | 41 | int KAboutApplicationPersonModel::rowCount(const QModelIndex &parent) const | ||
75 | { | 42 | { | ||
76 | Q_UNUSED(parent) | 43 | Q_UNUSED(parent) | ||
77 | return m_personList.count(); | 44 | return m_personList.count(); | ||
78 | } | 45 | } | ||
79 | 46 | | |||
Show All 20 Lines | |||||
100 | Qt::ItemFlags KAboutApplicationPersonModel::flags(const QModelIndex &index) const | 67 | Qt::ItemFlags KAboutApplicationPersonModel::flags(const QModelIndex &index) const | ||
101 | { | 68 | { | ||
102 | if (index.isValid()) { | 69 | if (index.isValid()) { | ||
103 | return Qt::ItemIsEnabled; | 70 | return Qt::ItemIsEnabled; | ||
104 | } | 71 | } | ||
105 | return QAbstractListModel::flags(index) | Qt::ItemIsEditable; | 72 | return QAbstractListModel::flags(index) | Qt::ItemIsEditable; | ||
106 | } | 73 | } | ||
107 | 74 | | |||
108 | void KAboutApplicationPersonModel::onProvidersLoaded() //SLOT | | |||
109 | { | | |||
110 | #if HAVE_ATTICA | | |||
111 | if (!m_providerManager.providers().isEmpty()) { | | |||
112 | m_provider = m_providerManager.providerByUrl(QUrl(m_providerUrl)); | | |||
113 | if (!m_provider.isValid()) { | | |||
114 | // qCDebug(DEBUG_KXMLGUI) << "OCS Provider error: could not find opendesktop.org provider."; | | |||
115 | return; | | |||
116 | } | | |||
117 | | ||||
118 | m_providerName = m_provider.name(); | | |||
119 | | ||||
120 | int i = 0; | | |||
121 | for (const auto &profile : qAsConst(m_profileList)) { | | |||
122 | if (!profile.ocsUsername().isEmpty()) { | | |||
123 | Attica::ItemJob< Attica::Person > *job = m_provider.requestPerson(profile.ocsUsername()); | | |||
124 | connect(job, SIGNAL(finished(Attica::BaseJob*)), | | |||
125 | this, SLOT(onPersonJobFinished(Attica::BaseJob*))); | | |||
126 | job->setProperty("personProfile", i); | | |||
127 | job->start(); | | |||
128 | } | | |||
129 | ++i; | | |||
130 | } | | |||
131 | } | | |||
132 | #endif //HAVE_ATTICA | | |||
133 | } | | |||
134 | | ||||
135 | void KAboutApplicationPersonModel::onPersonJobFinished(Attica::BaseJob *job) //SLOT | | |||
136 | { | | |||
137 | #if ! HAVE_ATTICA | | |||
138 | Q_UNUSED(job) | | |||
139 | #endif //HAVE_ATTICA | | |||
140 | #if HAVE_ATTICA | | |||
141 | Attica::ItemJob< Attica::Person > *personJob = | | |||
142 | static_cast< Attica::ItemJob< Attica::Person > * >(job); | | |||
143 | if (personJob->metadata().error() == Attica::Metadata::NoError) { | | |||
144 | Attica::Person p = personJob->result(); | | |||
145 | int personProfileListIndex = personJob->property("personProfile").toInt(); | | |||
146 | KAboutApplicationPersonProfile profile = m_profileList.value(personProfileListIndex); | | |||
147 | | ||||
148 | //Let's set up OCS links... | | |||
149 | QList< KAboutApplicationPersonProfileOcsLink > ocsLinks; | | |||
150 | | ||||
151 | for (int i = 2; i <= 10; ++i) { //OCS supports 10 total homepages as of 2/oct/2009 | | |||
152 | //This starts at 2 because the first homepage is | | |||
153 | //just "homepage" in the OCS API and is exposed by | | |||
154 | //Attica simply through Person::homepage() | | |||
155 | QString atticaType = p.extendedAttribute(QStringLiteral("homepagetype%1").arg(i)); | | |||
156 | QString url = p.extendedAttribute(QStringLiteral("homepage%1").arg(i)); | | |||
157 | if (url.isEmpty()) { | | |||
158 | continue; | | |||
159 | } | | |||
160 | | ||||
161 | KAboutApplicationPersonProfileOcsLink::Type type = | | |||
162 | KAboutApplicationPersonProfileOcsLink::typeFromAttica(atticaType); | | |||
163 | ocsLinks.append(KAboutApplicationPersonProfileOcsLink(type, QUrl(url))); | | |||
164 | if (!m_ocsLinkIcons.contains(type) && !m_ocsLinkIconUrls.contains(type)) { | | |||
165 | m_ocsLinkIconUrls.insert(type, p.extendedAttribute(QStringLiteral("homepageicon%1").arg(i))); | | |||
166 | } | | |||
167 | } | | |||
168 | | ||||
169 | if (profile.homepage().isEmpty()) { | | |||
170 | if (!p.homepage().isEmpty()) { | | |||
171 | profile.setHomepage(QUrl(p.homepage())); | | |||
172 | } else { | | |||
173 | if (!ocsLinks.isEmpty()) { | | |||
174 | QList< KAboutApplicationPersonProfileOcsLink >::iterator toUse = ocsLinks.begin(); | | |||
175 | for (QList< KAboutApplicationPersonProfileOcsLink >::iterator it = ocsLinks.begin(); | | |||
176 | it != ocsLinks.end(); ++it) { | | |||
177 | KAboutApplicationPersonProfileOcsLink link = *it; | | |||
178 | if (link.type() == KAboutApplicationPersonProfileOcsLink::Blog || | | |||
179 | link.type() == KAboutApplicationPersonProfileOcsLink::Homepage || | | |||
180 | link.type() == KAboutApplicationPersonProfileOcsLink::Other) { | | |||
181 | toUse = it; | | |||
182 | break; | | |||
183 | } | | |||
184 | } | | |||
185 | profile.setHomepage(toUse->url()); | | |||
186 | ocsLinks.erase(toUse); | | |||
187 | } | | |||
188 | } | | |||
189 | } else { | | |||
190 | KAboutApplicationPersonProfileOcsLink::Type type = | | |||
191 | KAboutApplicationPersonProfileOcsLink::typeFromAttica(p.extendedAttribute(QStringLiteral("homepagetype"))); | | |||
192 | ocsLinks.insert(0, KAboutApplicationPersonProfileOcsLink(type, QUrl(p.homepage()))); //we prepend the main homepage | | |||
193 | if (!m_ocsLinkIcons.contains(type) && !m_ocsLinkIconUrls.contains(type)) { | | |||
194 | m_ocsLinkIconUrls.insert(type, p.extendedAttribute(QStringLiteral("homepageicon"))); | | |||
195 | } | | |||
196 | } | | |||
197 | | ||||
198 | profile.setOcsLinks(ocsLinks); | | |||
199 | | ||||
200 | if (!(p.city().isEmpty() && p.country().isEmpty())) { | | |||
201 | if (!p.city().isEmpty()) { | | |||
202 | profile.setLocation(i18nc("City, Country", "%1, %2", p.city(), p.country())); | | |||
203 | } else { | | |||
204 | profile.setLocation(p.country()); | | |||
205 | } | | |||
206 | } | | |||
207 | | ||||
208 | profile.setOcsProfileUrl(p.extendedAttribute(QStringLiteral("profilepage"))); | | |||
209 | | ||||
210 | m_profileList.replace(personProfileListIndex, profile); | | |||
211 | | ||||
212 | if (p.avatarUrl().isEmpty()) { | | |||
213 | emit dataChanged(index(personProfileListIndex), index(personProfileListIndex)); | | |||
214 | fetchOcsLinkIcons(personProfileListIndex); | | |||
215 | } else { | | |||
216 | //TODO: Create a PixmapFromUrlJob in Attica which would use KIO::get if available | | |||
217 | // and QNAM otherwise. - Teo 30/10/2010 | | |||
218 | QNetworkAccessManager *manager = new QNetworkAccessManager(this); | | |||
219 | connect(manager, SIGNAL(finished(QNetworkReply*)), | | |||
220 | this, SLOT(onAvatarJobFinished(QNetworkReply*))); | | |||
221 | | ||||
222 | manager->get(QNetworkRequest(p.avatarUrl())); | | |||
223 | manager->setProperty("personProfile", personProfileListIndex); | | |||
224 | } | | |||
225 | } | | |||
226 | //else qCDebug(DEBUG_KXMLGUI) << "Could not fetch OCS person info."; | | |||
227 | #endif //HAVE_ATTICA | | |||
228 | } | | |||
229 | | ||||
230 | void KAboutApplicationPersonModel::onAvatarJobFinished(QNetworkReply *reply) //SLOT | | |||
231 | { | | |||
232 | #if ! HAVE_ATTICA | | |||
233 | Q_UNUSED(reply) | | |||
234 | #endif //HAVE_ATTICA | | |||
235 | #if HAVE_ATTICA | | |||
236 | QNetworkAccessManager *manager = reply->manager(); | | |||
237 | int personProfileListIndex = manager->property("personProfile").toInt(); | | |||
238 | | ||||
239 | if (reply->error() != QNetworkReply::NoError) { | | |||
240 | //qCDebug(DEBUG_KXMLGUI) << "Could not fetch OCS person avatar."; | | |||
241 | emit dataChanged(index(personProfileListIndex), index(personProfileListIndex)); | | |||
242 | return; | | |||
243 | } | | |||
244 | QByteArray data = reply->readAll(); | | |||
245 | QPixmap pixmap; | | |||
246 | pixmap.loadFromData(data); | | |||
247 | | ||||
248 | KAboutApplicationPersonProfile profile = m_profileList.value(personProfileListIndex); | | |||
249 | if (!pixmap.isNull()) { | | |||
250 | profile.setAvatar(pixmap); | | |||
251 | m_hasAvatarPixmaps = true; | | |||
252 | } | | |||
253 | | ||||
254 | m_profileList.replace(personProfileListIndex, profile); | | |||
255 | | ||||
256 | reply->deleteLater(); | | |||
257 | | ||||
258 | fetchOcsLinkIcons(personProfileListIndex); | | |||
259 | #endif //HAVE_ATTICA | | |||
260 | } | | |||
261 | | ||||
262 | void KAboutApplicationPersonModel::fetchOcsLinkIcons(int personProfileListIndex) | | |||
263 | { | | |||
264 | KAboutApplicationPersonIconsJob *job = | | |||
265 | new KAboutApplicationPersonIconsJob(this, personProfileListIndex); | | |||
266 | connect(job, SIGNAL(finished(KAboutApplicationPersonIconsJob*)), | | |||
267 | this, SLOT(onOcsLinksJobFinished(KAboutApplicationPersonIconsJob*))); | | |||
268 | job->start(); | | |||
269 | } | | |||
270 | | ||||
271 | void KAboutApplicationPersonModel::onOcsLinksJobFinished(KAboutApplicationPersonIconsJob *job) //SLOT | | |||
272 | { | | |||
273 | int personProfileListIndex = job->personProfileListIndex(); | | |||
274 | KAboutApplicationPersonProfile profile = m_profileList.value(personProfileListIndex); | | |||
275 | | ||||
276 | profile.setOcsLinks(job->ocsLinks()); | | |||
277 | | ||||
278 | m_profileList.replace(personProfileListIndex, profile); | | |||
279 | | ||||
280 | emit dataChanged(index(personProfileListIndex), index(personProfileListIndex)); | | |||
281 | emit layoutChanged(); | | |||
282 | } | | |||
283 | | ||||
284 | KAboutApplicationPersonProfileOcsLink::Type KAboutApplicationPersonProfileOcsLink::typeFromAttica(const QString &atticaType) | | |||
285 | { | | |||
286 | int index = -1; | | |||
287 | for (int i = 0; i < NUM_ATTICA_LINK_TYPES; ++i) { | | |||
288 | if (atticaType == QLatin1String(s_personOcsLinkAtticaTypes[i])) { | | |||
289 | index = i; | | |||
290 | break; | | |||
291 | } | | |||
292 | } | | |||
293 | if (index == -1) { | | |||
294 | return Other; | | |||
295 | } else { | | |||
296 | return static_cast< Type >(index); | | |||
297 | } | | |||
298 | } | | |||
299 | | ||||
300 | QString KAboutApplicationPersonProfileOcsLink::prettyType() const | | |||
301 | { | | |||
302 | //This can't be static like the other lists because of i18n | | |||
303 | switch (m_type) { | | |||
304 | case Other: | | |||
305 | return i18nc("A generic social network or homepage link of an unlisted type.", "Other"); | | |||
306 | case Blog: | | |||
307 | return i18nc("A type of link.", "Blog"); | | |||
308 | case Delicious: | | |||
309 | return QStringLiteral("Delicious"); | | |||
310 | case Digg: | | |||
311 | return QStringLiteral("Digg"); | | |||
312 | case Facebook: | | |||
313 | return QStringLiteral("Facebook"); | | |||
314 | case Homepage: | | |||
315 | return i18nc("A type of link.", "Homepage"); | | |||
316 | case Identica: | | |||
317 | return QStringLiteral("Identi.ca"); | | |||
318 | case LibreFm: | | |||
319 | return QStringLiteral("Libre.fm"); | | |||
320 | case LinkedIn: | | |||
321 | return QStringLiteral("LinkedIn"); | | |||
322 | case MySpace: | | |||
323 | return QStringLiteral("MySpace"); | | |||
324 | case Reddit: | | |||
325 | return QStringLiteral("Reddit"); | | |||
326 | case StackOverflow: | | |||
327 | return QStringLiteral("Stack Overflow"); | | |||
328 | case Twitter: | | |||
329 | return QStringLiteral("Twitter"); | | |||
330 | case Wikipedia: | | |||
331 | return QStringLiteral("Wikipedia"); | | |||
332 | case Xing: | | |||
333 | return QStringLiteral("Xing"); | | |||
334 | case YouTube: | | |||
335 | return QStringLiteral("YouTube"); | | |||
336 | case NUM_ATTICA_LINK_TYPES: // silence compiler warning | | |||
337 | break; | | |||
338 | } | | |||
339 | return QString(); | | |||
340 | } | | |||
341 | | ||||
342 | KAboutApplicationPersonIconsJob::KAboutApplicationPersonIconsJob(KAboutApplicationPersonModel *model, | | |||
343 | int personProfileListIndex) | | |||
344 | : QObject(model) | | |||
345 | , m_personProfileListIndex(personProfileListIndex) | | |||
346 | , m_model(model) | | |||
347 | { | | |||
348 | m_manager = new QNetworkAccessManager(this); | | |||
349 | connect(m_manager, SIGNAL(finished(QNetworkReply*)), | | |||
350 | this, SLOT(onJobFinished(QNetworkReply*))); | | |||
351 | | ||||
352 | m_ocsLinks = model->m_profileList.value(personProfileListIndex).ocsLinks(); | | |||
353 | } | | |||
354 | | ||||
355 | void KAboutApplicationPersonIconsJob::start() | | |||
356 | { | | |||
357 | getIcons(0); | | |||
358 | } | | |||
359 | | ||||
360 | void KAboutApplicationPersonIconsJob::getIcons(int i) | | |||
361 | { | | |||
362 | for (auto &ocsLink : m_ocsLinks) { | | |||
363 | if (m_model->m_ocsLinkIcons.contains(ocsLink.type())) { | | |||
364 | ocsLink.setIcon(m_model->m_ocsLinkIcons.value(ocsLink.type())); | | |||
365 | } else if (m_model->m_ocsLinkIconUrls.contains(ocsLink.type())) { | | |||
366 | QNetworkReply *reply = | | |||
367 | m_manager->get(QNetworkRequest(QUrl(m_model->m_ocsLinkIconUrls.value(ocsLink.type())))); | | |||
368 | reply->setProperty("linkIndex", i); | | |||
369 | return; | | |||
370 | } | | |||
371 | ++i; | | |||
372 | } | | |||
373 | emit finished(this); | | |||
374 | } | | |||
375 | | ||||
376 | void KAboutApplicationPersonIconsJob::onJobFinished(QNetworkReply *reply) //SLOT | | |||
377 | { | | |||
378 | int i = reply->property("linkIndex").toInt(); | | |||
379 | KAboutApplicationPersonProfileOcsLink::Type type = m_ocsLinks.at(i).type(); | | |||
380 | | ||||
381 | if (reply->error() != QNetworkReply::NoError) { | | |||
382 | //qCDebug(DEBUG_KXMLGUI) << "Could not fetch OCS link icon."; | | |||
383 | reply->deleteLater(); | | |||
384 | getIcons(i + 1); | | |||
385 | return; | | |||
386 | } | | |||
387 | QByteArray data = reply->readAll(); | | |||
388 | QPixmap pixmap; | | |||
389 | pixmap.loadFromData(data); | | |||
390 | | ||||
391 | if (!pixmap.isNull() && !m_model->m_ocsLinkIcons.contains(type)) { | | |||
392 | m_model->m_ocsLinkIcons.insert(type, pixmap); | | |||
393 | } | | |||
394 | reply->deleteLater(); | | |||
395 | getIcons(i); | | |||
396 | } | | |||
397 | | ||||
398 | } //namespace KDEPrivate | 75 | } //namespace KDEPrivate | ||
399 | 76 | |