KNewStuff

atticaprovider.cpp
1 /*
2  SPDX-FileCopyrightText: 2009-2010 Frederik Gladhorn <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.1-or-later
5 */
6 
7 #include "atticaprovider_p.h"
8 
9 #include "commentsmodel.h"
10 #include "question.h"
11 #include "tagsfilterchecker.h"
12 
13 #include <QCollator>
14 #include <knewstuffcore_debug.h>
15 #include <KLocalizedString>
16 
17 #include <attica/providermanager.h>
18 #include <attica/provider.h>
19 #include <attica/listjob.h>
20 #include <attica/content.h>
21 #include <attica/downloaditem.h>
22 #include <attica/accountbalance.h>
23 #include <attica/person.h>
24 
25 using namespace Attica;
26 
27 namespace KNSCore
28 {
29 
30 AtticaProvider::AtticaProvider(const QStringList &categories, const QString& additionalAgentInformation)
31  : mEntryJob(nullptr)
32  , mInitialized(false)
33 {
34  // init categories map with invalid categories
35  for (const QString &category : categories) {
36  mCategoryMap.insert(category, Attica::Category());
37  }
38 
39  connect(&m_providerManager, &ProviderManager::providerAdded, this, [=](const Attica::Provider &provider){
40  providerLoaded(provider);
41  m_provider.setAdditionalAgentInformation(additionalAgentInformation);
42  });
43  connect(&m_providerManager, SIGNAL(authenticationCredentialsMissing(Provider)),
44  SLOT(authenticationCredentialsMissing(Provider)));
45  connect(this, &Provider::loadComments, this, &AtticaProvider::loadComments);
46  connect(this, &Provider::loadPerson, this, &AtticaProvider::loadPerson);
47 }
48 
49 AtticaProvider::AtticaProvider(const Attica::Provider &provider, const QStringList &categories, const QString& additionalAgentInformation)
50  : mEntryJob(nullptr)
51  , mInitialized(false)
52 {
53  // init categories map with invalid categories
54  for (const QString &category : categories) {
55  mCategoryMap.insert(category, Attica::Category());
56  }
57  providerLoaded(provider);
58  m_provider.setAdditionalAgentInformation(additionalAgentInformation);
59 }
60 
61 QString AtticaProvider::id() const
62 {
63  return m_providerId;
64 }
65 
66 void AtticaProvider::authenticationCredentialsMissing(const KNSCore::Provider &)
67 {
68  qCDebug(KNEWSTUFFCORE) << "Authentication missing!";
69  // FIXME Show autentication dialog
70 }
71 
72 bool AtticaProvider::setProviderXML(const QDomElement &xmldata)
73 {
74  if (xmldata.tagName() != QLatin1String("provider")) {
75  return false;
76  }
77 
78  // FIXME this is quite ugly, repackaging the xml into a string
79  QDomDocument doc(QStringLiteral("temp"));
80  qCDebug(KNEWSTUFFCORE) << "setting provider xml" << doc.toString();
81 
82  doc.appendChild(xmldata.cloneNode(true));
83  m_providerManager.addProviderFromXml(doc.toString());
84 
85  if (!m_providerManager.providers().isEmpty()) {
86  qCDebug(KNEWSTUFFCORE) << "base url of attica provider:" << m_providerManager.providers().constLast().baseUrl().toString();
87  } else {
88  qCCritical(KNEWSTUFFCORE) << "Could not load provider.";
89  return false;
90  }
91  return true;
92 }
93 
94 void AtticaProvider::setCachedEntries(const KNSCore::EntryInternal::List &cachedEntries)
95 {
96  mCachedEntries = cachedEntries;
97 }
98 
99 void AtticaProvider::providerLoaded(const Attica::Provider &provider)
100 {
101  mName = provider.name();
102  qCDebug(KNEWSTUFFCORE) << "Added provider: " << provider.name();
103 
104  m_provider = provider;
105  m_provider.setAdditionalAgentInformation(mName);
106  m_providerId = provider.baseUrl().toString();
107 
108  Attica::ListJob<Attica::Category> *job = m_provider.requestCategories();
109  connect(job, &BaseJob::finished, this, &AtticaProvider::listOfCategoriesLoaded);
110  job->start();
111 }
112 
113 void AtticaProvider::listOfCategoriesLoaded(Attica::BaseJob *listJob)
114 {
115  if (!jobSuccess(listJob)) {
116  return;
117  }
118 
119  qCDebug(KNEWSTUFFCORE) << "loading categories: " << mCategoryMap.keys();
120 
121  Attica::ListJob<Attica::Category> *job = static_cast<Attica::ListJob<Attica::Category>*>(listJob);
122  const Category::List categoryList = job->itemList();
123 
124  QList<CategoryMetadata> categoryMetadataList;
125  for (const Category &category : categoryList) {
126  if (mCategoryMap.contains(category.name())) {
127  qCDebug(KNEWSTUFFCORE) << "Adding category: " << category.name() << category.displayName();
128  //If there is only the placeholder category, replace it
129  if (mCategoryMap.contains(category.name()) && !mCategoryMap.value(category.name()).isValid()) {
130  mCategoryMap.replace(category.name(), category);
131  } else {
132  mCategoryMap.insert(category.name(), category);
133  }
134 
135  CategoryMetadata categoryMetadata;
136  categoryMetadata.id = category.id();
137  categoryMetadata.name = category.name();
138  categoryMetadata.displayName = category.displayName();
139  categoryMetadataList << categoryMetadata;
140  }
141  }
142  std::sort(categoryMetadataList.begin(), categoryMetadataList.end(), [](const AtticaProvider::CategoryMetadata &i, const AtticaProvider::CategoryMetadata &j) -> bool {
143  const QString a(i.displayName.isEmpty() ? i.name : i.displayName);
144  const QString b(j.displayName.isEmpty() ? j.name : j.displayName);
145 
146  return (QCollator().compare(a, b) < 0);
147  });
148 
149  bool correct = false;
150  for(auto it = mCategoryMap.cbegin(), itEnd = mCategoryMap.cend(); it!=itEnd; ++it) {
151  if (!it.value().isValid()) {
152  qCWarning(KNEWSTUFFCORE) << "Could not find category" << it.key();
153  } else {
154  correct = true;
155  }
156  }
157 
158  if (correct) {
159  mInitialized = true;
160  emit providerInitialized(this);
161  emit categoriesMetadataLoded(categoryMetadataList);
162  } else {
163  emit signalErrorCode(KNSCore::ConfigFileError, i18n("All categories are missing"), QVariant());
164  }
165 }
166 
167 bool AtticaProvider::isInitialized() const
168 {
169  return mInitialized;
170 }
171 
172 void AtticaProvider::loadEntries(const KNSCore::Provider::SearchRequest &request)
173 {
174  if (mEntryJob) {
175  mEntryJob->abort();
176  mEntryJob = nullptr;
177  }
178 
179  mCurrentRequest = request;
180  switch (request.filter) {
181  case None:
182  break;
183  case ExactEntryId: {
184  ItemJob<Content> *job = m_provider.requestContent(request.searchTerm);
185  connect(job, &BaseJob::finished, this, &AtticaProvider::detailsLoaded);
186  job->start();
187  return;
188  }
189  case Installed:
190  if (request.page == 0) {
191  emit loadingFinished(request, installedEntries());
192  } else {
193  emit loadingFinished(request, EntryInternal::List());
194  }
195  return;
196  case Updates:
197  checkForUpdates();
198  return;
199  }
200 
201  Attica::Provider::SortMode sorting = atticaSortMode(request.sortMode);
202  Attica::Category::List categoriesToSearch;
203 
204  if (request.categories.isEmpty()) {
205  // search in all categories
206  categoriesToSearch = mCategoryMap.values();
207  } else {
208  categoriesToSearch.reserve(request.categories.size());
209  for (const QString &categoryName : qAsConst(request.categories)) {
210  categoriesToSearch.append(mCategoryMap.values(categoryName));
211  }
212  }
213 
214  ListJob<Content> *job = m_provider.searchContents(categoriesToSearch, request.searchTerm, sorting, request.page, request.pageSize);
215  connect(job, &BaseJob::finished, this, &AtticaProvider::categoryContentsLoaded);
216 
217  mEntryJob = job;
218  job->start();
219 }
220 
221 void AtticaProvider::checkForUpdates()
222 {
223  for (const EntryInternal &e : qAsConst(mCachedEntries)) {
224  ItemJob<Content> *job = m_provider.requestContent(e.uniqueId());
225  connect(job, &BaseJob::finished, this, &AtticaProvider::detailsLoaded);
226  m_updateJobs.insert(job);
227  job->start();
228  qCDebug(KNEWSTUFFCORE) << "Checking for update: " << e.name();
229  }
230 }
231 
232 void AtticaProvider::loadEntryDetails(const KNSCore::EntryInternal &entry)
233 {
234  ItemJob<Content> *job = m_provider.requestContent(entry.uniqueId());
235  connect(job, &BaseJob::finished, this, &AtticaProvider::detailsLoaded);
236  job->start();
237 }
238 
239 void AtticaProvider::detailsLoaded(BaseJob *job)
240 {
241  if (jobSuccess(job)) {
242  ItemJob<Content> *contentJob = static_cast<ItemJob<Content>*>(job);
243  Content content = contentJob->result();
244  EntryInternal entry = entryFromAtticaContent(content);
245  emit entryDetailsLoaded(entry);
246  qCDebug(KNEWSTUFFCORE) << "check update finished: " << entry.name();
247  }
248 
249  if (m_updateJobs.remove(job) && m_updateJobs.isEmpty()) {
250  qCDebug(KNEWSTUFFCORE) << "check update finished.";
251  QList<EntryInternal> updatable;
252  for (const EntryInternal &entry : qAsConst(mCachedEntries)) {
253  if (entry.status() == KNS3::Entry::Updateable) {
254  updatable.append(entry);
255  }
256  }
257  emit loadingFinished(mCurrentRequest, updatable);
258  }
259 }
260 
261 void AtticaProvider::categoryContentsLoaded(BaseJob *job)
262 {
263  if (!jobSuccess(job)) {
264  return;
265  }
266 
267  ListJob<Content> *listJob = static_cast<ListJob<Content>*>(job);
268  Content::List contents = listJob->itemList();
269 
270  EntryInternal::List entries;
271  TagsFilterChecker checker(tagFilter());
272  TagsFilterChecker downloadschecker(downloadTagFilter());
273  for (const Content &content : contents) {
274  if (!content.isValid()) {
275  qCDebug(KNEWSTUFFCORE) << "Filtered out an invalid entry. This suggests something is not right on the originating server. Please contact the administrators of" << name() << "and inform them there is an issue with content in the category or categories" << mCurrentRequest.categories;
276  continue;
277  }
278  if (checker.filterAccepts(content.tags())) {
279  bool filterAcceptsDownloads = true;
280  if (content.downloads() > 0) {
281  filterAcceptsDownloads = false;
282  for (const Attica::DownloadDescription &dli : content.downloadUrlDescriptions()) {
283  if (downloadschecker.filterAccepts(dli.tags())) {
284  filterAcceptsDownloads = true;
285  break;
286  }
287  }
288  }
289  if (filterAcceptsDownloads) {
290  mCachedContent.insert(content.id(), content);
291  entries.append(entryFromAtticaContent(content));
292  } else {
293  qCDebug(KNEWSTUFFCORE) << "Filter has excluded" << content.name() << "on download filter" << downloadTagFilter();
294  }
295  } else {
296  qCDebug(KNEWSTUFFCORE) << "Filter has excluded" << content.name() << "on entry filter" << tagFilter();
297  }
298  }
299 
300  qCDebug(KNEWSTUFFCORE) << "loaded: " << mCurrentRequest.hashForRequest() << " count: " << entries.size();
301  emit loadingFinished(mCurrentRequest, entries);
302  mEntryJob = nullptr;
303 }
304 
305 Attica::Provider::SortMode AtticaProvider::atticaSortMode(const SortMode &sortMode)
306 {
307  switch(sortMode) {
308  case Newest:
309  return Attica::Provider::Newest;
310  case Alphabetical:
311  return Attica::Provider::Alphabetical;
312  case Downloads:
313  return Attica::Provider::Downloads;
314  default:
315  return Attica::Provider::Rating;
316  }
317 }
318 
319 void AtticaProvider::loadPayloadLink(const KNSCore::EntryInternal &entry, int linkId)
320 {
321  Attica::Content content = mCachedContent.value(entry.uniqueId());
322  const DownloadDescription desc = content.downloadUrlDescription(linkId);
323 
324  if (desc.hasPrice()) {
325  // Ask for balance, then show information...
326  ItemJob<AccountBalance> *job = m_provider.requestAccountBalance();
327  connect(job, &BaseJob::finished, this, &AtticaProvider::accountBalanceLoaded);
328  mDownloadLinkJobs[job] = qMakePair(entry, linkId);
329  job->start();
330 
331  qCDebug(KNEWSTUFFCORE) << "get account balance";
332  } else {
333  ItemJob<DownloadItem> *job = m_provider.downloadLink(entry.uniqueId(), QString::number(linkId));
334  connect(job, &BaseJob::finished, this, &AtticaProvider::downloadItemLoaded);
335  mDownloadLinkJobs[job] = qMakePair(entry, linkId);
336  job->start();
337 
338  qCDebug(KNEWSTUFFCORE) << " link for " << entry.uniqueId();
339  }
340 }
341 
342 void AtticaProvider::loadComments(const EntryInternal &entry, int commentsPerPage, int page)
343 {
344  ListJob<Attica::Comment> *job = m_provider.requestComments(Attica::Comment::ContentComment, entry.uniqueId(), QLatin1String("0"), page, commentsPerPage);
345  connect(job, &BaseJob::finished, this, &AtticaProvider::loadedComments);
346  job->start();
347 }
348 
350 QList<std::shared_ptr<KNSCore::Comment>> getCommentsList(const Attica::Comment::List &comments, std::shared_ptr<KNSCore::Comment> parent) {
352  for (const Attica::Comment &comment : comments) {
353  qCDebug(KNEWSTUFFCORE) << "Appending comment with id" << comment.id() << ", which has" << comment.childCount() << "children";
354  auto knsComment = std::make_shared<KNSCore::Comment>();
355  knsComment->id = comment.id();
356  knsComment->subject = comment.subject();
357  knsComment->text = comment.text();
358  knsComment->childCount = comment.childCount();
359  knsComment->username = comment.user();
360  knsComment->date = comment.date();
361  knsComment->score = comment.score();
362  knsComment->parent = parent;
363  knsComments << knsComment;
364  if (comment.childCount() > 0) {
365  qCDebug(KNEWSTUFFCORE) << "Getting more comments, as this one has children, and we currently have this number of comments:" << knsComments.count();
366  knsComments << getCommentsList(comment.children(), knsComment);
367  qCDebug(KNEWSTUFFCORE) << "After getting the children, we now have the following number of comments:" << knsComments.count();
368  }
369  }
370  return knsComments;
371 }
372 
373 void AtticaProvider::loadedComments(Attica::BaseJob *baseJob)
374 {
375  if (!jobSuccess(baseJob)) {
376  return;
377  }
378 
379  ListJob<Attica::Comment> *job = static_cast<ListJob<Attica::Comment>*>(baseJob);
380  Attica::Comment::List comments = job->itemList();
381 
382  QList<std::shared_ptr<KNSCore::Comment>> receivedComments = getCommentsList(comments, nullptr);
383  emit commentsLoaded(receivedComments);
384 }
385 
386 void AtticaProvider::loadPerson(const QString &username)
387 {
388  if (m_provider.hasPersonService()) {
389  ItemJob<Attica::Person> *job = m_provider.requestPerson(username);
390  job->setProperty("username", username);
391  connect(job, &BaseJob::finished, this, &AtticaProvider::loadedPerson);
392  job->start();
393  }
394 }
395 
396 void AtticaProvider::loadedPerson(Attica::BaseJob *baseJob)
397 {
398  if (!jobSuccess(baseJob)) {
399  return;
400  }
401 
402  ItemJob<Attica::Person> *job = static_cast<ItemJob<Attica::Person>*>(baseJob);
403  Attica::Person person = job->result();
404 
405  auto author = std::make_shared<KNSCore::Author>();
406  author->setId(job->property("username").toString()); // This is a touch hack-like, but it ensures we actually have the data in case it is not returned by the server
407  author->setName(QString::fromLatin1("%1 %2").arg(person.firstName()).arg(person.lastName()).trimmed());
408  author->setHomepage(person.homepage());
409  author->setProfilepage(person.extendedAttribute(QStringLiteral("profilepage")));
410  author->setAvatarUrl(person.avatarUrl());
411  author->setDescription(person.extendedAttribute(QStringLiteral("description")));
412  emit personLoaded(author);
413 }
414 
415 void AtticaProvider::accountBalanceLoaded(Attica::BaseJob *baseJob)
416 {
417  if (!jobSuccess(baseJob)) {
418  return;
419  }
420 
421  ItemJob<AccountBalance> *job = static_cast<ItemJob<AccountBalance>*>(baseJob);
422  AccountBalance item = job->result();
423 
424  QPair<EntryInternal, int> pair = mDownloadLinkJobs.take(job);
425  EntryInternal entry(pair.first);
426  Content content = mCachedContent.value(entry.uniqueId());
427  if (content.downloadUrlDescription(pair.second).priceAmount() < item.balance()) {
428  qCDebug(KNEWSTUFFCORE) << "Your balance is greater than the price."
429  << content.downloadUrlDescription(pair.second).priceAmount() << " balance: " << item.balance();
430  Question question;
431  question.setQuestion(i18nc("the price of a download item, parameter 1 is the currency, 2 is the price",
432  "This item costs %1 %2.\nDo you want to buy it?",
433  item.currency(), content.downloadUrlDescription(pair.second).priceAmount()
434  ));
435  if(question.ask() == Question::YesResponse) {
436  ItemJob<DownloadItem> *job = m_provider.downloadLink(entry.uniqueId(), QString::number(pair.second));
437  connect(job, &BaseJob::finished, this, &AtticaProvider::downloadItemLoaded);
438  mDownloadLinkJobs[job] = qMakePair(entry, pair.second);
439  job->start();
440  } else {
441  return;
442  }
443  } else {
444  qCDebug(KNEWSTUFFCORE) << "You don't have enough money on your account!"
445  << content.downloadUrlDescription(0).priceAmount() << " balance: " << item.balance();
446  emit signalInformation(i18n("Your account balance is too low:\nYour balance: %1\nPrice: %2",
447  item.balance(), content.downloadUrlDescription(0).priceAmount()));
448  }
449 }
450 
451 void AtticaProvider::downloadItemLoaded(BaseJob *baseJob)
452 {
453  if (!jobSuccess(baseJob)) {
454  return;
455  }
456 
457  ItemJob<DownloadItem> *job = static_cast<ItemJob<DownloadItem>*>(baseJob);
458  DownloadItem item = job->result();
459 
460  EntryInternal entry = mDownloadLinkJobs.take(job).first;
461  entry.setPayload(QString(item.url().toString()));
462  emit payloadLinkLoaded(entry);
463 }
464 
465 EntryInternal::List AtticaProvider::installedEntries() const
466 {
467  EntryInternal::List entries;
468  for (const EntryInternal &entry : qAsConst(mCachedEntries)) {
469  if (entry.status() == KNS3::Entry::Installed || entry.status() == KNS3::Entry::Updateable) {
470  entries.append(entry);
471  }
472  }
473  return entries;
474 }
475 
476 void AtticaProvider::vote(const EntryInternal &entry, uint rating)
477 {
478  PostJob *job = m_provider.voteForContent(entry.uniqueId(), rating);
479  connect(job, &BaseJob::finished, this, &AtticaProvider::votingFinished);
480  job->start();
481 }
482 
483 void AtticaProvider::votingFinished(Attica::BaseJob *job)
484 {
485  if (!jobSuccess(job)) {
486  return;
487  }
488  emit signalInformation(i18nc("voting for an item (good/bad)", "Your vote was recorded."));
489 }
490 
491 void AtticaProvider::becomeFan(const EntryInternal &entry)
492 {
493  PostJob *job = m_provider.becomeFan(entry.uniqueId());
494  connect(job, &BaseJob::finished, this, &AtticaProvider::becomeFanFinished);
495  job->start();
496 }
497 
498 void AtticaProvider::becomeFanFinished(Attica::BaseJob *job)
499 {
500  if (!jobSuccess(job)) {
501  return;
502  }
503  emit signalInformation(i18n("You are now a fan."));
504 }
505 
506 bool AtticaProvider::jobSuccess(Attica::BaseJob *job) const
507 {
508  if (job->metadata().error() == Attica::Metadata::NoError) {
509  return true;
510  }
511  qCDebug(KNEWSTUFFCORE) << "job error: " << job->metadata().error() << " status code: " << job->metadata().statusCode() << job->metadata().message();
512 
513  if (job->metadata().error() == Attica::Metadata::NetworkError) {
514  emit signalErrorCode(KNSCore::NetworkError, i18n("Network error %1: %2", job->metadata().statusCode(), job->metadata().statusString()), job->metadata().statusCode());
515  }
516  if (job->metadata().error() == Attica::Metadata::OcsError) {
517  if (job->metadata().statusCode() == 200) {
518  emit signalErrorCode(KNSCore::OcsError, i18n("Too many requests to server. Please try again in a few minutes."), job->metadata().statusCode());
519  } else if (job->metadata().statusCode() == 405) {
520  emit signalErrorCode(KNSCore::OcsError, i18n("The Open Collaboration Services instance %1 does not support the attempted function.", name()), job->metadata().statusCode());
521  } else {
522  emit signalErrorCode(KNSCore::OcsError, i18n("Unknown Open Collaboration Service API error. (%1)", job->metadata().statusCode()), job->metadata().statusCode());
523  }
524  }
525  return false;
526 }
527 
528 EntryInternal AtticaProvider::entryFromAtticaContent(const Attica::Content &content)
529 {
530  EntryInternal entry;
531 
532  entry.setProviderId(id());
533  entry.setUniqueId(content.id());
534  entry.setStatus(KNS3::Entry::Downloadable);
535  entry.setVersion(content.version());
536  entry.setReleaseDate(content.updated().date());
537  entry.setCategory(content.attribute(QStringLiteral("typeid")));
538 
539  int index = mCachedEntries.indexOf(entry);
540  if (index >= 0) {
541  EntryInternal &cacheEntry = mCachedEntries[index];
542  // check if updateable
543  if (((cacheEntry.status() == KNS3::Entry::Installed) || (cacheEntry.status() == KNS3::Entry::Updateable)) &&
544  ((cacheEntry.version() != entry.version()) || (cacheEntry.releaseDate() != entry.releaseDate()))) {
545  cacheEntry.setStatus(KNS3::Entry::Updateable);
546  cacheEntry.setUpdateVersion(entry.version());
547  cacheEntry.setUpdateReleaseDate(entry.releaseDate());
548  }
549  entry = cacheEntry;
550  } else {
551  mCachedEntries.append(entry);
552  }
553 
554  entry.setName(content.name());
555  entry.setHomepage(content.detailpage());
556  entry.setRating(content.rating());
557  entry.setNumberOfComments(content.numberOfComments());
558  entry.setDownloadCount(content.downloads());
559  entry.setNumberFans(content.attribute(QStringLiteral("fans")).toInt());
560  entry.setDonationLink(content.attribute(QStringLiteral("donationpage")));
561  entry.setKnowledgebaseLink(content.attribute(QStringLiteral("knowledgebasepage")));
562  entry.setNumberKnowledgebaseEntries(content.attribute(QStringLiteral("knowledgebaseentries")).toInt());
563  entry.setHomepage(content.detailpage());
564 
565  entry.setPreviewUrl(content.smallPreviewPicture(QStringLiteral("1")), EntryInternal::PreviewSmall1);
566  entry.setPreviewUrl(content.smallPreviewPicture(QStringLiteral("2")), EntryInternal::PreviewSmall2);
567  entry.setPreviewUrl(content.smallPreviewPicture(QStringLiteral("3")), EntryInternal::PreviewSmall3);
568 
569  entry.setPreviewUrl(content.previewPicture(QStringLiteral("1")), EntryInternal::PreviewBig1);
570  entry.setPreviewUrl(content.previewPicture(QStringLiteral("2")), EntryInternal::PreviewBig2);
571  entry.setPreviewUrl(content.previewPicture(QStringLiteral("3")), EntryInternal::PreviewBig3);
572 
573  entry.setLicense(content.license());
574  Author author;
575  author.setId(content.author());
576  author.setName(content.author());
577  author.setHomepage(content.attribute(QStringLiteral("profilepage")));
578  entry.setAuthor(author);
579 
580  entry.setSource(EntryInternal::Online);
581  entry.setSummary(content.description());
582  entry.setShortSummary(content.summary());
583  entry.setChangelog(content.changelog());
584  entry.setTags(content.tags());
585 
588  for (const Attica::DownloadDescription &desc : descs) {
589  EntryInternal::DownloadLinkInformation info;
590  info.name = desc.name();
591  info.priceAmount = desc.priceAmount();
592  info.distributionType = desc.distributionType();
593  info.descriptionLink = desc.link();
594  info.id = desc.id();
595  info.size = desc.size();
596  info.isDownloadtypeLink = desc.type() == Attica::DownloadDescription::LinkDownload;
597  info.tags = desc.tags();
598  entry.appendDownloadLinkInformation(info);
599  }
600 
601  return entry;
602 }
603 
604 } // namespace
605 
void setRating(int rating)
Sets the rating between 0 (worst) and 100 (best).
A way to ask a user a question from inside a GUI-less library (like KNewStuffCore) ...
Definition: question.h:41
The configuration file is missing or somehow incorrect. The configuration file filename will be held ...
Definition: errorcode.h:28
void setUniqueId(const QString &id)
Set the object&#39;s unique ID.
void setName(const QString &name)
Sets the full name of the author.
Definition: core/author.cpp:77
QString uniqueId() const
Get the object&#39;s unique ID.
QString name() const
QDomNode appendChild(const QDomNode &newChild)
void setId(const QString &id)
Sets the user ID of the author.
Definition: core/author.cpp:67
void setHomepage(const QString &homepage)
Sets the homepage of the author.
QString toString(int indent) const const
void setCategory(const QString &category)
Sets the data category, e.g.
QString summary() const
Contains the core functionality for handling interaction with NewStuff providers. ...
QString attribute(const QString &key) const
void setKnowledgebaseLink(const QString &link)
Set the link for the knowledgebase.
used to keep track of a search
Definition: provider.h:67
QDate releaseDate() const
Retrieve the date of the object&#39;s publication.
KNewStuff author information.
Definition: core/author.h:30
QString toString(QUrl::FormattingOptions options) const const
void setShortSummary(const QString &summary)
Sets a short description of what the object is all about (should be very short)
QList< std::shared_ptr< KNSCore::Comment > > getCommentsList(const Attica::Comment::List &comments, std::shared_ptr< KNSCore::Comment > parent)
TODO KF6 QList is discouraged, and we&#39;ll probably want to switch this (and the rest of the KNS librar...
int size() const const
void setPayload(const QString &url)
Sets the object&#39;s file.
int downloads() const
void setSummary(const QString &summary)
Sets a description (which can potentially be very long)
QString number(int n, int base)
int count(const T &value) const const
QString version() const
Retrieve the version string of the object.
void append(const T &value)
QString & insert(int position, QChar ch)
int numberOfComments() const
void setUpdateVersion(const QString &version)
Sets the version number that is available as update.
QStringList tags() const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void setAdditionalAgentInformation(const QString &additionalInformation)
int toInt(bool *ok, int base) const const
bool isEmpty() const const
void setLicense(const QString &license)
Sets the license (abbreviation) applicable to the object.
void setAuthor(const Author &author)
Sets the author of the object.
void setSource(Source source)
The source of this entry can be Cache, Registry or Online -.
QString id() const
void setPreviewUrl(const QString &url, PreviewType type=PreviewSmall1)
Sets the object&#39;s preview file, if available.
void setNumberOfComments(int comments)
Sets the number of comments in the asset.
void setStatus(KNS3::Entry::Status status)
Sets the entry&#39;s status.
An error reported by the OCS API server. In signalErrorCode, this will be accompanied by the OCS erro...
Definition: errorcode.h:27
KNewStuff Base Provider class.
Definition: provider.h:42
Apply simple filtering logic to a list of tags.
QDateTime updated() const
void setNumberFans(int fans)
Sets how many people are fans.
void setDownloadCount(int downloads)
Sets the number of downloads.
QList::iterator end()
QString description() const
void setVersion(const QString &version)
Sets the version number.
QString i18n(const char *text, const TYPE &arg...)
void setDonationLink(const QString &link)
Set a string representation of the URL for the donation website for this entry.
void setUpdateReleaseDate(const QDate &releasedate)
Sets the release date that is available as update.
QDate date() const const
QString name() const
KNewStuff data entry container.
Definition: entryinternal.h:49
QDomNode cloneNode(bool deep) const const
void setTags(const QStringList &tags)
Set the tags for the content item.
bool filterAccepts(const QStringList &tags)
Check whether the filter list accepts the passed list of tags.
A network error. In signalErrorCode, this will be accompanied by the QtNetwork error code in the meta...
Definition: errorcode.h:26
QUrl detailpage() const
QString fromLatin1(const char *str, int size)
void setNumberKnowledgebaseEntries(int num)
Set the number of knowledgebase entries for this entry.
void setName(const QString &name)
Sets the name for this data object.
QString tagName() const const
void setHomepage(const QUrl &page)
Set a link to a website containing information about this entry.
KNS3::Entry::Status status() const
Retrieves the entry&#39;s status.
int compare(const QString &s1, const QString &s2) const const
void setChangelog(const QString &changelog)
The user written changelog.
int rating() const
QString name() const
Retrieve the name of the data object.
QUrl baseUrl() const
QList< DownloadDescription > downloadUrlDescriptions() const
void clearDownloadLinkInformation()
Remove all download options from this entry.
Definition: engine.h:28
QList::iterator begin()
void setReleaseDate(const QDate &releasedate)
Sets the release date.
void appendDownloadLinkInformation(const DownloadLinkInformation &info)
Add a new download option to this entry.
DownloadDescription downloadUrlDescription(int number) const
void replace(int i, const T &value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Tue Aug 11 2020 22:43:23 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.