File indexing completed on 2024-04-28 11:43:39
0001 /* 0002 knewstuff3/provider.cpp 0003 SPDX-FileCopyrightText: 2002 Cornelius Schumacher <schumacher@kde.org> 0004 SPDX-FileCopyrightText: 2003-2007 Josef Spillner <spillner@kde.org> 0005 SPDX-FileCopyrightText: 2009 Jeremy Whiting <jpwhiting@kde.org> 0006 SPDX-FileCopyrightText: 2009-2010 Frederik Gladhorn <gladhorn@kde.org> 0007 0008 SPDX-License-Identifier: LGPL-2.1-or-later 0009 */ 0010 0011 #include "staticxmlprovider_p.h" 0012 0013 #include "xmlloader.h" 0014 0015 #include <QTimer> 0016 #include <knewstuffcore_debug.h> 0017 #include <tagsfilterchecker.h> 0018 0019 namespace KNSCore 0020 { 0021 StaticXmlProvider::StaticXmlProvider() 0022 : mInitialized(false) 0023 { 0024 } 0025 0026 QString StaticXmlProvider::id() const 0027 { 0028 return mId; 0029 } 0030 0031 bool StaticXmlProvider::setProviderXML(const QDomElement &xmldata) 0032 { 0033 if (xmldata.tagName() != QLatin1String("provider")) { 0034 return false; 0035 } 0036 0037 mUploadUrl = QUrl(xmldata.attribute(QStringLiteral("uploadurl"))); 0038 mNoUploadUrl = QUrl(xmldata.attribute(QStringLiteral("nouploadurl"))); 0039 0040 QString url = xmldata.attribute(QStringLiteral("downloadurl")); 0041 if (!url.isEmpty()) { 0042 mDownloadUrls.insert(QString(), QUrl(url)); 0043 } 0044 0045 url = xmldata.attribute(QStringLiteral("downloadurl-latest")); 0046 if (!url.isEmpty()) { 0047 mDownloadUrls.insert(QStringLiteral("latest"), QUrl(url)); 0048 } 0049 0050 url = xmldata.attribute(QStringLiteral("downloadurl-score")); 0051 if (!url.isEmpty()) { 0052 mDownloadUrls.insert(QStringLiteral("score"), QUrl(url)); 0053 } 0054 0055 url = xmldata.attribute(QStringLiteral("downloadurl-downloads")); 0056 if (!url.isEmpty()) { 0057 mDownloadUrls.insert(QStringLiteral("downloads"), QUrl(url)); 0058 } 0059 0060 // FIXME: this depends on freedesktop.org icon naming... introduce 'desktopicon'? 0061 QUrl iconurl(xmldata.attribute(QStringLiteral("icon"))); 0062 if (!iconurl.isValid()) { 0063 iconurl = QUrl::fromLocalFile(xmldata.attribute(QStringLiteral("icon"))); 0064 } 0065 mIcon = iconurl; 0066 0067 QDomNode n; 0068 QLocale::Language systemLanguage = QLocale::system().language(); 0069 QString firstName; 0070 for (n = xmldata.firstChild(); !n.isNull(); n = n.nextSibling()) { 0071 QDomElement e = n.toElement(); 0072 if (e.tagName() == QLatin1String("title")) { 0073 const QString lang{e.attribute(QLatin1String("lang"))}; 0074 bool useThisTitle{false}; 0075 if (mName.isEmpty() && lang.isEmpty()) { 0076 // If we have no title as yet, and we've also got no language defined, this is the default 0077 // and name we need to set it, even if we might override it later 0078 useThisTitle = true; 0079 } else { 0080 const QLocale locale(lang); 0081 if (systemLanguage == locale.language()) { 0082 useThisTitle = true; 0083 } 0084 } 0085 if (useThisTitle) { 0086 mName = e.text().trimmed(); 0087 qCDebug(KNEWSTUFFCORE) << "add name for provider (" << this << "): " << e.text(); 0088 } 0089 if (firstName.isEmpty()) { 0090 firstName = e.text().trimmed(); 0091 } 0092 } 0093 } 0094 if (mName.isEmpty()) { 0095 // Just a fallback, because those are quite nice to have... 0096 mName = firstName; 0097 } 0098 0099 // Validation 0100 if ((mNoUploadUrl.isValid()) && (mUploadUrl.isValid())) { 0101 qWarning() << "StaticXmlProvider: both uploadurl and nouploadurl given"; 0102 return false; 0103 } 0104 0105 if ((!mNoUploadUrl.isValid()) && (!mUploadUrl.isValid())) { 0106 qWarning() << "StaticXmlProvider: neither uploadurl nor nouploadurl given"; 0107 return false; 0108 } 0109 0110 if (mUploadUrl.isValid()) { 0111 setWebsite(mUploadUrl); 0112 } else { 0113 setWebsite(mNoUploadUrl); 0114 } 0115 0116 mId = mDownloadUrls[QString()].url(); 0117 if (mId.isEmpty()) { 0118 mId = mDownloadUrls[mDownloadUrls.begin().key()].url(); 0119 } 0120 0121 QTimer::singleShot(0, this, &StaticXmlProvider::slotEmitProviderInitialized); 0122 0123 return true; 0124 } 0125 0126 void StaticXmlProvider::slotEmitProviderInitialized() 0127 { 0128 mInitialized = true; 0129 Q_EMIT providerInitialized(this); 0130 } 0131 0132 bool StaticXmlProvider::isInitialized() const 0133 { 0134 return mInitialized; 0135 } 0136 0137 void StaticXmlProvider::setCachedEntries(const KNSCore::EntryInternal::List &cachedEntries) 0138 { 0139 qCDebug(KNEWSTUFFCORE) << "Set cached entries " << cachedEntries.size(); 0140 mCachedEntries.append(cachedEntries); 0141 } 0142 0143 void StaticXmlProvider::loadEntries(const KNSCore::Provider::SearchRequest &request) 0144 { 0145 mCurrentRequest = request; 0146 0147 // static providers only have on page containing everything 0148 if (request.page > 0) { 0149 Q_EMIT loadingFinished(request, EntryInternal::List()); 0150 return; 0151 } 0152 0153 if (request.filter == Installed) { 0154 qCDebug(KNEWSTUFFCORE) << "Installed entries: " << mId << installedEntries().size(); 0155 if (request.page == 0) { 0156 Q_EMIT loadingFinished(request, installedEntries()); 0157 } else { 0158 Q_EMIT loadingFinished(request, EntryInternal::List()); 0159 } 0160 return; 0161 } 0162 0163 QUrl url = downloadUrl(request.sortMode); 0164 if (!url.isEmpty()) { 0165 // TODO first get the entries, then filter with searchString, finally emit the finished signal... 0166 // FIXME: don't create an endless number of xmlloaders! 0167 XmlLoader *loader = new XmlLoader(this); 0168 connect(loader, &XmlLoader::signalLoaded, this, &StaticXmlProvider::slotFeedFileLoaded); 0169 connect(loader, &XmlLoader::signalFailed, this, &StaticXmlProvider::slotFeedFailed); 0170 loader->setProperty("filter", request.filter); 0171 loader->setProperty("searchTerm", request.searchTerm); 0172 0173 mFeedLoaders.insert(request.sortMode, loader); 0174 0175 loader->load(url); 0176 } else { 0177 Q_EMIT loadingFailed(request); 0178 } 0179 } 0180 0181 QUrl StaticXmlProvider::downloadUrl(SortMode mode) const 0182 { 0183 QUrl url; 0184 switch (mode) { 0185 case Rating: 0186 url = mDownloadUrls.value(QStringLiteral("score")); 0187 break; 0188 case Alphabetical: 0189 url = mDownloadUrls.value(QString()); 0190 break; 0191 case Newest: 0192 url = mDownloadUrls.value(QStringLiteral("latest")); 0193 break; 0194 case Downloads: 0195 url = mDownloadUrls.value(QStringLiteral("downloads")); 0196 break; 0197 } 0198 if (url.isEmpty()) { 0199 url = mDownloadUrls.value(QString()); 0200 } 0201 return url; 0202 } 0203 0204 void StaticXmlProvider::slotFeedFileLoaded(const QDomDocument &doc) 0205 { 0206 XmlLoader *loader = qobject_cast<KNSCore::XmlLoader *>(sender()); 0207 if (!loader) { 0208 qWarning() << "Loader not found!"; 0209 Q_EMIT loadingFailed(mCurrentRequest); 0210 return; 0211 } 0212 0213 // load all the entries from the domdocument given 0214 EntryInternal::List entries; 0215 QDomElement element; 0216 const Provider::Filter filter = loader->property("filter").value<Provider::Filter>(); 0217 const QString searchTerm = loader->property("searchTerm").toString(); 0218 0219 TagsFilterChecker checker(tagFilter()); 0220 TagsFilterChecker downloadschecker(downloadTagFilter()); 0221 element = doc.documentElement(); 0222 QDomElement n; 0223 for (n = element.firstChildElement(); !n.isNull(); n = n.nextSiblingElement()) { 0224 EntryInternal entry; 0225 entry.setEntryXML(n.toElement()); 0226 entry.setStatus(KNS3::Entry::Downloadable); 0227 entry.setProviderId(mId); 0228 0229 int index = mCachedEntries.indexOf(entry); 0230 if (index >= 0) { 0231 EntryInternal cacheEntry = mCachedEntries.takeAt(index); 0232 // check if updateable 0233 if ((cacheEntry.status() == KNS3::Entry::Installed) 0234 && ((cacheEntry.version() != entry.version()) || (cacheEntry.releaseDate() != entry.releaseDate()))) { 0235 entry.setStatus(KNS3::Entry::Updateable); 0236 entry.setUpdateVersion(entry.version()); 0237 entry.setVersion(cacheEntry.version()); 0238 entry.setUpdateReleaseDate(entry.releaseDate()); 0239 entry.setReleaseDate(cacheEntry.releaseDate()); 0240 } else { 0241 entry.setStatus(cacheEntry.status()); 0242 } 0243 cacheEntry = entry; 0244 } 0245 0246 if (checker.filterAccepts(entry.tags())) { 0247 bool filterAcceptsDownloads = true; 0248 if (entry.downloadCount() > 0) { 0249 const auto downloadInfoList = entry.downloadLinkInformationList(); 0250 for (const KNSCore::EntryInternal::DownloadLinkInformation &dli : downloadInfoList) { 0251 if (downloadschecker.filterAccepts(dli.tags)) { 0252 filterAcceptsDownloads = true; 0253 break; 0254 } 0255 } 0256 } 0257 if (filterAcceptsDownloads) { 0258 mCachedEntries.append(entry); 0259 0260 if (searchIncludesEntry(entry)) { 0261 switch (filter) { 0262 case Installed: 0263 // This is dealth with in loadEntries separately 0264 Q_UNREACHABLE(); 0265 case Updates: 0266 if (entry.status() == KNS3::Entry::Updateable) { 0267 entries << entry; 0268 } 0269 break; 0270 case ExactEntryId: 0271 if (entry.uniqueId() == searchTerm) { 0272 entries << entry; 0273 } 0274 break; 0275 case None: 0276 entries << entry; 0277 break; 0278 } 0279 } 0280 } else { 0281 qCDebug(KNEWSTUFFCORE) << "Filter has excluded" << entry.name() << "on download filter" << downloadTagFilter(); 0282 } 0283 } else { 0284 qCDebug(KNEWSTUFFCORE) << "Filter has excluded" << entry.name() << "on entry filter" << tagFilter(); 0285 } 0286 } 0287 Q_EMIT loadingFinished(mCurrentRequest, entries); 0288 } 0289 0290 void StaticXmlProvider::slotFeedFailed() 0291 { 0292 Q_EMIT loadingFailed(mCurrentRequest); 0293 } 0294 0295 bool StaticXmlProvider::searchIncludesEntry(const KNSCore::EntryInternal &entry) const 0296 { 0297 if (mCurrentRequest.filter == Updates) { 0298 if (entry.status() != KNS3::Entry::Updateable) { 0299 return false; 0300 } 0301 } 0302 0303 if (mCurrentRequest.searchTerm.isEmpty()) { 0304 return true; 0305 } 0306 QString search = mCurrentRequest.searchTerm; 0307 if (entry.name().contains(search, Qt::CaseInsensitive) || entry.summary().contains(search, Qt::CaseInsensitive) 0308 || entry.author().name().contains(search, Qt::CaseInsensitive)) { 0309 return true; 0310 } 0311 return false; 0312 } 0313 0314 void StaticXmlProvider::loadPayloadLink(const KNSCore::EntryInternal &entry, int) 0315 { 0316 qCDebug(KNEWSTUFFCORE) << "Payload: " << entry.payload(); 0317 Q_EMIT payloadLinkLoaded(entry); 0318 } 0319 0320 EntryInternal::List StaticXmlProvider::installedEntries() const 0321 { 0322 EntryInternal::List entries; 0323 for (const EntryInternal &entry : std::as_const(mCachedEntries)) { 0324 if (entry.status() == KNS3::Entry::Installed || entry.status() == KNS3::Entry::Updateable) { 0325 entries.append(entry); 0326 } 0327 } 0328 return entries; 0329 } 0330 0331 } 0332 0333 #include "moc_staticxmlprovider_p.cpp"