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