KNewStuff

staticxmlprovider.cpp
1 /*
2  knewstuff3/provider.cpp
3  SPDX-FileCopyrightText: 2002 Cornelius Schumacher <[email protected]>
4  SPDX-FileCopyrightText: 2003-2007 Josef Spillner <[email protected]>
5  SPDX-FileCopyrightText: 2009 Jeremy Whiting <[email protected]>
6  SPDX-FileCopyrightText: 2009-2010 Frederik Gladhorn <[email protected]>
7 
8  SPDX-License-Identifier: LGPL-2.1-or-later
9 */
10 
11 #include "staticxmlprovider_p.h"
12 
13 #include "xmlloader.h"
14 
15 #include <knewstuffcore_debug.h>
16 #include <QTimer>
17 #include <tagsfilterchecker.h>
18 
19 
20 namespace KNSCore
21 {
22 
23 StaticXmlProvider::StaticXmlProvider()
24  : mInitialized(false)
25 {
26 }
27 
28 QString StaticXmlProvider::id() const
29 {
30  return mId;
31 }
32 
33 bool StaticXmlProvider::setProviderXML(const QDomElement &xmldata)
34 {
35  if (xmldata.tagName() != QLatin1String("provider")) {
36  return false;
37  }
38 
39  mUploadUrl = QUrl(xmldata.attribute(QStringLiteral("uploadurl")));
40  mNoUploadUrl = QUrl(xmldata.attribute(QStringLiteral("nouploadurl")));
41 
42  QString url = xmldata.attribute(QStringLiteral("downloadurl"));
43  if (!url.isEmpty()) {
44  mDownloadUrls.insert(QString(), QUrl(url));
45  }
46 
47  url = xmldata.attribute(QStringLiteral("downloadurl-latest"));
48  if (!url.isEmpty()) {
49  mDownloadUrls.insert(QStringLiteral("latest"), QUrl(url));
50  }
51 
52  url = xmldata.attribute(QStringLiteral("downloadurl-score"));
53  if (!url.isEmpty()) {
54  mDownloadUrls.insert(QStringLiteral("score"), QUrl(url));
55  }
56 
57  url = xmldata.attribute(QStringLiteral("downloadurl-downloads"));
58  if (!url.isEmpty()) {
59  mDownloadUrls.insert(QStringLiteral("downloads"), QUrl(url));
60  }
61 
62  // FIXME: this depends on freedesktop.org icon naming... introduce 'desktopicon'?
63  QUrl iconurl(xmldata.attribute(QStringLiteral("icon")));
64  if (!iconurl.isValid()) {
65  iconurl = QUrl::fromLocalFile(xmldata.attribute(QStringLiteral("icon")));
66  }
67  mIcon = iconurl;
68 
69  QDomNode n;
70  for (n = xmldata.firstChild(); !n.isNull(); n = n.nextSibling()) {
71  QDomElement e = n.toElement();
72  if (e.tagName() == QLatin1String("title")) {
73  //QString lang = e.attribute("lang");
74  mName = e.text().trimmed();
75  qCDebug(KNEWSTUFFCORE) << "add name for provider ("<< this << "): " << e.text();
76  }
77  }
78 
79  // Validation
80  if ((mNoUploadUrl.isValid()) && (mUploadUrl.isValid())) {
81  qWarning() << "StaticXmlProvider: both uploadurl and nouploadurl given";
82  return false;
83  }
84 
85  if ((!mNoUploadUrl.isValid()) && (!mUploadUrl.isValid())) {
86  qWarning() << "StaticXmlProvider: neither uploadurl nor nouploadurl given";
87  return false;
88  }
89 
90  mId = mDownloadUrls[QString()].url();
91  if (mId.isEmpty()) {
92  mId = mDownloadUrls[mDownloadUrls.begin().key()].url();
93  }
94 
95  QTimer::singleShot(0, this, &StaticXmlProvider::slotEmitProviderInitialized);
96 
97  return true;
98 }
99 
100 void StaticXmlProvider::slotEmitProviderInitialized()
101 {
102  mInitialized = true;
103  Q_EMIT providerInitialized(this);
104 }
105 
106 bool StaticXmlProvider::isInitialized() const
107 {
108  return mInitialized;
109 }
110 
111 void StaticXmlProvider::setCachedEntries(const KNSCore::EntryInternal::List &cachedEntries)
112 {
113  qCDebug(KNEWSTUFFCORE) << "Set cached entries " << cachedEntries.size();
114  mCachedEntries.append(cachedEntries);
115 }
116 
117 void StaticXmlProvider::loadEntries(const KNSCore::Provider::SearchRequest &request)
118 {
119  mCurrentRequest = request;
120 
121  // static providers only have on page containing everything
122  if (request.page > 0) {
123  Q_EMIT loadingFinished(request, EntryInternal::List());
124  return;
125  }
126 
127  if (request.filter == Installed) {
128  qCDebug(KNEWSTUFFCORE) << "Installed entries: " << mId << installedEntries().size();
129  Q_EMIT loadingFinished(request, installedEntries());
130  return;
131  }
132 
133  QUrl url = downloadUrl(request.sortMode);
134  if (!url.isEmpty()) {
135  // TODO first get the entries, then filter with searchString, finally emit the finished signal...
136  // FIXME: don't creat an endless number of xmlloaders!
137  XmlLoader *loader = new XmlLoader(this);
138  connect(loader, &XmlLoader::signalLoaded, this, &StaticXmlProvider::slotFeedFileLoaded);
139  connect(loader, &XmlLoader::signalFailed, this, &StaticXmlProvider::slotFeedFailed);
140  loader->setProperty("filter", request.filter);
141  loader->setProperty("searchTerm", request.searchTerm);
142 
143  mFeedLoaders.insert(request.sortMode, loader);
144 
145  loader->load(url);
146  } else {
147  Q_EMIT loadingFailed(request);
148  }
149 }
150 
151 QUrl StaticXmlProvider::downloadUrl(SortMode mode) const
152 {
153  QUrl url;
154  switch (mode) {
155  case Rating:
156  url = mDownloadUrls.value(QStringLiteral("score"));
157  break;
158  case Alphabetical:
159  url = mDownloadUrls.value(QString());
160  break;
161  case Newest:
162  url = mDownloadUrls.value(QStringLiteral("latest"));
163  break;
164  case Downloads:
165  url = mDownloadUrls.value(QStringLiteral("downloads"));
166  break;
167  }
168  if (url.isEmpty()) {
169  url = mDownloadUrls.value(QString());
170  }
171  return url;
172 }
173 
174 void StaticXmlProvider::slotFeedFileLoaded(const QDomDocument &doc)
175 {
176  XmlLoader *loader = qobject_cast<KNSCore::XmlLoader *>(sender());
177  if (!loader) {
178  qWarning() << "Loader not found!";
179  Q_EMIT loadingFailed(mCurrentRequest);
180  return;
181  }
182 
183  // load all the entries from the domdocument given
184  EntryInternal::List entries;
185  QDomElement element;
186  const Provider::Filter filter = loader->property("filter").value<Provider::Filter>();
187  const QString searchTerm = loader->property("searchTerm").toString();
188 
189  TagsFilterChecker checker(tagFilter());
190  TagsFilterChecker downloadschecker(downloadTagFilter());
191  element = doc.documentElement();
192  QDomElement n;
193  for (n = element.firstChildElement(); !n.isNull(); n = n.nextSiblingElement()) {
194  EntryInternal entry;
195  entry.setEntryXML(n.toElement());
196  entry.setStatus(KNS3::Entry::Downloadable);
197  entry.setProviderId(mId);
198 
199  int index = mCachedEntries.indexOf(entry);
200  if (index >= 0) {
201 
202  EntryInternal cacheEntry = mCachedEntries.takeAt(index);
203  // check if updateable
204  if ((cacheEntry.status() == KNS3::Entry::Installed) &&
205  ((cacheEntry.version() != entry.version()) || (cacheEntry.releaseDate() != entry.releaseDate()))) {
206  entry.setStatus(KNS3::Entry::Updateable);
207  entry.setUpdateVersion(entry.version());
208  entry.setVersion(cacheEntry.version());
209  entry.setUpdateReleaseDate(entry.releaseDate());
210  entry.setReleaseDate(cacheEntry.releaseDate());
211  } else {
212  entry.setStatus(cacheEntry.status());
213  }
214  cacheEntry = entry;
215  }
216 
217  if (checker.filterAccepts(entry.tags())) {
218  bool filterAcceptsDownloads = true;
219  if (entry.downloadCount() > 0) {
220  const auto downloadInfoList = entry.downloadLinkInformationList();
221  for (const KNSCore::EntryInternal::DownloadLinkInformation &dli : downloadInfoList) {
222  if (downloadschecker.filterAccepts(dli.tags)) {
223  filterAcceptsDownloads = true;
224  break;
225  }
226  }
227  }
228  if (filterAcceptsDownloads) {
229  mCachedEntries.append(entry);
230 
231  if (searchIncludesEntry(entry)) {
232  switch(filter) {
233  case Installed:
234  //This is dealth with in loadEntries separately
235  Q_UNREACHABLE();
236  case Updates:
237  if (entry.status() == KNS3::Entry::Updateable) {
238  entries << entry;
239  }
240  break;
241  case ExactEntryId:
242  if (entry.uniqueId() == searchTerm) {
243  entries << entry;
244  }
245  break;
246  case None:
247  entries << entry;
248  break;
249  }
250  }
251  } else {
252  qCDebug(KNEWSTUFFCORE) << "Filter has excluded" << entry.name() << "on download filter" << downloadTagFilter();
253  }
254  } else {
255  qCDebug(KNEWSTUFFCORE) << "Filter has excluded" << entry.name() << "on entry filter" << tagFilter();
256  }
257  }
258  Q_EMIT loadingFinished(mCurrentRequest, entries);
259 }
260 
261 void StaticXmlProvider::slotFeedFailed()
262 {
263  Q_EMIT loadingFailed(mCurrentRequest);
264 }
265 
266 bool StaticXmlProvider::searchIncludesEntry(const KNSCore::EntryInternal &entry) const
267 {
268  if (mCurrentRequest.filter == Updates) {
269  if (entry.status() != KNS3::Entry::Updateable) {
270  return false;
271  }
272  }
273 
274  if (mCurrentRequest.searchTerm.isEmpty()) {
275  return true;
276  }
277  QString search = mCurrentRequest.searchTerm;
278  if (entry.name().contains(search, Qt::CaseInsensitive) ||
279  entry.summary().contains(search, Qt::CaseInsensitive) ||
280  entry.author().name().contains(search, Qt::CaseInsensitive)
281  ) {
282  return true;
283  }
284  return false;
285 }
286 
287 void StaticXmlProvider::loadPayloadLink(const KNSCore::EntryInternal &entry, int)
288 {
289  qCDebug(KNEWSTUFFCORE) << "Payload: " << entry.payload();
290  Q_EMIT payloadLinkLoaded(entry);
291 }
292 
293 EntryInternal::List StaticXmlProvider::installedEntries() const
294 {
295  EntryInternal::List entries;
296  for (const EntryInternal &entry : qAsConst(mCachedEntries)) {
297  if (entry.status() == KNS3::Entry::Installed || entry.status() == KNS3::Entry::Updateable) {
298  entries.append(entry);
299  }
300  }
301  return entries;
302 }
303 
304 }
305 
void signalLoaded(const QDomDocument &)
Indicates that the list of providers has been successfully loaded.
KNewStuff xml loader.
Definition: xmlloader.h:37
QString summary() const
Retrieve a (potentially very long) description of the object.
Author author() const
Retrieve the author of the object.
QString attribute(const QString &name, const QString &defValue) const const
Rating
QObject * sender() const const
Contains the core functionality for handling interaction with NewStuff providers. ...
QDomElement nextSiblingElement(const QString &tagName) const const
QDomElement documentElement() const const
used to keep track of a search
Definition: provider.h:67
bool isEmpty() const const
int size() const const
QDomNode nextSibling() const const
QDomElement toElement() const const
QString payload() const
Retrieve the file name of the object.
QString & insert(int position, QChar ch)
QString text() const const
CaseInsensitive
QString trimmed() const const
QFuture< void > filter(Sequence &sequence, KeepFunctor filterFunction)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool isNull() const const
bool isValid() const const
QDomNode firstChild() const const
KNewStuff data entry container.
Definition: entryinternal.h:49
QDomElement firstChildElement(const QString &tagName) const const
QString tagName() const const
KNS3::Entry::Status status() const
Retrieves the entry&#39;s status.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString name() const
Retrieve the name of the data object.
T qobject_cast(QObject *object)
Q_EMITQ_EMIT
QUrl fromLocalFile(const QString &localFile)
QString name() const
Retrieve the author&#39;s name.
Definition: core/author.cpp:82
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Jan 18 2021 22:43:50 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.