KNewStuff

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

KDE's Doxygen guidelines are available online.