KNewStuff

enginebase.cpp
1/*
2 SPDX-FileCopyrightText: 2007 Josef Spillner <spillner@kde.org>
3 SPDX-FileCopyrightText: 2007-2010 Frederik Gladhorn <gladhorn@kde.org>
4 SPDX-FileCopyrightText: 2009 Jeremy Whiting <jpwhiting@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.1-or-later
7*/
8
9#include "enginebase.h"
10#include "enginebase_p.h"
11#include <knewstuffcore_debug.h>
12
13#include <KConfig>
14#include <KConfigGroup>
15#include <KFileUtils>
16#include <KFormat>
17#include <KLocalizedString>
18
19#include <QFileInfo>
20#include <QNetworkRequest>
21#include <QProcess>
22#include <QStandardPaths>
23#include <QThreadStorage>
24#include <QTimer>
25
26#include "attica/atticaprovider_p.h"
27#include "categorymetadata.h"
28#include "compat_p.h"
29#include "opds/opdsprovider_p.h"
30#include "providerbubblewrap_p.h"
31#include "providercore.h"
32#include "providercore_p.h"
33#include "resultsstream.h"
34#include "searchrequest_p.h"
35#include "staticxml/staticxmlprovider_p.h"
36#include "transaction.h"
37#include "xmlloader_p.h"
38
39using namespace KNSCore;
40
41typedef QHash<QUrl, QPointer<XmlLoader>> EngineProviderLoaderHash;
42Q_GLOBAL_STATIC(QThreadStorage<EngineProviderLoaderHash>, s_engineProviderLoaders)
43
44namespace
45{
46
47}
48
49EngineBasePrivate::EngineBasePrivate(EngineBase *qptr)
50 : q(qptr)
51{
52}
53
54void EngineBasePrivate::addProvider(const QSharedPointer<KNSCore::ProviderCore> &provider)
55{
56 { // ProviderCore
57 qCDebug(KNEWSTUFFCORE) << "Engine addProvider called with provider with id " << provider->d->base->id();
58 providerCores.insert(provider->d->base->id(), provider);
59 provider->d->base->setTagFilter(tagFilter);
60 provider->d->base->setDownloadTagFilter(downloadTagFilter);
61 QObject::connect(provider->d->base, &ProviderBase::providerInitialized, q, [this, providerBase = provider->d->base] {
62 qCDebug(KNEWSTUFFCORE) << "providerInitialized" << providerBase->name();
63 providerBase->setCachedEntries(cache->registryForProvider(providerBase->id()));
64
65 for (const auto &core : std::as_const(providerCores)) {
66 if (!core->d->base->isInitialized()) {
67 return;
68 }
69 }
70 Q_EMIT q->signalProvidersLoaded();
71 });
72
73 QObject::connect(provider->d->base, &ProviderBase::signalError, q, [this, provider](const QString &msg) {
74 Q_EMIT q->signalErrorCode(ErrorCode::ProviderError, msg, providerFileUrl);
75 });
76 QObject::connect(provider->d->base, &ProviderBase::signalErrorCode, q, &EngineBase::signalErrorCode);
77 QObject::connect(provider->d->base, &ProviderBase::signalInformation, q, &EngineBase::signalMessage);
78 QObject::connect(provider->d->base, &ProviderBase::basicsLoaded, q, &EngineBase::providersChanged);
79 Q_EMIT q->providerAdded(provider.get());
80 }
81
82 { // ProviderBubbleWrap for legacy compatibility
83 QSharedPointer<ProviderBubbleWrap> wrappedProvider(new ProviderBubbleWrap(provider));
84 legacyProviders.insert(wrappedProvider->id(), wrappedProvider);
85 wrappedProvider->setTagFilter(tagFilter);
86 wrappedProvider->setDownloadTagFilter(downloadTagFilter);
87 q->addProvider(wrappedProvider);
88 }
89
90 Q_EMIT q->providersChanged();
91}
92
93EngineBase::EngineBase(QObject *parent)
94 : QObject(parent)
95 , d(new EngineBasePrivate(this))
96{
97 connect(d->installation, &Installation::signalInstallationError, this, [this](const QString &message) {
98 Q_EMIT signalErrorCode(ErrorCode::InstallationError, i18n("An error occurred during the installation process:\n%1", message), QVariant());
99 });
100}
101
103{
104 QStringList configSearchLocations;
106 QStringLiteral("knsrcfiles"),
109 return KFileUtils::findAllUniqueFiles(configSearchLocations, {QStringLiteral("*.knsrc")});
110}
111
112EngineBase::~EngineBase()
113{
114 if (d->cache) {
115 d->cache->writeRegistry();
116 }
117 delete d->atticaProviderManager;
118 delete d->installation;
119}
120
121bool EngineBase::init(const QString &configfile)
122{
123 qCDebug(KNEWSTUFFCORE) << "Initializing KNSCore::EngineBase from" << configfile;
124
125 QString resolvedConfigFilePath;
126 if (QFileInfo(configfile).isAbsolute()) {
127 resolvedConfigFilePath = configfile; // It is an absolute path
128 } else {
129 // Don't do the expensive search unless the config is relative
130 resolvedConfigFilePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("knsrcfiles/%1").arg(configfile));
131 }
132
133 if (!QFileInfo::exists(resolvedConfigFilePath)) {
134 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ConfigFileError, i18n("Configuration file does not exist: \"%1\"", configfile), configfile);
135 qCCritical(KNEWSTUFFCORE) << "The knsrc file" << configfile << "does not exist";
136 return false;
137 }
138
139 const KConfig conf(resolvedConfigFilePath);
140
141 if (conf.accessMode() == KConfig::NoAccess) {
142 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ConfigFileError, i18n("Configuration file exists, but cannot be opened: \"%1\"", configfile), configfile);
143 qCCritical(KNEWSTUFFCORE) << "The knsrc file" << configfile << "was found but could not be opened.";
144 return false;
145 }
146
147 const KConfigGroup group = conf.hasGroup(QStringLiteral("KNewStuff")) ? conf.group(QStringLiteral("KNewStuff")) : conf.group(QStringLiteral("KNewStuff3"));
148 if (!group.exists()) {
149 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ConfigFileError, i18n("Configuration file is invalid: \"%1\"", configfile), configfile);
150 qCCritical(KNEWSTUFFCORE) << configfile << "doesn't contain a KNewStuff or KNewStuff3 section.";
151 return false;
152 }
153
154 d->name = group.readEntry("Name");
155 d->categories = group.readEntry("Categories", QStringList());
156 qCDebug(KNEWSTUFFCORE) << "Categories: " << d->categories;
157 d->adoptionCommand = group.readEntry("AdoptionCommand");
158 d->useLabel = group.readEntry("UseLabel", i18n("Use"));
160 d->uploadEnabled = group.readEntry("UploadEnabled", true);
162
163 d->providerFileUrl = group.readEntry("ProvidersUrl", QUrl(QStringLiteral("https://autoconfig.kde.org/ocs/providers.xml")));
164 if (group.readEntry("UseLocalProvidersFile", false)) {
165 // The local providers file is called "appname.providers", to match "appname.knsrc"
166 d->providerFileUrl = QUrl::fromLocalFile(QLatin1String("%1.providers").arg(configfile.left(configfile.length() - 6)));
167 }
168
169 d->tagFilter = group.readEntry("TagFilter", QStringList(QStringLiteral("ghns_excluded!=1")));
170 d->downloadTagFilter = group.readEntry("DownloadTagFilter", QStringList());
171
172 QByteArray rawContentWarningType = group.readEntry("ContentWarning", QByteArrayLiteral("Static"));
173 bool ok = false;
174 int value = QMetaEnum::fromType<ContentWarningType>().keyToValue(rawContentWarningType.constData(), &ok);
175 if (ok) {
176 d->contentWarningType = static_cast<ContentWarningType>(value);
177 } else {
178 qCWarning(KNEWSTUFFCORE) << "Could not parse ContentWarning, invalid entry" << rawContentWarningType;
179 }
180
182
183 // Make sure that config is valid
184 QString error;
185 if (!d->installation->readConfig(group, error)) {
186 Q_EMIT signalErrorCode(ErrorCode::ConfigFileError,
187 i18n("Could not initialise the installation handler for %1:\n%2\n"
188 "This is a critical error and should be reported to the application author",
189 configfile,
190 error),
191 configfile);
192 return false;
193 }
194
195 QString registryFileName = group.readEntry("AliasFor", QString());
196 if (registryFileName.isEmpty()) {
197 registryFileName = QFileInfo(resolvedConfigFilePath).completeBaseName();
198 }
199
200 d->cache = Cache2::getCache(registryFileName);
201 qCDebug(KNEWSTUFFCORE) << "Cache is" << d->cache << "for" << registryFileName;
202 d->cache->readRegistry();
203 // This is a facade for cache2, no need to call anything on it.
204 d->legacyCache = Cache::getCache(registryFileName);
205
206 // Cache cleanup option, to help work around people deleting files from underneath KNewStuff (this
207 // happens a lot with e.g. wallpapers and icons)
208 if (d->installation->uncompressionSetting() == Installation::UseKPackageUncompression) {
209 d->shouldRemoveDeletedEntries = true;
210 }
211
212 d->shouldRemoveDeletedEntries = group.readEntry("RemoveDeadEntries", d->shouldRemoveDeletedEntries);
213 if (d->shouldRemoveDeletedEntries) {
214 d->cache->removeDeletedEntries();
215 }
216
217 loadProviders();
218
219 return true;
220}
221
222void EngineBase::loadProviders()
223{
224 if (d->providerFileUrl.isEmpty()) {
225 // it would be nicer to move the attica stuff into its own class
226 qCDebug(KNEWSTUFFCORE) << "Using OCS default providers";
227 delete d->atticaProviderManager;
228 d->atticaProviderManager = new Attica::ProviderManager;
229 connect(d->atticaProviderManager, &Attica::ProviderManager::providerAdded, this, &EngineBase::atticaProviderLoaded);
230 connect(d->atticaProviderManager, &Attica::ProviderManager::failedToLoad, this, &EngineBase::slotProvidersFailed);
231 d->atticaProviderManager->loadDefaultProviders();
232 } else {
233 qCDebug(KNEWSTUFFCORE) << "loading providers from " << d->providerFileUrl;
234 Q_EMIT loadingProvider();
235
236 XmlLoader *loader = s_engineProviderLoaders()->localData().value(d->providerFileUrl);
237 if (!loader) {
238 qCDebug(KNEWSTUFFCORE) << "No xml loader for this url yet, so create one and temporarily store that" << d->providerFileUrl;
239 loader = new XmlLoader(this);
240 s_engineProviderLoaders()->localData().insert(d->providerFileUrl, loader);
241 connect(loader, &XmlLoader::signalLoaded, this, [this]() {
242 s_engineProviderLoaders()->localData().remove(d->providerFileUrl);
243 });
244 connect(loader, &XmlLoader::signalFailed, this, [this]() {
245 s_engineProviderLoaders()->localData().remove(d->providerFileUrl);
246 });
247 connect(loader, &XmlLoader::signalHttpError, this, [this](int status, QList<QNetworkReply::RawHeaderPair> rawHeaders) {
248 if (status == 503) { // Temporarily Unavailable
249 QDateTime retryAfter;
250 static const QByteArray retryAfterKey{"Retry-After"};
251 for (const QNetworkReply::RawHeaderPair &headerPair : rawHeaders) {
252 if (headerPair.first == retryAfterKey) {
253 // Retry-After is not a known header, so we need to do a bit of running around to make that work
254 // Also, the fromHttpDate function is in the private qnetworkrequest header, so we can't use that
255 // So, simple workaround, just pass it through a dummy request and get a formatted date out (the
256 // cost is sufficiently low here, given we've just done a bunch of i/o heavy things, so...)
257 QNetworkRequest dummyRequest;
258 dummyRequest.setRawHeader(QByteArray{"Last-Modified"}, headerPair.second);
259 retryAfter = dummyRequest.header(QNetworkRequest::LastModifiedHeader).toDateTime();
260 break;
261 }
262 }
263 QTimer::singleShot(retryAfter.toMSecsSinceEpoch() - QDateTime::currentMSecsSinceEpoch(), this, &EngineBase::loadProviders);
264 // if it's a matter of a human moment's worth of seconds, just reload
265 if (retryAfter.toSecsSinceEpoch() - QDateTime::currentSecsSinceEpoch() > 2) {
266 // more than that, spit out TryAgainLaterError to let the user know what we're doing with their time
267 static const KFormat formatter;
268 Q_EMIT signalErrorCode(KNSCore::ErrorCode::TryAgainLaterError,
269 i18n("The service is currently undergoing maintenance and is expected to be back in %1.",
271 {retryAfter});
272 }
273 }
274 });
275 loader->load(d->providerFileUrl);
276 }
277 connect(loader, &XmlLoader::signalLoaded, this, &EngineBase::slotProviderFileLoaded);
278 connect(loader, &XmlLoader::signalFailed, this, &EngineBase::slotProvidersFailed);
279 }
280}
281
283{
284 return d->name;
285}
286
288{
289 return d->categories;
290}
291
292#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
294{
296 for (const auto &data : d->categoriesMetadata) {
297 list.append(Provider::CategoryMetadata{.id = data.id(), .name = data.name(), .displayName = data.displayName()});
298 }
299 return list;
300}
301#endif
302
304{
305 return d->categoriesMetadata;
306}
307
308#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
310{
312 for (const auto &preset : d->searchPresets) {
313 // This is slightly mad backwards compat. We back-convert a SearchPreset which requires a convert of
314 // SearchRequest and all the involved enums.
315 // Since this is the only place we need it this has been implemented thusly.
316 // Should someone find it offensive feel free to tear it apart into functions, but understand they are only
317 // used here.
318 list.append(KNSCompat::searchPresetToLegacy(preset));
319 }
320 return list;
321}
322#endif
323
325{
326 return d->searchPresets;
327}
328
330{
331 return d->useLabel;
332}
333
335{
336 return d->uploadEnabled;
337}
338
339#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
341{
342 // Connections are established in the modern variant of this function. No need to do anything.
343}
344#endif
345
346void EngineBase::providerInitialized([[maybe_unused]] Provider *p)
347{
348 // Unused. Replaced by lambda. Here for ABI stability.
349}
350
351void EngineBase::slotProvidersFailed()
352{
353 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ProviderError,
354 i18n("Loading of providers from file: %1 failed", d->providerFileUrl.toString()),
355 d->providerFileUrl);
356}
357
358void EngineBase::slotProviderFileLoaded(const QDomDocument &doc)
359{
360 qCDebug(KNEWSTUFFCORE) << "slotProvidersLoaded";
361
362 bool isAtticaProviderFile = false;
363
364 // get each provider element, and create a provider object from it
365 QDomElement providers = doc.documentElement();
366
367 if (providers.tagName() == QLatin1String("providers")) {
368 isAtticaProviderFile = true;
369 } else if (providers.tagName() != QLatin1String("ghnsproviders") && providers.tagName() != QLatin1String("knewstuffproviders")) {
370 qCWarning(KNEWSTUFFCORE) << "No document in providers.xml.";
371 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ProviderError,
372 i18n("Could not load get hot new stuff providers from file: %1", d->providerFileUrl.toString()),
373 d->providerFileUrl);
374 return;
375 }
376
377 QDomElement n = providers.firstChildElement(QStringLiteral("provider"));
378 while (!n.isNull()) {
379 qCDebug(KNEWSTUFFCORE) << "Provider attributes: " << n.attribute(QStringLiteral("type"));
380
381 QSharedPointer<KNSCore::ProviderCore> provider;
382 if (isAtticaProviderFile || n.attribute(QStringLiteral("type")).toLower() == QLatin1String("rest")) {
383 provider.reset(new ProviderCore(new AtticaProvider(d->categories, {})));
384 connect(provider->d->base, &ProviderBase::categoriesMetadataLoaded, this, [this](const QList<CategoryMetadata> &categories) {
385 d->categoriesMetadata = categories;
386 Q_EMIT signalCategoriesMetadataLoaded(categories);
387 });
388#ifdef SYNDICATION_FOUND
389 } else if (n.attribute(QStringLiteral("type")).toLower() == QLatin1String("opds")) {
390 provider.reset(new ProviderCore(new OPDSProvider));
391 connect(provider->d->base, &ProviderBase::searchPresetsLoaded, this, [this](const QList<SearchPreset> &presets) {
392 d->searchPresets = presets;
393 Q_EMIT signalSearchPresetsLoaded(presets);
394 });
395#endif
396 } else {
397 provider.reset(new ProviderCore(new StaticXmlProvider));
398 }
399
400 if (provider->d->base->setProviderXML(n)) {
401 d->addProvider(provider);
402 } else {
403 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ProviderError, i18n("Error initializing provider."), d->providerFileUrl);
404 }
405 n = n.nextSiblingElement();
406 }
407 Q_EMIT loadingProvider();
408}
409
410void EngineBase::atticaProviderLoaded(const Attica::Provider &atticaProvider)
411{
412 qCDebug(KNEWSTUFFCORE) << "atticaProviderLoaded called";
413 if (!atticaProvider.hasContentService()) {
414 qCDebug(KNEWSTUFFCORE) << "Found provider: " << atticaProvider.baseUrl() << " but it does not support content";
415 return;
416 }
417 auto provider = QSharedPointer<KNSCore::ProviderCore>(new KNSCore::ProviderCore(new AtticaProvider(atticaProvider, d->categories, {})));
418 d->addProvider(provider);
419}
420
421#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
423{
424 return d->legacyCache;
425}
426#endif
427
429{
430 d->tagFilter = filter;
431 for (const auto &core : std::as_const(d->providerCores)) {
432 core->d->base->setTagFilter(d->tagFilter);
433 }
434}
435
437{
438 return d->tagFilter;
439}
440
442{
443 d->tagFilter << filter;
444 for (const auto &core : std::as_const(d->providerCores)) {
445 core->d->base->setTagFilter(d->tagFilter);
446 }
447}
448
450{
451 d->downloadTagFilter = filter;
452 for (const auto &core : std::as_const(d->providerCores)) {
453 core->d->base->setDownloadTagFilter(d->downloadTagFilter);
454 }
455}
456
458{
459 return d->downloadTagFilter;
460}
461
463{
464 d->downloadTagFilter << filter;
465 for (const auto &core : std::as_const(d->providerCores)) {
466 core->d->base->setDownloadTagFilter(d->downloadTagFilter);
467 }
468}
469
471{
472 // This function is absolutely horrific. Unfortunately used in discover.
474 ret.reserve(d->providerCores.size());
475 for (const auto &core : d->providerCores) {
476 if (const auto &provider = qobject_cast<AtticaProvider *>(core->d->base)) {
477 ret.append(provider->provider());
478 }
479 }
480 return ret;
481}
482
484{
485 const auto &core = d->providerCores.value(entry.providerId());
486 return core->d->base->userCanVote();
487}
488
489void EngineBase::vote(const Entry &entry, uint rating)
490{
491 const auto &core = d->providerCores.value(entry.providerId());
492 core->d->base->vote(entry, rating);
493}
494
496{
497 const auto &core = d->providerCores.value(entry.providerId());
498 return core->d->base->userCanBecomeFan();
499}
500
501void EngineBase::becomeFan(const Entry &entry)
502{
503 const auto &core = d->providerCores.value(entry.providerId());
504 core->d->base->becomeFan(entry);
505}
506
507#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
509{
510 return d->legacyProviders.value(providerId);
511}
512#endif
513
514#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
516{
517 if (!d->legacyProviders.isEmpty()) {
518 return d->legacyProviders.constBegin().value();
519 }
520 return nullptr;
521}
522#endif
523
525{
526 return d->legacyProviders.keys();
527}
528
530{
531 return !d->adoptionCommand.isEmpty();
532}
533
534void EngineBase::updateStatus()
535{
536}
537
538Installation *EngineBase::installation() const
539{
540 return d->installation;
541}
542
543#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
544ResultsStream *EngineBase::search(const Provider::SearchRequest &request)
545{
546 return new ResultsStream(searchRequestFromLegacy(request), this);
547}
548#endif
549
551{
552 return d->contentWarningType;
553}
554
555#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
557{
558 return d->legacyProviders.values();
559}
560#endif
561
563{
564 return new ResultsStream(request, this);
565}
QUrl baseUrl() const
bool hasContentService() const
bool hasGroup(const QString &group) const
KConfigGroup group(const QString &group)
QString name() const
QString readEntry(const char *key, const char *aDefault=nullptr) const
bool exists() const
AccessMode accessMode() const override
QString formatSpelloutDuration(quint64 msecs) const
KNewStuff engine.
Definition enginebase.h:56
bool uploadEnabled
Whether or not the configuration says that the providers are expected to support uploading.
Definition enginebase.h:71
ContentWarningType
The ContentWarningType enum.
Definition enginebase.h:387
QList< Provider::SearchPreset > searchPresets()
void signalErrorCode(KNSCore::ErrorCode::ErrorCode errorCode, const QString &message, const QVariant &metadata)
Fires in the case of any critical or serious errors, such as network or API problems.
void addTagFilter(const QString &filter)
Add a single filter entry to the entry tag filter.
bool userCanBecomeFan(const Entry &entry)
Whether or not the user is allowed to become a fan of a particular entry.
QStringList categories() const
The list of the server-side names of the categories handled by this engine instance.
virtual void addProvider(QSharedPointer< KNSCore::Provider > provider)
Add a provider and connect it to the right slots.
QList< QSharedPointer< Provider > > providers() const
void becomeFan(const Entry &entry)
This will mark the user who is currently authenticated as a fan of the entry passed to the function.
bool hasAdoptionCommand() const
Whether or not an adoption command exists for this engine.
QSharedPointer< Cache > cache() const
Get the entries cache for this engine (note that it may be null if the engine is not yet initialized)...
QList< SearchPreset > searchPresets2()
void setTagFilter(const QStringList &filter)
Set a filter for results, which filters out all entries which do not match the filter,...
QStringList tagFilter() const
Gets the current tag filter list.
QString name() const
The name as defined by the knsrc file.
Q_SIGNAL void contentWarningTypeChanged()
Emitted after the initial config load.
QSharedPointer< Provider > provider(const QString &providerId) const
The Provider instance with the passed ID.
Q_SIGNAL void uploadEnabledChanged()
Fired when the uploadEnabled property changes.
Q_SIGNAL void useLabelChanged()
Signal gets emitted when the useLabel property changes.
bool userCanVote(const Entry &entry)
Whether or not a user is able to vote on the passed entry.
static QStringList availableConfigFiles()
List of all available config files.
QStringList downloadTagFilter() const
Gets the current downloadlink tag filter list.
void providersChanged()
Fired whenever the list of providers changes.
QString useLabel
Text that should be displayed for the adoption button, this defaults to "Use".
Definition enginebase.h:63
virtual bool init(const QString &configfile)
Initializes the engine.
QList< CategoryMetadata > categoriesMetadata2()
The list of metadata for the categories handled by this engine instance.
void vote(const Entry &entry, uint rating)
Cast a vote on the passed entry.
QList< Attica::Provider * > atticaProviders() const
QList< Provider::CategoryMetadata > categoriesMetadata()
ResultsStream * search(const KNSCore::Provider::SearchRequest &request)
Returns a stream object that will fulfill the request.
void addDownloadTagFilter(const QString &filter)
Add a single filter entry to the download tag filter.
QSharedPointer< Provider > defaultProvider() const
Return the first provider in the providers list (usually the default provider)
QStringList providerIDs
Definition enginebase.h:76
ContentWarningType contentWarningType
Definition enginebase.h:81
void setDownloadTagFilter(const QStringList &filter)
Sets a filter to be applied to the downloads for an entry.
void signalMessage(const QString &message)
Indicates a message to be added to the ui's log, or sent to a messagebox.
KNewStuff data entry container.
Definition entry.h:48
KNewStuff Base Provider class.
Definition provider.h:47
The ResultsStream is returned by EngineBase::search.
A search request.
Q_SCRIPTABLE CaptureState status()
QString i18n(const char *text, const TYPE &arg...)
KCOREADDONS_EXPORT QStringList findAllUniqueFiles(const QStringList &dirs, const QStringList &nameFilters={})
const char * constData() const const
qint64 currentMSecsSinceEpoch()
qint64 currentSecsSinceEpoch()
qint64 toMSecsSinceEpoch() const const
qint64 toSecsSinceEpoch() const const
QDomElement documentElement() const const
QString attribute(const QString &name, const QString &defValue) const const
bool isNull() const const
QDomElement nextSiblingElement(const QString &tagName, const QString &namespaceURI) const const
QString completeBaseName() const const
bool exists(const QString &path)
void append(QList< T > &&value)
void reserve(qsizetype size)
QMetaEnum fromType()
int keyToValue(const char *key, bool *ok) const const
typedef RawHeaderPair
QVariant header(KnownHeaders header) const const
void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T qobject_cast(QObject *object)
T * get() const const
QString locate(StandardLocation type, const QString &fileName, LocateOptions options)
QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options)
QStringList standardLocations(StandardLocation type)
bool isEmpty() const const
QString left(qsizetype n) const const
qsizetype length() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QUrl fromLocalFile(const QString &localFile)
QDateTime toDateTime() const const
Describes a category: id/name/displayName.
Definition provider.h:112
used to keep track of a search
Definition provider.h:77
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Mar 28 2025 11:59:16 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.