KNewStuff

engine.cpp
1 /*
2  knewstuff3/engine.cpp
3  SPDX-FileCopyrightText: 2007 Josef Spillner <[email protected]>
4  SPDX-FileCopyrightText: 2007-2010 Frederik Gladhorn <[email protected]>
5  SPDX-FileCopyrightText: 2009 Jeremy Whiting <[email protected]>
6  SPDX-FileCopyrightText: 2010 Matthias Fuchs <[email protected]>
7 
8  SPDX-License-Identifier: LGPL-2.1-or-later
9 */
10 
11 #include "engine.h"
12 
13 #include "commentsmodel.h"
14 #include "installation.h"
15 #include "question.h"
16 #include "xmlloader.h"
17 #include "imageloader_p.h"
18 
19 #include <memory>
20 #include <KConfig>
21 #include <KConfigGroup>
22 #include <knewstuffcore_debug.h>
23 #include <KLocalizedString>
24 #include <KShell>
25 #include <QDesktopServices>
26 
27 #include <QTimer>
28 #include <QProcess>
29 #include <QDir>
30 #include <qdom.h>
31 #include <QUrlQuery>
32 #include <QThreadStorage>
33 
34 #if defined(Q_OS_WIN)
35 #include <windows.h>
36 #include <shlobj.h>
37 #endif
38 
39 // libattica
40 #include <attica/providermanager.h>
41 #include <qstandardpaths.h>
42 
43 // own
44 #include "../attica/atticaprovider_p.h"
45 #include "cache.h"
46 #include "../staticxml/staticxmlprovider_p.h"
47 
48 using namespace KNSCore;
49 
51 Q_GLOBAL_STATIC(QThreadStorage<EngineProviderLoaderHash>, s_engineProviderLoaders)
52 
53 class EnginePrivate {
54 public:
55  QString getAdoptionCommand(const QString &command, const KNSCore::EntryInternal& entry, Installation *inst)
56  {
57  auto adoption = command;
58  if(adoption.isEmpty())
59  return {};
60 
61  const QLatin1String dirReplace("%d");
62  if (adoption.contains(dirReplace)) {
63  QString installPath = sharedDir(entry.installedFiles(), inst->targetInstallationPath()).path();
64  adoption.replace(dirReplace, KShell::quoteArg(installPath));
65  }
66 
67  const QLatin1String fileReplace("%f");
68  if (adoption.contains(fileReplace)) {
69  if (entry.installedFiles().isEmpty()) {
70  qCWarning(KNEWSTUFFCORE) << "no installed files to adopt";
71  return {};
72  } else if (entry.installedFiles().count() != 1) {
73  qCWarning(KNEWSTUFFCORE) << "can only adopt one file, will be using the first" << entry.installedFiles().at(0);
74  }
75 
76  adoption.replace(fileReplace, KShell::quoteArg(entry.installedFiles().at(0)));
77  }
78  return adoption;
79  }
84  static QDir sharedDir(QStringList dirs, QString rootPath)
85  {
86  // Ensure that rootPath definitely is a clean path with a slash at the end
87  rootPath = QDir::cleanPath(rootPath) + QStringLiteral("/");
88  qCInfo(KNEWSTUFFCORE) << Q_FUNC_INFO << dirs << rootPath;
89  while(!dirs.isEmpty()) {
90  QString thisDir(dirs.takeLast());
91  if (thisDir.endsWith(QStringLiteral("*"))) {
92  qCInfo(KNEWSTUFFCORE) << "Directory entry" << thisDir << "ends in a *, indicating this was installed from an archive - see Installation::archiveEntries";
93  thisDir.chop(1);
94  }
95 
96  const QString currentPath = QDir::cleanPath(thisDir);
97  qCInfo(KNEWSTUFFCORE) << "Current path is" << currentPath;
98  if (!currentPath.startsWith(rootPath)) {
99  qCInfo(KNEWSTUFFCORE) << "Current path" << currentPath << "does not start with" << rootPath << "and should be ignored";
100  continue;
101  }
102 
103  const QFileInfo current(currentPath);
104  qCInfo(KNEWSTUFFCORE) << "Current file info is" << current;
105  if (!current.isDir()) {
106  qCInfo(KNEWSTUFFCORE) << "Current path" << currentPath << "is not a directory, and should be ignored";
107  continue;
108  }
109 
110  const QDir dir(currentPath);
111  if (dir.path()==(rootPath+dir.dirName())) {
112  qCDebug(KNEWSTUFFCORE) << "Found directory" << dir;
113  return dir;
114  }
115  }
116  qCWarning(KNEWSTUFFCORE) << "Failed to locate any shared installed directory in" << dirs << "and this is almost certainly very bad.";
117  return {};
118  }
119 
120  QList<Provider::CategoryMetadata> categoriesMetadata;
121  Attica::ProviderManager *m_atticaProviderManager = nullptr;
122  QStringList tagFilter;
123  QStringList downloadTagFilter;
124  bool configLocationFallback = true;
125  QString name;
127  bool shouldRemoveDeletedEntries = false;
128 
129  // Used for updating purposes - we ought to be saving this information, but we also have to deal with old stuff, and so... this will have to do for now, and so
130  // TODO KF6: Installed state needs to move onto a per-downloadlink basis rather than per-entry
132  QMap<EntryInternal, QString> payloadToIdentify;
133  Engine::BusyState busyState;
134  QString busyMessage;
135  QString useLabel;
136 };
137 
139  : QObject(parent)
140  , m_installation(new Installation)
141  , m_cache()
142  , m_searchTimer(new QTimer)
143  , d(new EnginePrivate)
144  , m_currentPage(-1)
145  , m_pageSize(20)
146  , m_numDataJobs(0)
147  , m_numPictureJobs(0)
148  , m_numInstallJobs(0)
149  , m_initialized(false)
150 {
151  m_searchTimer->setSingleShot(true);
152  m_searchTimer->setInterval(1000);
153  connect(m_searchTimer, &QTimer::timeout, this, &Engine::slotSearchTimerExpired);
154  connect(m_installation, &Installation::signalInstallationFinished, this, &Engine::slotInstallationFinished);
155  connect(m_installation, &Installation::signalInstallationFailed, this, &Engine::slotInstallationFailed);
156  connect(m_installation, &Installation::signalInstallationError, this, [this](const QString &message){ Q_EMIT signalErrorCode(ErrorCode::InstallationError, i18n("An error occurred during the installation process:\n%1", message), QVariant()); });
157 #if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(5, 53)
158  // Pass along old error signal for compatibility
159  connect(this, &Engine::signalErrorCode, this, [this] (const KNSCore::ErrorCode &, const QString &msg, const QVariant &) {
160  Q_EMIT signalError(msg);
161  });
162 #endif
163 
164 #if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(5, 77)
165  connect(this, &Engine::signalEntryEvent, this,
166  [this](const EntryInternal &entry, EntryInternal::EntryEvent event) {
167  if (event == EntryInternal::StatusChangedEvent) {
168  Q_EMIT signalEntryChanged(entry);
169  } else if (event == EntryInternal::DetailsLoadedEvent){
170  Q_EMIT signalEntryDetailsLoaded(entry);
171  }
172  });
173 #endif
174 }
175 
177 {
178  if (m_cache) {
179  m_cache->writeRegistry();
180  }
181  delete d->m_atticaProviderManager;
182  delete m_searchTimer;
183  delete m_installation;
184  delete d;
185 }
186 
187 bool Engine::init(const QString &configfile)
188 {
189  qCDebug(KNEWSTUFFCORE) << "Initializing KNSCore::Engine from '" << configfile << "'";
190 
191  setBusy(BusyOperation::Initializing, i18n("Initializing"));
192 
194  // TODO KF6: This is fallback logic for an old location for the knsrc files. This is deprecated in KF5 and should be removed in KF6
195  bool isRelativeConfig = QFileInfo(configfile).isRelative();
196  QString actualConfig;
197  if (isRelativeConfig) {
198  // Don't do the expensive search unless the config is relative
199  actualConfig = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("knsrcfiles/%1").arg(configfile));
200  }
201  QString configFileName{configfile};
202  if (isRelativeConfig && d->configLocationFallback && actualConfig.isEmpty()) {
203  conf.reset(new KConfig(configfile));
204  qCWarning(KNEWSTUFFCORE) << "Using a deprecated location for the knsrc file" << configfile << " - please contact the author of the software which provides this file to get it updated to use the new location";
205  } else if (isRelativeConfig) {
206  configFileName = QFileInfo(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("knsrcfiles/%1").arg(configfile))).baseName();
207  conf.reset(new KConfig(QStringLiteral("knsrcfiles/%1").arg(configfile), KConfig::FullConfig, QStandardPaths::GenericDataLocation));
208  } else {
209  configFileName = QFileInfo(configfile).baseName();
210  conf.reset(new KConfig(configfile));
211  }
212 
213  if (conf->accessMode() == KConfig::NoAccess) {
214  Q_EMIT signalErrorCode(KNSCore::ConfigFileError, i18n("Configuration file exists, but cannot be opened: \"%1\"", configfile), configfile);
215  qCCritical(KNEWSTUFFCORE) << "The knsrc file '" << configfile << "' was found but could not be opened.";
216  return false;
217  }
218 
219  KConfigGroup group;
220  if (conf->hasGroup("KNewStuff3")) {
221  qCDebug(KNEWSTUFFCORE) << "Loading KNewStuff3 config: " << configfile;
222  group = conf->group("KNewStuff3");
223  } else if (conf->hasGroup("KNewStuff2")) {
224  qCDebug(KNEWSTUFFCORE) << "Loading KNewStuff2 config: " << configfile;
225  group = conf->group("KNewStuff2");
226  } else {
227  Q_EMIT signalErrorCode(KNSCore::ConfigFileError, i18n("Configuration file is invalid: \"%1\"", configfile), configfile);
228  qCCritical(KNEWSTUFFCORE) << configfile << " doesn't contain a KNewStuff3 section.";
229  return false;
230  }
231 
232  d->name = group.readEntry("Name");
233  m_categories = group.readEntry("Categories", QStringList());
234  m_adoptionCommand = group.readEntry("AdoptionCommand");
235  d->useLabel = group.readEntry("UseLabel", i18n("Use"));
237 
238  qCDebug(KNEWSTUFFCORE) << "Categories: " << m_categories;
239  m_providerFileUrl = group.readEntry("ProvidersUrl");
240 
241  d->tagFilter = group.readEntry("TagFilter", QStringList(QStringLiteral("ghns_excluded!=1")));
242  d->downloadTagFilter = group.readEntry("DownloadTagFilter", QStringList());
243 
244  // Make sure that config is valid
245  if (!m_installation->readConfig(group)) {
246  Q_EMIT signalErrorCode(ErrorCode::ConfigFileError,
247  i18n("Could not initialise the installation handler for %1\n"
248  "This is a critical error and should be reported to the application author", configfile),
249  configfile);
250  return false;
251  }
252 
253  connect(m_installation, &Installation::signalEntryChanged, this, &Engine::slotEntryChanged);
254 
255  m_cache = Cache::getCache(configFileName);
256  qCDebug(KNEWSTUFFCORE) << "Cache is" << m_cache << "for" << configFileName;
257  connect(this, &Engine::signalEntryEvent, m_cache.data(), [this] (const EntryInternal &entry, EntryInternal::EntryEvent event) {
258  if (event == EntryInternal::StatusChangedEvent) {
259  m_cache->registerChangedEntry(entry);
260  }
261  });
262  connect(m_cache.data(), &Cache::entryChanged, this, &Engine::slotEntryChanged);
263  m_cache->readRegistry();
264 
265  // Cache cleanup option, to help work around people deleting files from underneath KNewStuff (this
266  // happens a lot with e.g. wallpapers and icons)
268  d->shouldRemoveDeletedEntries = true;
269  }
270 
271  d->shouldRemoveDeletedEntries = group.readEntry("RemoveDeadEntries", d->shouldRemoveDeletedEntries);
272  if (d->shouldRemoveDeletedEntries) {
273  m_cache->removeDeletedEntries();
274  }
275 
276  m_initialized = true;
277 
278  // load the providers
279  loadProviders();
280 
281  return true;
282 }
283 
285 {
286  return d->name;
287 }
288 
290 {
291  return m_categories;
292 }
293 
295 {
296  return m_currentRequest.categories;
297 }
298 
300 {
301  return d->categoriesMetadata;
302 }
303 
304 void Engine::loadProviders()
305 {
306  if (m_providerFileUrl.isEmpty()) {
307  // it would be nicer to move the attica stuff into its own class
308  qCDebug(KNEWSTUFFCORE) << "Using OCS default providers";
309  delete d->m_atticaProviderManager;
310  d->m_atticaProviderManager = new Attica::ProviderManager;
311  connect(d->m_atticaProviderManager, &Attica::ProviderManager::providerAdded, this, &Engine::atticaProviderLoaded);
312  connect(d->m_atticaProviderManager, &Attica::ProviderManager::failedToLoad, this, &Engine::slotProvidersFailed);
313  d->m_atticaProviderManager->loadDefaultProviders();
314  } else {
315  qCDebug(KNEWSTUFFCORE) << "loading providers from " << m_providerFileUrl;
316  setBusy(BusyOperation::LoadingData, i18n("Loading provider information"));
317 
318  XmlLoader *loader = s_engineProviderLoaders()->localData().value(m_providerFileUrl);
319  if (!loader) {
320  qCDebug(KNEWSTUFFCORE) << "No xml loader for this url yet, so create one and temporarily store that" << m_providerFileUrl;
321  loader = new XmlLoader(this);
322  s_engineProviderLoaders()->localData().insert(m_providerFileUrl, loader);
323  connect(loader, &XmlLoader::signalLoaded, this, [this](){ s_engineProviderLoaders()->localData().remove(m_providerFileUrl); });
324  connect(loader, &XmlLoader::signalFailed, this, [this](){ s_engineProviderLoaders()->localData().remove(m_providerFileUrl); });
325  loader->load(QUrl(m_providerFileUrl));
326  }
327  connect(loader, &XmlLoader::signalLoaded, this, &Engine::slotProviderFileLoaded);
328  connect(loader, &XmlLoader::signalFailed, this, &Engine::slotProvidersFailed);
329  }
330 }
331 
332 void Engine::slotProviderFileLoaded(const QDomDocument &doc)
333 {
334  qCDebug(KNEWSTUFFCORE) << "slotProvidersLoaded";
335 
336  bool isAtticaProviderFile = false;
337 
338  // get each provider element, and create a provider object from it
340 
341  if (providers.tagName() == QLatin1String("providers")) {
342  isAtticaProviderFile = true;
343  } else if (providers.tagName() != QLatin1String("ghnsproviders") && providers.tagName() != QLatin1String("knewstuffproviders")) {
344  qWarning() << "No document in providers.xml.";
345  Q_EMIT signalErrorCode(KNSCore::ProviderError, i18n("Could not load get hot new stuff providers from file: %1", m_providerFileUrl), m_providerFileUrl);
346  return;
347  }
348 
349  QDomElement n = providers.firstChildElement(QStringLiteral("provider"));
350  while (!n.isNull()) {
351  qCDebug(KNEWSTUFFCORE) << "Provider attributes: " << n.attribute(QStringLiteral("type"));
352 
354  if (isAtticaProviderFile || n.attribute(QStringLiteral("type")).toLower() == QLatin1String("rest")) {
355  provider.reset(new AtticaProvider(m_categories, d->name));
356  connect(provider.data(), &Provider::categoriesMetadataLoded,
357  this, [this](const QList<Provider::CategoryMetadata> &categories){
358  d->categoriesMetadata = categories;
359  Q_EMIT signalCategoriesMetadataLoded(categories);
360  });
361  } else {
362  provider.reset(new StaticXmlProvider);
363  }
364 
365  if (provider->setProviderXML(n)) {
366  addProvider(provider);
367  } else {
368  Q_EMIT signalErrorCode(KNSCore::ProviderError, i18n("Error initializing provider."), m_providerFileUrl);
369  }
370  n = n.nextSiblingElement();
371  }
372  setBusy(BusyOperation::LoadingData, i18n("Loading data"));
373 }
374 
375 void Engine::atticaProviderLoaded(const Attica::Provider &atticaProvider)
376 {
377  qCDebug(KNEWSTUFFCORE) << "atticaProviderLoaded called";
378  if (!atticaProvider.hasContentService()) {
379  qCDebug(KNEWSTUFFCORE) << "Found provider: " << atticaProvider.baseUrl() << " but it does not support content";
380  return;
381  }
383  QSharedPointer<KNSCore::Provider> (new AtticaProvider(atticaProvider, m_categories, d->name));
384  connect(provider.data(), &Provider::categoriesMetadataLoded,
385  this, [this](const QList<Provider::CategoryMetadata> &categories){
386  d->categoriesMetadata = categories;
387  Q_EMIT signalCategoriesMetadataLoded(categories);
388  });
389  addProvider(provider);
390 }
391 
392 void Engine::addProvider(QSharedPointer<KNSCore::Provider> provider)
393 {
394  qCDebug(KNEWSTUFFCORE) << "Engine addProvider called with provider with id " << provider->id();
395  m_providers.insert(provider->id(), provider);
396  provider->setTagFilter(d->tagFilter);
397  provider->setDownloadTagFilter(d->downloadTagFilter);
398  connect(provider.data(), &Provider::providerInitialized, this, &Engine::providerInitialized);
399  connect(provider.data(), &Provider::loadingFinished, this, &Engine::slotEntriesLoaded);
400  connect(provider.data(), &Provider::entryDetailsLoaded, this, &Engine::slotEntryDetailsLoaded);
401  connect(provider.data(), &Provider::payloadLinkLoaded, this, &Engine::downloadLinkLoaded);
402 
403  connect(provider.data(), &Provider::signalError, this, [this, provider](const QString &msg) {
404  Q_EMIT signalErrorCode(ErrorCode::ProviderError, msg, m_providerFileUrl);
405  });
406  connect(provider.data(), &Provider::signalErrorCode, this, &Engine::signalErrorCode);
407  connect(provider.data(), &Provider::signalInformation, this, [this](const QString &message) {
408  Q_EMIT signalMessage(message);
409  });
410 }
411 
412 void Engine::providerJobStarted(KJob *job)
413 {
414  Q_EMIT jobStarted(job, i18n("Loading data from provider"));
415 }
416 
417 void Engine::slotProvidersFailed()
418 {
419  Q_EMIT signalErrorCode(KNSCore::ProviderError, i18n("Loading of providers from file: %1 failed", m_providerFileUrl), m_providerFileUrl);
420 }
421 
422 void Engine::providerInitialized(Provider *p)
423 {
424  qCDebug(KNEWSTUFFCORE) << "providerInitialized" << p->name();
425  p->setCachedEntries(m_cache->registryForProvider(p->id()));
426  updateStatus();
427 
428  for (const QSharedPointer<KNSCore::Provider> &p : qAsConst(m_providers)) {
429  if (!p->isInitialized()) {
430  return;
431  }
432  }
433  Q_EMIT signalProvidersLoaded();
434 }
435 
436 void Engine::slotEntriesLoaded(const KNSCore::Provider::SearchRequest &request, KNSCore::EntryInternal::List entries)
437 {
438  m_currentPage = qMax<int>(request.page, m_currentPage);
439  qCDebug(KNEWSTUFFCORE) << "loaded page " << request.page << "current page" << m_currentPage << "count:" << entries.count();
440 
441  if (request.filter == Provider::Updates) {
442  Q_EMIT signalUpdateableEntriesLoaded(entries);
443  } else {
444  m_cache->insertRequest(request, entries);
445  Q_EMIT signalEntriesLoaded(entries);
446  }
447 
448  --m_numDataJobs;
449  updateStatus();
450 }
451 
452 void Engine::reloadEntries()
453 {
454  Q_EMIT signalResetView();
455  m_currentPage = -1;
456  m_currentRequest.pageSize = m_pageSize;
457  m_currentRequest.page = 0;
458  m_numDataJobs = 0;
459 
460  for (const QSharedPointer<KNSCore::Provider> &p : qAsConst(m_providers)) {
461  if (p->isInitialized()) {
462  if (m_currentRequest.filter == Provider::Installed) {
463  // when asking for installed entries, never use the cache
464  p->loadEntries(m_currentRequest);
465  } else {
466  // take entries from cache until there are no more
468  EntryInternal::List lastCache = m_cache->requestFromCache(m_currentRequest);
469  while (!lastCache.isEmpty()) {
470  qCDebug(KNEWSTUFFCORE) << "From cache";
471  cache << lastCache;
472 
473  m_currentPage = m_currentRequest.page;
474  ++m_currentRequest.page;
475  lastCache = m_cache->requestFromCache(m_currentRequest);
476  }
477 
478  // Since the cache has no more pages, reset the request's page
479  if (m_currentPage >= 0) {
480  m_currentRequest.page = m_currentPage;
481  }
482 
483  if (!cache.isEmpty()) {
484  Q_EMIT signalEntriesLoaded(cache);
485  } else {
486  qCDebug(KNEWSTUFFCORE) << "From provider";
487  p->loadEntries(m_currentRequest);
488 
489  ++m_numDataJobs;
490  updateStatus();
491  }
492  }
493  }
494  }
495 }
496 
498 {
499  m_currentRequest.categories = categories;
500  reloadEntries();
501 }
502 
503 void Engine::setSortMode(Provider::SortMode mode)
504 {
505  if (m_currentRequest.sortMode != mode) {
506  m_currentRequest.page = -1;
507  }
508  m_currentRequest.sortMode = mode;
509  reloadEntries();
510 }
511 
512 Provider::SortMode KNSCore::Engine::sortMode() const
513 {
514  return m_currentRequest.sortMode;
515 }
516 
517 void KNSCore::Engine::setFilter(Provider::Filter filter)
518 {
519  if (m_currentRequest.filter != filter) {
520  m_currentRequest.page = -1;
521  }
522  m_currentRequest.filter = filter;
523  reloadEntries();
524 }
525 
526 Provider::Filter KNSCore::Engine::filter() const
527 {
528  return m_currentRequest.filter;
529 }
530 
532 {
533  m_searchTimer->stop();
534  m_currentRequest = KNSCore::Provider::SearchRequest(KNSCore::Provider::Newest, KNSCore::Provider::ExactEntryId, id);
535  m_currentRequest.pageSize = m_pageSize;
536 
537  EntryInternal::List cache = m_cache->requestFromCache(m_currentRequest);
538  if (!cache.isEmpty()) {
539  reloadEntries();
540  } else {
541  m_searchTimer->start();
542  }
543 }
544 
545 void Engine::setSearchTerm(const QString &searchString)
546 {
547  m_searchTimer->stop();
548  m_currentRequest.searchTerm = searchString;
549  EntryInternal::List cache = m_cache->requestFromCache(m_currentRequest);
550  if (!cache.isEmpty()) {
551  reloadEntries();
552  } else {
553  m_searchTimer->start();
554  }
555 }
556 
558 {
559  return m_currentRequest.searchTerm;
560 }
561 
563 {
564  d->tagFilter = filter;
565  for (const QSharedPointer<KNSCore::Provider> &p : qAsConst(m_providers)) {
566  p->setTagFilter(d->tagFilter);
567  }
568 }
569 
571 {
572  return d->tagFilter;
573 }
574 
576 {
577  d->tagFilter << filter;
578  for (const QSharedPointer<KNSCore::Provider> &p : qAsConst(m_providers)) {
579  p->setTagFilter(d->tagFilter);
580  }
581 }
582 
584 {
585  d->downloadTagFilter = filter;
586  for (const QSharedPointer<KNSCore::Provider> &p : qAsConst(m_providers)) {
587  p->setDownloadTagFilter(d->downloadTagFilter);
588  }
589 }
590 
592 {
593  return d->downloadTagFilter;
594 }
595 
597 {
598  d->downloadTagFilter << filter;
599  for (const QSharedPointer<KNSCore::Provider> &p : qAsConst(m_providers)) {
600  p->setDownloadTagFilter(d->downloadTagFilter);
601  }
602 }
603 
604 void Engine::slotSearchTimerExpired()
605 {
606  reloadEntries();
607 }
608 
609 void Engine::requestMoreData()
610 {
611  qCDebug(KNEWSTUFFCORE) << "Get more data! current page: " << m_currentPage << " requested: " << m_currentRequest.page;
612 
613  if (m_currentPage < m_currentRequest.page) {
614  return;
615  }
616 
617  m_currentRequest.page++;
618  doRequest();
619 }
620 
621 void Engine::requestData(int page, int pageSize)
622 {
623  m_currentRequest.page = page;
624  m_currentRequest.pageSize = pageSize;
625  doRequest();
626 }
627 
628 void Engine::doRequest()
629 {
630  for (const QSharedPointer<KNSCore::Provider> &p : qAsConst(m_providers)) {
631  if (p->isInitialized()) {
632  p->loadEntries(m_currentRequest);
633  ++m_numDataJobs;
634  updateStatus();
635  }
636  }
637 }
638 
640 {
641  if (entry.status() == KNS3::Entry::Updateable) {
642  entry.setStatus(KNS3::Entry::Updating);
643  } else {
644  entry.setStatus(KNS3::Entry::Installing);
645  }
646  Q_EMIT signalEntryEvent(entry, EntryInternal::StatusChangedEvent);
647 
648  qCDebug(KNEWSTUFFCORE) << "Install " << entry.name()
649  << " from: " << entry.providerId();
650  QSharedPointer<Provider> p = m_providers.value(entry.providerId());
651  if (p) {
652  // If linkId is -1, assume that it's an update and that we don't know what to update
653  if (entry.status() == KNS3::Entry::Updating && linkId == -1) {
654  if (entry.downloadLinkCount() == 1) {
655  // If there is only one downloadable item, then we can fairly safely assume that's what we're wanting
656  // to update, meaning we can bypass some of the more expensive operations in downloadLinkLoaded
657  qCDebug(KNEWSTUFFCORE) << "Just the one download link, so let's use that";
658  d->payloadToIdentify[entry] = QString{};
659  linkId = 1;
660  } else {
661  qCDebug(KNEWSTUFFCORE) << "Try and identify a download link to use from a total of" << entry.downloadLinkCount();
662  // While this seems silly, the payload gets reset when fetching the new download link information
663  d->payloadToIdentify[entry] = entry.payload();
664  // Drop a fresh list in place so we've got something to work with when we get the links
665  d->payloads[entry] = QStringList{};
666  linkId = 1;
667  }
668  } else {
669  qCDebug(KNEWSTUFFCORE) << "Link ID already known" << linkId;
670  // If there is no payload to identify, we will assume the payload is already known and just use that
671  d->payloadToIdentify[entry] = QString{};
672  }
673 
674  p->loadPayloadLink(entry, linkId);
675 
676  ++m_numInstallJobs;
677  updateStatus();
678  }
679 }
680 
681 void Engine::slotInstallationFinished()
682 {
683  --m_numInstallJobs;
684  updateStatus();
685 }
686 
687 void Engine::slotInstallationFailed(const QString &message)
688 {
689  --m_numInstallJobs;
691 }
692 
693 void Engine::slotEntryDetailsLoaded(const KNSCore::EntryInternal &entry)
694 {
695  Q_EMIT signalEntryEvent(entry, EntryInternal::DetailsLoadedEvent);
696 }
697 
698 void Engine::downloadLinkLoaded(const KNSCore::EntryInternal &entry)
699 {
700  if (entry.status() == KNS3::Entry::Updating) {
701  if (d->payloadToIdentify.isEmpty()) {
702  // If there's nothing to identify, and we've arrived here, then we know what the payload is
703  qCDebug(KNEWSTUFFCORE) << "If there's nothing to identify, and we've arrived here, then we know what the payload is";
704  m_installation->install(entry);
705  } else if (d->payloads[entry].count() < entry.downloadLinkCount()) {
706  // We've got more to get before we can attempt to identify anything, so fetch the next one...
707  qCDebug(KNEWSTUFFCORE) << "We've got more to get before we can attempt to identify anything, so fetch the next one...";
708  QStringList payloads = d->payloads[entry];
709  payloads << entry.payload();
710  d->payloads[entry] = payloads;
711  QSharedPointer<Provider> p = m_providers.value(entry.providerId());
712  if (p) {
713  // ok, so this should definitely always work, but... safety first, kids!
714  p->loadPayloadLink(entry, payloads.count());
715  }
716  } else {
717  // We now have all the links, so let's try and identify the correct one...
718  qCDebug(KNEWSTUFFCORE) << "We now have all the links, so let's try and identify the correct one...";
719  QString identifiedLink;
720  const QString payloadToIdentify = d->payloadToIdentify[entry];
722  const QStringList &payloads = d->payloads[entry];
723 
724  if (payloads.contains(payloadToIdentify)) {
725  // Simplest option, the link hasn't changed at all
726  qCDebug(KNEWSTUFFCORE) << "Simplest option, the link hasn't changed at all";
727  identifiedLink = payloadToIdentify;
728  } else {
729  // Next simplest option, filename is the same but in a different folder
730  qCDebug(KNEWSTUFFCORE) << "Next simplest option, filename is the same but in a different folder";
731  const QStringRef fileName = payloadToIdentify.splitRef(QChar::fromLatin1('/')).last();
732  for (const QString &payload : payloads) {
733  if (payload.endsWith(fileName)) {
734  identifiedLink = payload;
735  break;
736  }
737  }
738 
739  // Possibly the payload itself is named differently (by a CDN, for example), but the link identifier is the same...
740  qCDebug(KNEWSTUFFCORE) << "Possibly the payload itself is named differently (by a CDN, for example), but the link identifier is the same...";
741  QStringList payloadNames;
742  for (const EntryInternal::DownloadLinkInformation &downloadLink : downloadLinks) {
743  qCDebug(KNEWSTUFFCORE) << "Download link" << downloadLink.name << downloadLink.id << downloadLink.size << downloadLink.descriptionLink;
744  payloadNames << downloadLink.name;
745  if (downloadLink.name == fileName) {
746  identifiedLink = payloads[payloadNames.count() - 1];
747  qCDebug(KNEWSTUFFCORE) << "Found a suitable download link for" << fileName << "which should match" << identifiedLink;
748  }
749  }
750 
751  if (identifiedLink.isEmpty()) {
752  // Least simple option, no match - ask the user to pick (and if we still haven't got one... that's us done, no installation)
753  qCDebug(KNEWSTUFFCORE) << "Least simple option, no match - ask the user to pick (and if we still haven't got one... that's us done, no installation)";
754  auto question = std::make_unique<Question>(Question::SelectFromListQuestion);
755  question->setTitle(i18n("Pick Update Item"));
756  question->setQuestion(i18n("Please pick the item from the list below which should be used to apply this update. We were unable to identify which item to select, based on the original item, which was named %1", fileName.toString()));
757  question->setList(payloadNames);
758  if(question->ask() == Question::OKResponse) {
759  identifiedLink = payloads.value(payloadNames.indexOf(question->response()));
760  }
761  }
762  }
763  if (!identifiedLink.isEmpty()) {
764  KNSCore::EntryInternal theEntry(entry);
765  theEntry.setPayload(identifiedLink);
766  m_installation->install(theEntry);
767  } else {
768  qCWarning(KNEWSTUFFCORE) << "We failed to identify a good link for updating" << entry.name() << "and are unable to perform the update";
769  KNSCore::EntryInternal theEntry(entry);
770  theEntry.setStatus(KNS3::Entry::Updateable);
771  Q_EMIT signalEntryEvent(theEntry, EntryInternal::StatusChangedEvent);
772  Q_EMIT signalErrorCode(ErrorCode::InstallationError, i18n("We failed to identify a good link for updating %1, and are unable to perform the update", entry.name()), {entry.uniqueId()});
773  }
774  // As the serverside data may change before next time this is called, even in the same session,
775  // let's not make assumptions, and just get rid of this
776  d->payloads.remove(entry);
777  d->payloadToIdentify.remove(entry);
778  }
779  } else {
780  m_installation->install(entry);
781  }
782 }
783 
785 {
786  const KNSCore::EntryInternal::List list = m_cache->registryForProvider(entry.providerId());
787  //we have to use the cached entry here, not the entry from the provider
788  //since that does not contain the list of installed files
789  KNSCore::EntryInternal actualEntryForUninstall;
790  for (const KNSCore::EntryInternal &eInt : list) {
791  if (eInt.uniqueId() == entry.uniqueId()) {
792  actualEntryForUninstall = eInt;
793  break;
794  }
795  }
796  if (!actualEntryForUninstall.isValid()) {
797  qCDebug(KNEWSTUFFCORE) << "could not find a cached entry with following id:" << entry.uniqueId() <<
798  " -> using the non-cached version";
799  actualEntryForUninstall = entry;
800  }
801 
802  entry.setStatus(KNS3::Entry::Installing);
803  actualEntryForUninstall.setStatus(KNS3::Entry::Installing);
804  Q_EMIT signalEntryEvent(entry, EntryInternal::StatusChangedEvent);
805 
806  qCDebug(KNEWSTUFFCORE) << "about to uninstall entry " << entry.uniqueId();
807  m_installation->uninstall(actualEntryForUninstall);
808 
809  entry.setStatus(actualEntryForUninstall.status());
810  Q_EMIT signalEntryEvent(entry, EntryInternal::StatusChangedEvent);
811 }
812 
814 {
815  QSharedPointer<Provider> p = m_providers.value(entry.providerId());
816  p->loadEntryDetails(entry);
817 }
818 
819 void Engine::loadPreview(const KNSCore::EntryInternal &entry, EntryInternal::PreviewType type)
820 {
821  qCDebug(KNEWSTUFFCORE) << "START preview: " << entry.name() << type;
822  ImageLoader *l = new ImageLoader(entry, type, this);
823  connect(l, &ImageLoader::signalPreviewLoaded, this, &Engine::slotPreviewLoaded);
824  connect(l, &ImageLoader::signalError, this, [this](const KNSCore::EntryInternal &entry,
825  EntryInternal::PreviewType type,
826  const QString &errorText) {
827  Q_EMIT signalErrorCode(KNSCore::ImageError, errorText, QVariantList() << entry.name() << type);
828  qCDebug(KNEWSTUFFCORE) << "ERROR preview: " << errorText << entry.name() << type;
829  --m_numPictureJobs;
830  updateStatus();
831  });
832  l->start();
833  ++m_numPictureJobs;
834  updateStatus();
835 }
836 
837 void Engine::slotPreviewLoaded(const KNSCore::EntryInternal &entry, EntryInternal::PreviewType type)
838 {
839  qCDebug(KNEWSTUFFCORE) << "FINISH preview: " << entry.name() << type;
840  Q_EMIT signalEntryPreviewLoaded(entry, type);
841  --m_numPictureJobs;
842  updateStatus();
843 }
844 
846 {
847  if (!entry.author().email().isEmpty()) {
848  // invoke mail with the address of the author
849  QUrl mailUrl;
850  mailUrl.setScheme(QStringLiteral("mailto"));
851  mailUrl.setPath(entry.author().email());
852  QUrlQuery query;
853  query.addQueryItem(QStringLiteral("subject"), i18n("Re: %1", entry.name()));
854  mailUrl.setQuery(query);
855  QDesktopServices::openUrl(mailUrl);
856  } else if (!entry.author().homepage().isEmpty()) {
858  }
859 }
860 
861 void Engine::slotEntryChanged(const KNSCore::EntryInternal &entry)
862 {
863  Q_EMIT signalEntryEvent(entry, EntryInternal::StatusChangedEvent);
864 }
865 
867 {
868  QSharedPointer<Provider> p = m_providers.value(entry.providerId());
869  return p->userCanVote();
870 }
871 
872 void Engine::vote(const EntryInternal &entry, uint rating)
873 {
874  QSharedPointer<Provider> p = m_providers.value(entry.providerId());
875  p->vote(entry, rating);
876 }
877 
879 {
880  QSharedPointer<Provider> p = m_providers.value(entry.providerId());
881  return p->userCanBecomeFan();
882 }
883 
885 {
886  QSharedPointer<Provider> p = m_providers.value(entry.providerId());
887  p->becomeFan(entry);
888 }
889 
890 void Engine::updateStatus()
891 {
892  BusyState state;
894  if (m_numInstallJobs > 0) {
895  busyMessage = i18n("Installing");
896  state |= BusyOperation::InstallingEntry;
897  }
898  if (m_numPictureJobs > 0) {
899  busyMessage = i18np("Loading one preview", "Loading %1 previews", m_numPictureJobs);
900  state |= BusyOperation::LoadingPreview;
901  }
902  if (m_numDataJobs > 0) {
903  busyMessage = i18n("Loading data");
904  state |= BusyOperation::LoadingPreview;
905  }
906  setBusy(state, busyMessage);
907 }
908 
910 {
911  for (const QSharedPointer<KNSCore::Provider> &p : qAsConst(m_providers)) {
912  Provider::SearchRequest request(KNSCore::Provider::Newest, KNSCore::Provider::Updates);
913  p->loadEntries(request);
914  }
915 }
916 
918 {
919  for (const QSharedPointer<KNSCore::Provider> &p : qAsConst(m_providers)) {
920  Provider::SearchRequest request(KNSCore::Provider::Newest, KNSCore::Provider::Installed);
921  request.page = 0;
922  request.pageSize = m_pageSize;
923  p->loadEntries(request);
924  }
925 }
926 
927 #if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(5, 77)
929 {
930  return d->getAdoptionCommand(m_adoptionCommand, entry, m_installation);
931 }
932 #endif
933 
935 {
936  return !m_adoptionCommand.isEmpty();
937 }
938 
940 {
941  m_pageSize = pageSize;
942 }
943 
945 {
946  QStringList ret;
947  if(includeFallbackLocations) {
949  }
951  for( const QString& path : paths) {
952  ret << QString::fromLocal8Bit("%1/knsrcfiles").arg(path);
953  }
954  return ret;
955 }
956 
958 {
959  d->configLocationFallback = enableFallback;
960 }
961 
963 {
964  return m_providers.value(providerId);
965 }
966 
968 {
969  if (m_providers.count() > 0)
970  return m_providers.constBegin().value();
971  return nullptr;
972 }
973 
975 {
976  CommentsModel *model = d->commentsModels[entry];
977  if (!model) {
978  model = new CommentsModel(this);
979  model->setEntry(entry);
980  connect(model, &QObject::destroyed, this, [=](){
981  d->commentsModels.remove(entry);
982  });
983  d->commentsModels[entry] = model;
984  }
985  return model;
986 }
987 
989 {
990  return d->busyMessage;
991 }
992 
994 {
995  if (busyMessage != d->busyMessage) {
996  d->busyMessage = busyMessage;
998  }
999 #if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(5, 74)
1000  // Emit old signals for compatibility
1001  if (busyMessage.isEmpty()) {
1002  Q_EMIT signalIdle({});
1003  } else {
1004  Q_EMIT signalBusy(busyMessage);
1005  }
1006 #endif
1007 }
1008 
1010 {
1011  return d->busyState;
1012 }
1013 
1015 {
1016  if (d->busyState != state) {
1017  d->busyState = state;
1019  }
1020 }
1021 
1023  setBusyState(state);
1024  setBusyMessage(busyMessage);
1025 }
1026 
1028 {
1029  return m_cache;
1030 }
1031 
1033 {
1034  // This gets called from QML, because in QtQuick we reuse the engine, BUG: 417985
1035  // We can't handle this in the cache, because it can't access the configuration of the engine
1036  if (m_cache && d->shouldRemoveDeletedEntries) {
1037  for (const auto &provider : qAsConst(m_providers)) {
1038  if (provider && provider->isInitialized()) {
1039  const EntryInternal::List cacheBefore = m_cache->registryForProvider(provider->id());
1040  m_cache->removeDeletedEntries();
1041  const EntryInternal::List cacheAfter = m_cache->registryForProvider(provider->id());
1042  // If the user has deleted them in the background we have to update the state to deleted
1043  for (const auto &oldCachedEntry : cacheBefore){
1044  if (!cacheAfter.contains(oldCachedEntry)) {
1045  EntryInternal removedEntry = oldCachedEntry;
1046  removedEntry.setStatus(KNS3::Entry::Deleted);
1047  Q_EMIT signalEntryEvent(removedEntry, EntryInternal::StatusChangedEvent);
1048  }
1049  }
1050  }
1051  }
1052  }
1053 }
1054 
1056 {
1057  if (!hasAdoptionCommand()) {
1058  qCWarning(KNEWSTUFFCORE) << "no adoption command specified";
1059  return;
1060  }
1061  const QString command = d->getAdoptionCommand(m_adoptionCommand, entry, m_installation);
1062  QStringList split = KShell::splitArgs(command);
1063  QProcess *process = new QProcess(this);
1064  process->setProgram(split.takeFirst());
1065  process->setArguments(split);
1066 
1068  // The debug output is too talkative to be useful
1069  env.insert(QStringLiteral("QT_LOGGING_RULES"), QStringLiteral("*.debug=false"));
1070  process->setProcessEnvironment(env);
1071 
1072  process->start();
1073 
1074  connect(process, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this,
1075  [this, process ,entry, command](int exitCode, QProcess::ExitStatus) {
1076  if (exitCode == 0) {
1077  Q_EMIT signalEntryEvent(entry, EntryInternal::EntryEvent::AdoptedEvent);
1078 
1079  // Handle error output as warnings if the process hasn't crashed
1080  const QString stdErr = QString::fromLocal8Bit(process->readAllStandardError());
1081  if (!stdErr.isEmpty()) {
1082  Q_EMIT signalMessage(stdErr);
1083  }
1084  } else {
1085  const QString errorMsg = i18n("Failed to adopt '%1'\n%2",
1086  entry.name(), QString::fromLocal8Bit(process->readAllStandardError()));
1087  Q_EMIT signalErrorCode(KNSCore::AdoptionError, errorMsg, QVariantList{command});
1088  }
1089  });
1090 }
1091 
1092 QString Engine::useLabel() const
1093 {
1094  return d->useLabel;
1095 }
void setInterval(int msec)
void signalLoaded(const QDomDocument &)
Indicates that the list of providers has been successfully loaded.
void uninstall(KNSCore::EntryInternal entry)
Uninstalls an entry.
Definition: engine.cpp:784
KNewStuff xml loader.
Definition: xmlloader.h:37
void loadPreview(const KNSCore::EntryInternal &entry, EntryInternal::PreviewType type)
Attempt to load a specific preview for the specified entry.
Definition: engine.cpp:819
The configuration file is missing or somehow incorrect. The configuration file filename will be held ...
Definition: errorcode.h:29
QList< Provider::CategoryMetadata > categoriesMetadata()
The list of metadata for the categories handled by this engine instance.
Definition: engine.cpp:299
QString busyMessage() const
String representation of the engines busy state.
QString name(const QVariant &location)
QString uniqueId() const
Get the object&#39;s unique ID.
virtual QString id() const =0
A unique Id for this provider (the url in most cases)
Author author() const
Retrieve the author of the object.
Provider::SortMode sortMode() const
The sort mode set on the current request.
Definition: engine.cpp:512
QString attribute(const QString &name, const QString &defValue) const const
void setTagFilter(const QStringList &tagFilter)
Set the tag filter used for entries by this provider.
Definition: provider.cpp:73
QString toString() const const
QStringList downloadTagFilter() const
Gets the current downloadlink tag filter list.
Definition: engine.cpp:591
Installation of a content item has failed. If known, the entry&#39;s unique ID will be the metadata...
Definition: errorcode.h:31
< As Archive, except that if there is more than an item in the file, put contents in a subdirectory w...
Definition: installation.h:65
bool hasContentService() const
const T & at(int i) const const
UncompressionOptions uncompressionSetting() const
Returns the uncompression setting, in a computer-readable format.
void checkForUpdates()
Request for packages that are installed and need update.
Definition: engine.cpp:909
Contains the core functionality for handling interaction with NewStuff providers. ...
void insert(const QString &name, const QString &value)
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QStringList categories() const
The list of the server-side names of the categories handled by this engine instance.
Definition: engine.cpp:289
void setSearchTerm(const QString &searchString)
Sets a string search term.
Definition: engine.cpp:545
QDomElement nextSiblingElement(const QString &tagName) const const
bool userCanBecomeFan(const EntryInternal &entry)
Whether or not the user is allowed to become a fan of a particular entry.
Definition: engine.cpp:878
void setDownloadTagFilter(const QStringList &downloadTagFilter)
Set the tag filter used for download items by this provider.
Definition: provider.cpp:83
void addTagFilter(const QString &filter)
Add a single filter entry to the entry tag filter.
Definition: engine.cpp:575
QDomElement documentElement() const const
used to keep track of a search
Definition: provider.h:67
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
void setDownloadTagFilter(const QStringList &filter)
Sets a filter to be applied to the downloads for an entry.
Definition: engine.cpp:583
void fetchEntryById(const QString &id)
Convenience method to launch a search for one specific entry.
Definition: engine.cpp:531
bool hasAdoptionCommand() const
Whether or not an adoption command exists for this engine.
Definition: engine.cpp:934
void setArguments(const QStringList &arguments)
void chop(int n)
T * data() const const
BusyState busyState() const
Busy state of the engine.
QCA_EXPORT ProviderList providers()
Q_INVOKABLE void adoptEntry(const KNSCore::EntryInternal &entry)
Adopt an entry using the adoption command.
Definition: engine.cpp:1055
ErrorCode
An enumeration of specific error conditions which might occur and which users of KNewStuff would want...
Definition: errorcode.h:25
void setBusyMessage(const QString &busyMessage)
Definition: engine.cpp:993
int size() const const
QStringList standardLocations(QStandardPaths::StandardLocation type)
void reset(T *other)
Engine(QObject *parent=nullptr)
Constructor.
Definition: engine.cpp:138
virtual bool event(QEvent *e)
Q_SIGNAL void busyStateChanged()
Signal gets emitted when the busy state changes.
void setPath(const QString &path, QUrl::ParsingMode mode)
void timeout()
void setPayload(const QString &url)
Sets the object&#39;s file.
void addQueryItem(const QString &key, const QString &value)
QString payload() const
Retrieve the file name of the object.
int count(const T &value) const const
QString fromLocal8Bit(const char *str, int size)
void contactAuthor(const EntryInternal &entry)
Try to contact the author of the entry by email or showing their homepage.
Definition: engine.cpp:845
Q_SIGNAL void useLabelChanged()
Signal gets emitted when the useLabel property changes.
CommentsModel * commentsForEntry(const KNSCore::EntryInternal &entry)
This function will return an instance of a model which contains comments for the entry passed to it...
Definition: engine.cpp:974
void setCategoriesFilter(const QStringList &categories)
Set the categories that will be included in searches.
Definition: engine.cpp:497
Adopting one entry has failed. The adoption command will be in the metadata as a QVariantList.
Definition: errorcode.h:33
QList< DownloadLinkInformation > downloadLinkInformationList() const
A list of downloadable data for this entry.
QChar fromLatin1(char c)
void load(const QUrl &url)
Starts asynchronously loading the xml document from the specified URL.
Definition: xmlloader.cpp:28
void addDownloadTagFilter(const QString &filter)
Add a single filter entry to the download tag filter.
Definition: engine.cpp:596
Loading an image has failed. The entry name and preview type which failed will be held in the metadat...
Definition: errorcode.h:32
bool isEmpty() const const
void setConfigLocationFallback(bool enableFallback)
Sets whether or not the config file location discovery fallback should be active. ...
Definition: engine.cpp:957
QStringList installedFiles() const
Retrieve the locally installed files.
int downloadLinkCount() const
The number of available download options for this entry.
~Engine()
Destructor.
Definition: engine.cpp:176
bool isEmpty() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
void setScheme(const QString &scheme)
void finished(int exitCode)
QString searchTerm() const
The search term for the current search (empty if none is set)
Definition: engine.cpp:557
void setBusyState(BusyState state)
Sets the busy state of the engine.
Definition: engine.cpp:1014
void checkForInstalled()
Requests installed packages with an up to date state.
Definition: engine.cpp:917
QSharedPointer< Provider > provider(const QString &providerId) const
The Provider instance with the passed ID.
Definition: engine.cpp:962
A model which takes care of the comments for a single EntryInternal.
bool init(const QString &configfile)
Initializes the engine.
Definition: engine.cpp:187
QString homepage() const
Retrieve the author&#39;s homepage.
void setStatus(KNS3::Entry::Status status)
Sets the entry&#39;s status.
KCOREADDONS_EXPORT QStringList splitArgs(const QString &cmd, Options flags=NoOptions, Errors *err=nullptr)
int indexOf(QStringView str, int from) const const
KNewStuff Base Provider class.
Definition: provider.h:42
void signalMessage(const QString &message)
Indicates a message to be added to the ui&#39;s log, or sent to a messagebox.
Provider::Filter filter() const
The result filter set on the current request.
Definition: engine.cpp:526
void setFilter(Provider::Filter filter)
Set a filter for results (defaults to none), which will allow you to show only installed entries...
Definition: engine.cpp:517
void setPageSize(int pageSize)
Set the page size for requests not made explicitly with requestData(int,int)
Definition: engine.cpp:939
void setSortMode(Provider::SortMode mode)
Set the order the search results are returned in.
Definition: engine.cpp:503
Q_INVOKABLE void revalidateCacheEntries()
If the same engine gets reused and the user could have used the delete functionality of the KCMs the ...
Definition: engine.cpp:1032
QString useLabel() const
Text that should be displayed for the adoption button, this defaults to i18n("Use") ...
QString toLower() const const
void setProgram(const QString &program)
KCOREADDONS_EXPORT QString quoteArg(const QString &arg)
QStringList tagFilter() const
Gets the current tag filter list.
Definition: engine.cpp:570
void signalInstallationError(const QString &message)
An informational signal fired when a serious error occurs during the installation.
void becomeFan(const EntryInternal &entry)
This will mark the user who is currently authenticated as a fan of the entry passed to the function...
Definition: engine.cpp:884
bool contains(const T &value) const const
QString targetInstallationPath() const
void stop()
void uninstall(KNSCore::EntryInternal entry)
Uninstalls an entry.
bool isNull() const const
void vote(const EntryInternal &entry, uint rating)
Cast a vote on the passed entry.
Definition: engine.cpp:872
QString adoptionCommand(const KNSCore::EntryInternal &entry) const
The adoption command can be used to allow a user to make use of an entry&#39;s installed data...
Definition: engine.cpp:928
QString i18n(const char *text, const TYPE &arg...)
void setProcessEnvironment(const QProcessEnvironment &environment)
QString cleanPath(const QString &path)
T takeLast()
virtual QString name() const
Retrieves the common name of the provider.
Definition: provider.cpp:63
bool isRelative() const const
virtual void loadEntries(const KNSCore::Provider::SearchRequest &request)=0
load the given search and return given page
KIOFILEWIDGETS_EXPORT QString dir(const QString &fileClass)
QString name() const
The name as defined by the knsrc file.
Definition: engine.cpp:284
QSharedPointer< Cache > cache() const
Get the entries cache for this engine (note that it may be null if the engine is not yet initialized)...
Definition: engine.cpp:1027
T takeFirst()
Q_SIGNAL void busyMessageChanged()
Signal gets emitted when the busy message changes.
KNewStuff data entry container.
Definition: entryinternal.h:49
void setBusy(BusyState state, const QString &busyMessage)
Utility method to set both the state and busyMessage.
Definition: engine.cpp:1022
QVector< QStringRef > splitRef(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QDomElement firstChildElement(const QString &tagName) const const
QSharedPointer< Provider > defaultProvider() const
Return the first provider in the providers list (usually the default provider)
Definition: engine.cpp:967
A provider has failed to load or initialize. The provider file URL or provider URL will be held in th...
Definition: errorcode.h:30
KNewStuff entry installation.
Definition: installation.h:38
void setQuery(const QString &query, QUrl::ParsingMode mode)
void start(int msec)
void loadDetails(const KNSCore::EntryInternal &entry)
Get the full details of a specific entry.
Definition: engine.cpp:813
QStringList categoriesFilter() const
The list of categories searches will actually show results from.
Definition: engine.cpp:294
QString tagName() const const
QProcessEnvironment systemEnvironment()
void install(const KNSCore::EntryInternal &entry)
Installs an entry&#39;s payload file.
KNS3::Entry::Status status() const
Retrieves the entry&#39;s status.
bool openUrl(const QUrl &url)
static QStringList configSearchLocations(bool includeFallbackLocations=false)
Get a list of all the locations which will be used when searching for knsrc files, in the order in which the search will occur.
Definition: engine.cpp:944
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString name() const
Retrieve the name of the data object.
void signalErrorCode(const KNSCore::ErrorCode &errorCode, const QString &message, const QVariant &metadata)
Fires in the case of any critical or serious errors, such as network or API problems.
QString email() const
Retrieve the author&#39;s email address.
Definition: core/author.cpp:92
QUrl baseUrl() const
QString providerId() const
The id of the provider this entry belongs to.
bool userCanVote(const EntryInternal &entry)
Whether or not a user is able to vote on the passed entry.
Definition: engine.cpp:866
T readEntry(const QString &key, const T &aDefault) const
QString baseName() const const
void install(KNSCore::EntryInternal entry, int linkId=1)
Installs an entry&#39;s payload file.
Definition: engine.cpp:639
void destroyed(QObject *obj)
void start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode)
Q_EMITQ_EMIT
QByteArray readAllStandardError()
QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
void setTagFilter(const QStringList &filter)
Set a filter for results, which filters out all entries which do not match the filter, as applied to the tags for the entry.
Definition: engine.cpp:562
void setSingleShot(bool singleShot)
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Jan 19 2021 22:44:09 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.