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

KDE's Doxygen guidelines are available online.