File indexing completed on 2024-10-13 03:39:10
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_p.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 setIcon(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 (name().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 setName(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 (name().isEmpty()) { 0095 // Just a fallback, because those are quite nice to have... 0096 setName(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::Entry::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, Entry::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, Entry::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->setFilter(request.filter); 0171 loader->setSearchTerm(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 Entry::List entries; 0215 QDomElement element; 0216 0217 TagsFilterChecker checker(tagFilter()); 0218 TagsFilterChecker downloadschecker(downloadTagFilter()); 0219 element = doc.documentElement(); 0220 QDomElement n; 0221 for (n = element.firstChildElement(); !n.isNull(); n = n.nextSiblingElement()) { 0222 Entry entry; 0223 entry.setEntryXML(n.toElement()); 0224 entry.setStatus(KNSCore::Entry::Downloadable); 0225 entry.setProviderId(mId); 0226 0227 int index = mCachedEntries.indexOf(entry); 0228 if (index >= 0) { 0229 Entry cacheEntry = mCachedEntries.takeAt(index); 0230 // check if updateable 0231 if ((cacheEntry.status() == KNSCore::Entry::Installed) 0232 && ((cacheEntry.version() != entry.version()) || (cacheEntry.releaseDate() != entry.releaseDate()))) { 0233 entry.setStatus(KNSCore::Entry::Updateable); 0234 entry.setUpdateVersion(entry.version()); 0235 entry.setVersion(cacheEntry.version()); 0236 entry.setUpdateReleaseDate(entry.releaseDate()); 0237 entry.setReleaseDate(cacheEntry.releaseDate()); 0238 } else { 0239 entry.setStatus(cacheEntry.status()); 0240 } 0241 cacheEntry = entry; 0242 } 0243 0244 if (checker.filterAccepts(entry.tags())) { 0245 bool filterAcceptsDownloads = true; 0246 if (entry.downloadCount() > 0) { 0247 const auto downloadInfoList = entry.downloadLinkInformationList(); 0248 for (const KNSCore::Entry::DownloadLinkInformation &dli : downloadInfoList) { 0249 if (downloadschecker.filterAccepts(dli.tags)) { 0250 filterAcceptsDownloads = true; 0251 break; 0252 } 0253 } 0254 } 0255 if (filterAcceptsDownloads) { 0256 mCachedEntries.append(entry); 0257 0258 if (searchIncludesEntry(entry)) { 0259 switch (loader->filter()) { 0260 case Installed: 0261 // This is dealth with in loadEntries separately 0262 Q_UNREACHABLE(); 0263 case Updates: 0264 if (entry.status() == KNSCore::Entry::Updateable) { 0265 entries << entry; 0266 } 0267 break; 0268 case ExactEntryId: 0269 if (entry.uniqueId() == loader->searchTerm()) { 0270 entries << entry; 0271 } 0272 break; 0273 case None: 0274 entries << entry; 0275 break; 0276 } 0277 } 0278 } else { 0279 qCDebug(KNEWSTUFFCORE) << "Filter has excluded" << entry.name() << "on download filter" << downloadTagFilter(); 0280 } 0281 } else { 0282 qCDebug(KNEWSTUFFCORE) << "Filter has excluded" << entry.name() << "on entry filter" << tagFilter(); 0283 } 0284 } 0285 Q_EMIT loadingFinished(mCurrentRequest, entries); 0286 } 0287 0288 void StaticXmlProvider::slotFeedFailed() 0289 { 0290 Q_EMIT loadingFailed(mCurrentRequest); 0291 } 0292 0293 bool StaticXmlProvider::searchIncludesEntry(const KNSCore::Entry &entry) const 0294 { 0295 if (mCurrentRequest.filter == Updates) { 0296 if (entry.status() != KNSCore::Entry::Updateable) { 0297 return false; 0298 } 0299 } 0300 0301 if (mCurrentRequest.searchTerm.isEmpty()) { 0302 return true; 0303 } 0304 QString search = mCurrentRequest.searchTerm; 0305 if (entry.name().contains(search, Qt::CaseInsensitive) || entry.summary().contains(search, Qt::CaseInsensitive) 0306 || entry.author().name().contains(search, Qt::CaseInsensitive)) { 0307 return true; 0308 } 0309 return false; 0310 } 0311 0312 void StaticXmlProvider::loadPayloadLink(const KNSCore::Entry &entry, int) 0313 { 0314 qCDebug(KNEWSTUFFCORE) << "Payload: " << entry.payload(); 0315 Q_EMIT payloadLinkLoaded(entry); 0316 } 0317 0318 Entry::List StaticXmlProvider::installedEntries() const 0319 { 0320 Entry::List entries; 0321 for (const Entry &entry : std::as_const(mCachedEntries)) { 0322 if (entry.status() == KNSCore::Entry::Installed || entry.status() == KNSCore::Entry::Updateable) { 0323 entries.append(entry); 0324 } 0325 } 0326 return entries; 0327 } 0328 0329 } 0330 0331 #include "moc_staticxmlprovider_p.cpp"