From 1814ac22bab7ac2949031b047dd20edf275000c9 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sun, 12 Jan 2020 01:02:39 +0300 Subject: [PATCH 01/61] Initial support for AlpineAPK backend --- .../AlpineApkBackend/AlpineApkBackend.cpp | 240 ++++++++++++++++++ .../AlpineApkBackend/AlpineApkBackend.h | 73 ++++++ .../AlpineApkBackend/AlpineApkResource.cpp | 208 +++++++++++++++ .../AlpineApkBackend/AlpineApkResource.h | 81 ++++++ .../AlpineApkSourcesBackend.cpp | 151 +++++++++++ .../AlpineApkSourcesBackend.h | 50 ++++ .../AlpineApkBackend/AlpineApkTransaction.cpp | 90 +++++++ .../AlpineApkBackend/AlpineApkTransaction.h | 46 ++++ .../backends/AlpineApkBackend/CMakeLists.txt | 52 ++++ libdiscover/backends/CMakeLists.txt | 13 + 10 files changed, 1004 insertions(+) create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkResource.h create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.h create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.h create mode 100644 libdiscover/backends/AlpineApkBackend/CMakeLists.txt diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp new file mode 100644 index 000000000..f5aa5477d --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -0,0 +1,240 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#include "AlpineApkBackend.h" +#include "AlpineApkResource.h" +//#include "DummyReviewsBackend.h" +#include "AlpineApkTransaction.h" +#include "AlpineApkSourcesBackend.h" +#include "alpineapk_backend_logging.h" // generated by ECM + +#include "resources/StandardBackendUpdater.h" +#include "resources/SourcesModel.h" +#include "Transaction/Transaction.h" +#include "Category/Category.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DISCOVER_BACKEND_PLUGIN(AlpineApkBackend) + +AlpineApkBackend::AlpineApkBackend(QObject *parent) + : AbstractResourcesBackend(parent) + , m_updater(new StandardBackendUpdater(this)) + , m_startElements(120) +{ +#ifndef QT_DEBUG + const_cast(LOG_ALPINEAPK()).setEnabled(QtDebugMsg, false); +#endif + + qCDebug(LOG_ALPINEAPK) << "constructing backend!"; + + QTimer::singleShot(500, this, &AlpineApkBackend::toggleFetching); + //connect(m_reviews, &DummyReviewsBackend::ratingsReady, this, &AbstractResourcesBackend::emitRatingsReady); + connect(m_updater, &StandardBackendUpdater::updatesCountChanged, this, &AlpineApkBackend::updatesCountChanged); + + populate(); + //if (!m_fetching) + // m_reviews->initialize(); + + SourcesModel::global()->addSourcesBackend(new AlpineApkSourcesBackend(this)); +} + +QVector AlpineApkBackend::category() const +{ + static Category *cat = new Category( + QStringLiteral("All applications"), // displayName + QStringLiteral("applications-other"), // icon + { }, // orFilters + { displayName() }, // pluginName + { }, // subCategories + QUrl(), // decoration (what is it?) + false // isAddons + ); + return { cat }; +} + +void AlpineApkBackend::populate() +{ + qCDebug(LOG_ALPINEAPK) << "populating resources..."; + + if (m_apkdb.open(QtApk::Database::QTAPK_OPENF_READONLY)) { + m_availablePackages = m_apkdb.getAvailablePackages(); + m_installedPackages = m_apkdb.getInstalledPackages(); + m_apkdb.close(); + } + + if (m_availablePackages.size() > 0) { + for (const QtApk::Package &pkg: m_availablePackages) { + AlpineApkResource *res = new AlpineApkResource(pkg, this); + res->setCategoryName(QStringLiteral("all")); + res->setOriginSource(QStringLiteral("apk")); + res->setSection(QStringLiteral("dummy")); + const QString key = pkg.name.toLower(); + m_resources.insert(key, res); + connect(res, &AlpineApkResource::stateChanged, this, &AlpineApkBackend::updatesCountChanged); + } + qCDebug(LOG_ALPINEAPK) << " available" << m_availablePackages.size() + << "packages"; + } + if (m_installedPackages.size() > 0) { + for (const QtApk::Package &pkg: m_installedPackages) { + const QString key = pkg.name.toLower(); + if (m_resources.contains(key)) { + m_resources.value(key)->setState(AbstractResource::Installed); + } + } + qCDebug(LOG_ALPINEAPK) << " installed" << m_installedPackages.size() + << "packages"; + } +} + +void AlpineApkBackend::toggleFetching() +{ + m_fetching = !m_fetching; + qCDebug(LOG_ALPINEAPK) << "fetching..." << m_fetching; + emit fetchingChanged(); + //if (!m_fetching) + // m_reviews->initialize(); +} + +int AlpineApkBackend::updatesCount() const +{ + qCDebug(LOG_ALPINEAPK) << "updatesCount(): " << m_updater->updatesCount(); + return m_updater->updatesCount(); +} + +ResultsStream *AlpineApkBackend::search(const AbstractResourcesBackend::Filters &filter) +{ + QVector ret; + if (!filter.resourceUrl.isEmpty()) { + return findResourceByPackageName(filter.resourceUrl); + } else { + for (AbstractResource *r: qAsConst(m_resources)) { + if (r->type() == AbstractResource::Technical + && filter.state != AbstractResource::Upgradeable) { + continue; + } + if (r->state() < filter.state) { + continue; + } + if(r->name().contains(filter.search, Qt::CaseInsensitive) + || r->comment().contains(filter.search, Qt::CaseInsensitive)) { + ret += r; + } + } + } + return new ResultsStream(QStringLiteral("AlpineApkStream"), ret); +} + +ResultsStream *AlpineApkBackend::findResourceByPackageName(const QUrl &searchUrl) +{ +// if (search.isLocalFile()) { +// AlpineApkResource* res = new AlpineApkResource( +// search.fileName(), AbstractResource::Technical, this); +// res->setSize(666); +// res->setState(AbstractResource::None); +// m_resources.insert(res->packageName(), res); +// connect(res, &AlpineApkResource::stateChanged, this, &AlpineApkBackend::updatesCountChanged); +// return new ResultsStream(QStringLiteral("AlpineApkStream-local"), { res }); +// } + + AlpineApkResource *result = nullptr; + + // QUrl("appstream://org.kde.krita.desktop") + // smart workaround for appstream + if (searchUrl.scheme() == QLatin1String("appstream")) { + // remove leading "org.kde." + QString pkgName = searchUrl.host(); + if (pkgName.startsWith(QLatin1String("org.kde."))) { + pkgName = pkgName.mid(8); + } + // remove trailing ".desktop" + if (pkgName.endsWith(QLatin1String(".desktop"))) { + pkgName = pkgName.left(pkgName.length() - 8); + } + // now we can search for "krita" package + result = m_resources.value(pkgName); + } + + if (!result) { + return new ResultsStream(QStringLiteral("AlpineApkStream"), {}); + } + return new ResultsStream(QStringLiteral("AlpineApkStream"), { result }); +} + +AbstractBackendUpdater *AlpineApkBackend::backendUpdater() const +{ + return m_updater; +} + +AbstractReviewsBackend *AlpineApkBackend::reviewsBackend() const +{ + // qCDebug(LOG_ALPINEAPK) << "reviewsBbackend(): we don't support reviews ("; + // return m_reviews; + return nullptr; +} + +Transaction* AlpineApkBackend::installApplication(AbstractResource *app, const AddonList &addons) +{ + return new AlpineApkTransaction(qobject_cast(app), addons, Transaction::InstallRole); +} + +Transaction* AlpineApkBackend::installApplication(AbstractResource *app) +{ + return new AlpineApkTransaction(qobject_cast(app), Transaction::InstallRole); +} + +Transaction* AlpineApkBackend::removeApplication(AbstractResource *app) +{ + return new AlpineApkTransaction(qobject_cast(app), Transaction::RemoveRole); +} + +void AlpineApkBackend::checkForUpdates() +{ + if(m_fetching) { + qCDebug(LOG_ALPINEAPK) << "checkForUpdates(): already fetching"; + return; + } + qCDebug(LOG_ALPINEAPK) << "checkForUpdates()!"; + toggleFetching(); + // populate(QStringLiteral("Moar")); + QTimer::singleShot(1000, this, &AlpineApkBackend::toggleFetching); +} + +QString AlpineApkBackend::displayName() const +{ + return QStringLiteral("Alpine APK backend"); +} + +bool AlpineApkBackend::hasApplications() const +{ + return true; +} + +#include "AlpineApkBackend.moc" diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h new file mode 100644 index 000000000..624df1c9f --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef AlpineApkBackend_H +#define AlpineApkBackend_H + +#include +#include + +#include + +// class DummyReviewsBackend; +class StandardBackendUpdater; +class AlpineApkResource; +class AlpineApkBackend : public AbstractResourcesBackend +{ + Q_OBJECT + Q_PROPERTY(int startElements MEMBER m_startElements) + +public: + explicit AlpineApkBackend(QObject *parent = nullptr); + + QVector category() const override; + int updatesCount() const override; + AbstractBackendUpdater *backendUpdater() const override; + AbstractReviewsBackend *reviewsBackend() const override; + ResultsStream *search(const AbstractResourcesBackend::Filters &filter) override; + ResultsStream *findResourceByPackageName(const QUrl &search); + QHash resources() const { return m_resources; } + bool isValid() const override { return true; } // No external file dependencies that could cause runtime errors + + Transaction *installApplication(AbstractResource *app) override; + Transaction *installApplication(AbstractResource *app, const AddonList &addons) override; + Transaction *removeApplication(AbstractResource *app) override; + bool isFetching() const override { return m_fetching; } + void checkForUpdates() override; + QString displayName() const override; + bool hasApplications() const override; + +public Q_SLOTS: + void toggleFetching(); + +private: + void populate(); + + QHash m_resources; + StandardBackendUpdater *m_updater; + // DummyReviewsBackend* m_reviews; + QtApk::Database m_apkdb; + QVector m_availablePackages; + QVector m_installedPackages; + bool m_fetching = true; + int m_startElements = 0; +}; + +#endif // AlpineApkBackend_H diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp new file mode 100644 index 000000000..d827e7b3b --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp @@ -0,0 +1,208 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#include +#include "AlpineApkResource.h" +#include "alpineapk_backend_logging.h" // generated by ECM +#include "Transaction/AddonList.h" + +AlpineApkResource::AlpineApkResource(const QtApk::Package &apkPkg, + AbstractResourcesBackend *parent) + : AbstractResource(parent) + , m_state(AbstractResource::State::None) + , m_type(Application) + , m_pkg(apkPkg) +{ +} + +QList AlpineApkResource::addonsInformation() +{ + return m_addons; +} + +QString AlpineApkResource::availableVersion() const +{ + return m_pkg.version; +} + +QStringList AlpineApkResource::categories() +{ + return { m_category }; +} + +QString AlpineApkResource::comment() +{ + return m_pkg.description; +} + +int AlpineApkResource::size() +{ + return static_cast(m_pkg.size); +} + +QUrl AlpineApkResource::homepage() +{ + return QUrl::fromUserInput(m_pkg.url); +} + +QUrl AlpineApkResource::helpURL() +{ + return QUrl(); +} + +QUrl AlpineApkResource::bugURL() +{ + return QUrl(); +} + +QUrl AlpineApkResource::donationURL() +{ + return QUrl(); +} + +QVariant AlpineApkResource::icon() const +{ + return QStringLiteral("package-x-generic"); +} + +QString AlpineApkResource::installedVersion() const +{ + return m_pkg.version; +} + +QJsonArray AlpineApkResource::licenses() +{ + return { + QJsonObject { + { QStringLiteral("name"), m_pkg.license }, + { QStringLiteral("url"), QStringLiteral("https://spdx.org/license-list") }, + } + }; +} + +QString AlpineApkResource::longDescription() +{ + return m_pkg.description; +} + +QString AlpineApkResource::name() const +{ + return m_pkg.name; +} + +QString AlpineApkResource::origin() const +{ + return m_originSoruce; +} + +QString AlpineApkResource::packageName() const +{ + return m_pkg.name; +} + +QString AlpineApkResource::section() +{ + return m_sectionName; +} + +AbstractResource::State AlpineApkResource::state() +{ + return m_state; +} + +void AlpineApkResource::fetchChangelog() +{ + // QString log = longDescription(); + // Q_EMIT changelogFetched(log); +} + +void AlpineApkResource::fetchScreenshots() +{ + // Q_EMIT screenshotsFetched(m_screenshotThumbnails, m_screenshots); +} + +void AlpineApkResource::setState(AbstractResource::State state) +{ + m_state = state; + emit stateChanged(); +} + +void AlpineApkResource::setCategoryName(const QString &categoryName) +{ + m_category = categoryName; +} + +void AlpineApkResource::setOriginSource(const QString &originSource) +{ + m_originSoruce = originSource; +} + +void AlpineApkResource::setSection(const QString §ionName) +{ + m_sectionName = sectionName; +} + +void AlpineApkResource::setAddons(const AddonList &addons) +{ + const QStringList addonsToInstall = addons.addonsToInstall(); + for (const QString &toInstall : addonsToInstall) { + setAddonInstalled(toInstall, true); + } + const QStringList addonsToRemove = addons.addonsToRemove(); + for (const QString &toRemove : addonsToRemove) { + setAddonInstalled(toRemove, false); + } +} + +void AlpineApkResource::setAddonInstalled(const QString &addon, bool installed) +{ + for(PackageState &elem : m_addons) { + if(elem.name() == addon) { + elem.setInstalled(installed); + } + } +} + + +void AlpineApkResource::invokeApplication() const +{ + // QDesktopServices d; + // d.openUrl(QUrl(QStringLiteral("https://projects.kde.org/projects/extragear/sysadmin/muon"))); +} + +QUrl AlpineApkResource::url() const +{ + return QUrl(QLatin1String("apk://") + packageName()); +} + +QString AlpineApkResource::author() const +{ + return m_pkg.maintainer; +} + +QString AlpineApkResource::sourceIcon() const +{ + return QStringLiteral("player-time"); +} + +QDate AlpineApkResource::releaseDate() const +{ + return m_pkg.buildTime.date(); +} diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h new file mode 100644 index 000000000..79b100e10 --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef ALPINEAPKRESOURCE_H +#define ALPINEAPKRESOURCE_H + +#include +#include + +class AddonList; + +class AlpineApkResource : public AbstractResource +{ + Q_OBJECT + +public: + explicit AlpineApkResource(const QtApk::Package &apkPkg, AbstractResourcesBackend *parent); + + QList addonsInformation() override; + QString section() override; + QString origin() const override; + QString longDescription() override; + QString availableVersion() const override; + QString installedVersion() const override; + QJsonArray licenses() override; + int size() override; + QUrl homepage() override; + QUrl helpURL() override; + QUrl bugURL() override; + QUrl donationURL() override; + QStringList categories() override; + AbstractResource::State state() override; + QVariant icon() const override; + QString comment() override; + QString name() const override; + QString packageName() const override; + AbstractResource::Type type() const override { return m_type; } + bool canExecute() const override { return true; } + void invokeApplication() const override; + void fetchChangelog() override; + void fetchScreenshots() override; + QUrl url() const override; + QString author() const override; + QString sourceIcon() const override; + QDate releaseDate() const override; + + void setState(State state); + void setCategoryName(const QString &categoryName); + void setOriginSource(const QString &originSource); + void setSection(const QString §ionName); + void setAddons(const AddonList &addons); + void setAddonInstalled(const QString &addon, bool installed); + +public: + AbstractResource::State m_state; + const AbstractResource::Type m_type; + QtApk::Package m_pkg; + QString m_category; + QString m_originSoruce; + QString m_sectionName; + QList m_addons; +}; + +#endif // ALPINEAPKRESOURCE_H diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp new file mode 100644 index 000000000..b964b40d1 --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp @@ -0,0 +1,151 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#include "AlpineApkSourcesBackend.h" +#include "alpineapk_backend_logging.h" // generated by ECM + +#include +#include +#include + +// KF5 +#include + +// libapk-qt +#include + +AlpineApkSourcesBackend::AlpineApkSourcesBackend(AbstractResourcesBackend *parent) + : AbstractSourcesBackend(parent) + , m_sourcesModel(new QStandardItemModel(this)) + , m_refreshAction(new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), + QStringLiteral("Refresh"), this)) +{ + loadSources(); + QObject::connect(m_refreshAction, &QAction::triggered, + this, &AlpineApkSourcesBackend::loadSources); + + // can be used to track enabling/disabling repo source + // QObject::connect(m_sourcesModel, &QStandardItemModel::itemChanged, this, [](QStandardItem* item) { + // qCDebug(LOG_ALPINEAPK) << "source backend: DummySource changed" << item << item->checkState(); + // }); +} + +QAbstractItemModel *AlpineApkSourcesBackend::sources() +{ + return m_sourcesModel; +} + +bool AlpineApkSourcesBackend::addSource(const QString &id) +{ + return addSourceFull(id, QString(), true); +} + +QStandardItem *AlpineApkSourcesBackend::sourceForId(const QString& id) const +{ + for (int i = 0, c = m_sourcesModel->rowCount(); i < c; ++i) { + QStandardItem *it = m_sourcesModel->item(i, 0); + if (it->text() == id) { + return it; + } + } + return nullptr; +} + +bool AlpineApkSourcesBackend::addSourceFull(const QString &id, const QString &comment, bool enabled) +{ + if (id.isEmpty()) { + return false; + } + + qCDebug(LOG_ALPINEAPK) << "source backend: Adding source:" << id; + + QStandardItem *it = new QStandardItem(id); + it->setData(id, AbstractSourcesBackend::IdRole); + it->setData(comment, Qt::ToolTipRole); + it->setCheckable(true); + it->setCheckState(enabled ? Qt::Checked : Qt::Unchecked); + // for now, disable editing sources + it->setFlags(it->flags() & ~Qt::ItemIsEnabled); + m_sourcesModel->appendRow(it); + return true; +} + +void AlpineApkSourcesBackend::loadSources() +{ + QVector repos = QtApk::Database::getRepositories(); + m_sourcesModel->clear(); + for (const QtApk::Repository &repo: repos) { + addSourceFull(repo.url, repo.comment, repo.enabled); + } +} + +bool AlpineApkSourcesBackend::removeSource(const QString &id) +{ + const QStandardItem *it = sourceForId(id); + if (!it) { + qCWarning(LOG_ALPINEAPK) << "source backend: couldn't find " << id; + return false; + } + return m_sourcesModel->removeRow(it->row()); +} + +QString AlpineApkSourcesBackend::idDescription() +{ + return i18nc("Adding repo", "Enter apk repository URL, for example: " + "http://dl-cdn.alpinelinux.org/alpine/edge/testing/"); +} + +QVariantList AlpineApkSourcesBackend::actions() const +{ + static const QVariantList s_actions { + QVariant::fromValue(m_refreshAction), + }; + return s_actions; +} + +bool AlpineApkSourcesBackend::supportsAdding() const +{ + return false; // for now, disable editing sources +} + +bool AlpineApkSourcesBackend::canMoveSources() const +{ + return false; // for now, disable editing sources +} + +bool AlpineApkSourcesBackend::moveSource(const QString& sourceId, int delta) +{ + int row = sourceForId(sourceId)->row(); + QList prevRow = m_sourcesModel->takeRow(row); + if (prevRow.isEmpty()) { + return false; + } + + const int destRow = row + delta; + m_sourcesModel->insertRow(destRow, prevRow); + if (destRow == 0 || row == 0) { + Q_EMIT firstSourceIdChanged(); + } + if (destRow == (m_sourcesModel->rowCount() - 1) + || row == (m_sourcesModel->rowCount() - 1)) { + Q_EMIT lastSourceIdChanged(); + } + return true; +} diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.h b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.h new file mode 100644 index 000000000..578945917 --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef ALPINEAPKSOURCESBACKEND_H +#define ALPINEAPKSOURCESBACKEND_H + +#include +#include + +class AlpineApkSourcesBackend : public AbstractSourcesBackend +{ +public: + explicit AlpineApkSourcesBackend(AbstractResourcesBackend *parent); + + QAbstractItemModel *sources() override; + bool addSource(const QString &id) override; + bool removeSource(const QString &id) override; + QString idDescription() override; + QVariantList actions() const override; + bool supportsAdding() const override; + bool canMoveSources() const override; + bool moveSource(const QString &sourceId, int delta) override; + +private: + QStandardItem *sourceForId(const QString &id) const; + bool addSourceFull(const QString &id, const QString &comment, bool enabled); + void loadSources(); + + QStandardItemModel *m_sourcesModel = nullptr; + QAction *m_refreshAction = nullptr; +}; + +#endif // ALPINEAPKSOURCESBACKEND_H diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp new file mode 100644 index 000000000..b4f90df56 --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#include "AlpineApkTransaction.h" +#include "AlpineApkBackend.h" +#include "AlpineApkResource.h" +#include "alpineapk_backend_logging.h" // generated by ECM +#include +#include +#include + +// #define TEST_PROCEED + +AlpineApkTransaction::AlpineApkTransaction(AlpineApkResource *app, Role role) + : AlpineApkTransaction(app, {}, role) +{ +} + +AlpineApkTransaction::AlpineApkTransaction(AlpineApkResource *app, const AddonList &addons, Transaction::Role role) + : Transaction(app->backend(), app, role, addons) + , m_app(app) +{ + setCancellable(true); + setStatus(DownloadingStatus); + iterateTransaction(); +} + +void AlpineApkTransaction::iterateTransaction() +{ + if (!m_iterate) { + return; + } + + if(progress() < 100) { + setProgress(qBound(0, progress() + (KRandom::random() % 30), 100)); + QTimer::singleShot(/*KRandom::random()%*/100, this, &AlpineApkTransaction::iterateTransaction); + } else if (status() == DownloadingStatus) { + setStatus(CommittingStatus); + QTimer::singleShot(/*KRandom::random()%*/100, this, &AlpineApkTransaction::iterateTransaction); + } else { + finishTransaction(); + } +} + +void AlpineApkTransaction::proceed() +{ + finishTransaction(); +} + +void AlpineApkTransaction::cancel() +{ + m_iterate = false; + + setStatus(CancelledStatus); +} + +void AlpineApkTransaction::finishTransaction() +{ + AbstractResource::State newState; + switch(role()) { + case InstallRole: + case ChangeAddonsRole: + newState = AbstractResource::Installed; + break; + case RemoveRole: + newState = AbstractResource::None; + break; + } + m_app->setAddons(addons()); + m_app->setState(newState); + setStatus(DoneStatus); + deleteLater(); +} diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.h b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.h new file mode 100644 index 000000000..63aeef8d7 --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef ALPINEAPKTRANSACTION_H +#define ALPINEAPKTRANSACTION_H + +#include + +class AlpineApkResource; +class AlpineApkTransaction : public Transaction +{ + Q_OBJECT + public: + AlpineApkTransaction(AlpineApkResource *app, Role role); + AlpineApkTransaction(AlpineApkResource *app, const AddonList &list, Role role); + + void cancel() override; + void proceed() override; + + private Q_SLOTS: + void iterateTransaction(); + void finishTransaction(); + + private: + bool m_iterate = true; + AlpineApkResource *m_app; +}; + +#endif // ALPINEAPKTRANSACTION_H diff --git a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt new file mode 100644 index 000000000..45a56eb63 --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt @@ -0,0 +1,52 @@ +# add_subdirectory(tests) # no tests yet + +set(alpineapkbackend_SRCS + AlpineApkBackend.cpp + AlpineApkBackend.h + AlpineApkResource.cpp + AlpineApkResource.h + AlpineApkSourcesBackend.cpp + AlpineApkSourcesBackend.h + AlpineApkTransaction.cpp + AlpineApkTransaction.h +) + +ecm_qt_declare_logging_category( + alpineapkbackend_SRCS # sources_var + HEADER alpineapk_backend_logging.h + IDENTIFIER LOG_ALPINEAPK + CATEGORY_NAME org.kde.plasma.discover.alpineapk + DEFAULT_SEVERITY Debug +) + +add_library( + alpineapk-backend + MODULE + ${alpineapkbackend_SRCS} +) + +target_link_libraries( + alpineapk-backend + PRIVATE + Qt5::Core + Qt5::Widgets + KF5::CoreAddons + KF5::ConfigCore + Discover::Common + apk-qt +) + +install( + TARGETS alpineapk-backend + DESTINATION ${PLUGIN_INSTALL_DIR}/discover +) + +# install(FILES alpineapk-backend-categories.xml DESTINATION ${DATA_INSTALL_DIR}/libdiscover/categories) + +# add_library(AlpineApkNotifier MODULE AlpineApkNotifier.cpp) + +# target_link_libraries(AlpineApkNotifier Discover::Notifiers) + +# set_target_properties(AlpineApkNotifier PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}/plasma-discover) + +# install(TARGETS AlpineApkNotifier DESTINATION ${PLUGIN_INSTALL_DIR}/discover-notifier) diff --git a/libdiscover/backends/CMakeLists.txt b/libdiscover/backends/CMakeLists.txt index bd3a03256..0807e7b75 100644 --- a/libdiscover/backends/CMakeLists.txt +++ b/libdiscover/backends/CMakeLists.txt @@ -54,3 +54,16 @@ option(BUILD_RpmOstreeBackend "Build rpm-ostree support." "ON") if(BUILD_RpmOstreeBackend AND Ostree_FOUND AND RpmOstree_FOUND) add_subdirectory(RpmOstreeBackend) endif() + +# Optional library +find_package(ApkQt CONFIG) +set_package_properties(ApkQt PROPERTIES + DESCRIPTION "C++/Qt interface library for Alpine package keeper" + URL "https://gitlab.com/postmarketOS/libapk-qt" + PURPOSE "Required to build the Alpine APK backend" + TYPE OPTIONAL) + +option(BUILD_AlpineApkBackend "Build Alpine APK support." "ON") +if(BUILD_AlpineApkBackend AND ApkQt_FOUND) + add_subdirectory(AlpineApkBackend) +endif() -- GitLab From a19a743a93156e9036652bb902d33f5bcd09475c Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Wed, 29 Jan 2020 17:35:17 +0300 Subject: [PATCH 02/61] CMake: Add AlpineApkReviewsBackend + its files --- .../AlpineApkReviewsBackend.cpp | 35 +++++++++++++ .../AlpineApkReviewsBackend.h | 52 +++++++++++++++++++ .../backends/AlpineApkBackend/CMakeLists.txt | 2 + 3 files changed, 89 insertions(+) create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkReviewsBackend.cpp create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkReviewsBackend.h diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkReviewsBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkReviewsBackend.cpp new file mode 100644 index 000000000..fd7ad47f2 --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkReviewsBackend.cpp @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#include "AlpineApkReviewsBackend.h" +#include "AlpineApkBackend.h" +#include "resources/AbstractResource.h" + +AlpineApkReviewsBackend::AlpineApkReviewsBackend(AlpineApkBackend *parent) + : AbstractReviewsBackend(parent) +{ +} + +void AlpineApkReviewsBackend::fetchReviews(AbstractResource *app, int page) +{ + Q_UNUSED(page) + static const QVector reviews; + Q_EMIT reviewsReady(app, reviews, false); +} diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkReviewsBackend.h b/libdiscover/backends/AlpineApkBackend/AlpineApkReviewsBackend.h new file mode 100644 index 000000000..435f845ba --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkReviewsBackend.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef ALPINEAPKREVIEWSBACKEND_H +#define ALPINEAPKREVIEWSBACKEND_H + +#include "ReviewsBackend/AbstractReviewsBackend.h" + +class AlpineApkBackend; + +class AlpineApkReviewsBackend : public AbstractReviewsBackend +{ + Q_OBJECT + +public: + explicit AlpineApkReviewsBackend(AlpineApkBackend *parent = nullptr); + + QString userName() const override { return QStringLiteral("dummy"); } + void login() override {} + void logout() override {} + void registerAndLogin() override {} + + Rating *ratingForApplication(AbstractResource *) const override { return nullptr; } + bool hasCredentials() const override { return false; } + void deleteReview(Review *) override {} + void fetchReviews(AbstractResource *app, int page = 1) override; + bool isFetching() const override { return false; } + bool isReviewable() const override { return false; } + void submitReview(AbstractResource *, const QString &, const QString &, const QString &) override {} + void flagReview(Review *, const QString&, const QString&) override {} + void submitUsefulness(Review *, bool) override {} + bool isResourceSupported(AbstractResource *) const override { return false; } +}; + +#endif // ALPINEAPKREVIEWSBACKEND_H diff --git a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt index 45a56eb63..7d2b86e82 100644 --- a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt +++ b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt @@ -5,6 +5,8 @@ set(alpineapkbackend_SRCS AlpineApkBackend.h AlpineApkResource.cpp AlpineApkResource.h + AlpineApkReviewsBackend.cpp + AlpineApkReviewsBackend.h AlpineApkSourcesBackend.cpp AlpineApkSourcesBackend.h AlpineApkTransaction.cpp -- GitLab From b964655d508438d3ec7669f45cbf5189e1cbd9ca Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Wed, 29 Jan 2020 17:35:43 +0300 Subject: [PATCH 03/61] AlpineApkBackend: use own reviews backend --- .../AlpineApkBackend/AlpineApkBackend.cpp | 15 +++++---------- .../backends/AlpineApkBackend/AlpineApkBackend.h | 4 ++-- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp index f5aa5477d..de93609ac 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -20,7 +20,7 @@ #include "AlpineApkBackend.h" #include "AlpineApkResource.h" -//#include "DummyReviewsBackend.h" +#include "AlpineApkReviewsBackend.h" #include "AlpineApkTransaction.h" #include "AlpineApkSourcesBackend.h" #include "alpineapk_backend_logging.h" // generated by ECM @@ -46,6 +46,7 @@ DISCOVER_BACKEND_PLUGIN(AlpineApkBackend) AlpineApkBackend::AlpineApkBackend(QObject *parent) : AbstractResourcesBackend(parent) , m_updater(new StandardBackendUpdater(this)) + , m_reviews(new AlpineApkReviewsBackend(this)) , m_startElements(120) { #ifndef QT_DEBUG @@ -55,12 +56,9 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) qCDebug(LOG_ALPINEAPK) << "constructing backend!"; QTimer::singleShot(500, this, &AlpineApkBackend::toggleFetching); - //connect(m_reviews, &DummyReviewsBackend::ratingsReady, this, &AbstractResourcesBackend::emitRatingsReady); connect(m_updater, &StandardBackendUpdater::updatesCountChanged, this, &AlpineApkBackend::updatesCountChanged); populate(); - //if (!m_fetching) - // m_reviews->initialize(); SourcesModel::global()->addSourcesBackend(new AlpineApkSourcesBackend(this)); } @@ -117,10 +115,9 @@ void AlpineApkBackend::populate() void AlpineApkBackend::toggleFetching() { m_fetching = !m_fetching; + qCDebug(LOG_ALPINEAPK) << "fetching..." << m_fetching; emit fetchingChanged(); - //if (!m_fetching) - // m_reviews->initialize(); } int AlpineApkBackend::updatesCount() const @@ -195,9 +192,7 @@ AbstractBackendUpdater *AlpineApkBackend::backendUpdater() const AbstractReviewsBackend *AlpineApkBackend::reviewsBackend() const { - // qCDebug(LOG_ALPINEAPK) << "reviewsBbackend(): we don't support reviews ("; - // return m_reviews; - return nullptr; + return m_reviews; } Transaction* AlpineApkBackend::installApplication(AbstractResource *app, const AddonList &addons) @@ -221,7 +216,7 @@ void AlpineApkBackend::checkForUpdates() qCDebug(LOG_ALPINEAPK) << "checkForUpdates(): already fetching"; return; } - qCDebug(LOG_ALPINEAPK) << "checkForUpdates()!"; + qCDebug(LOG_ALPINEAPK) << "checkForUpdates() start!"; toggleFetching(); // populate(QStringLiteral("Moar")); QTimer::singleShot(1000, this, &AlpineApkBackend::toggleFetching); diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h index 624df1c9f..f8d3aa69f 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h @@ -26,7 +26,7 @@ #include -// class DummyReviewsBackend; +class AlpineApkReviewsBackend; class StandardBackendUpdater; class AlpineApkResource; class AlpineApkBackend : public AbstractResourcesBackend @@ -62,7 +62,7 @@ private: QHash m_resources; StandardBackendUpdater *m_updater; - // DummyReviewsBackend* m_reviews; + AlpineApkReviewsBackend *m_reviews; QtApk::Database m_apkdb; QVector m_availablePackages; QVector m_installedPackages; -- GitLab From 6dc717adda44574fff7e4f6409cc7372d15c657c Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Wed, 29 Jan 2020 19:53:39 +0300 Subject: [PATCH 04/61] AlpineApkBackend: some cleanup on updates checking functions --- .../AlpineApkBackend/AlpineApkBackend.cpp | 41 ++++++++++++------- .../AlpineApkBackend/AlpineApkBackend.h | 5 ++- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp index de93609ac..41b2e0532 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -55,8 +55,10 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) qCDebug(LOG_ALPINEAPK) << "constructing backend!"; - QTimer::singleShot(500, this, &AlpineApkBackend::toggleFetching); - connect(m_updater, &StandardBackendUpdater::updatesCountChanged, this, &AlpineApkBackend::updatesCountChanged); + QTimer::singleShot(1000, this, &AlpineApkBackend::startCheckForUpdates); + + QObject::connect(m_updater, &StandardBackendUpdater::updatesCountChanged, + this, &AlpineApkBackend::updatesCountChanged); populate(); @@ -65,12 +67,14 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) QVector AlpineApkBackend::category() const { + // single root category + // we could add more, but Alpine apk does not have this concept static Category *cat = new Category( - QStringLiteral("All applications"), // displayName - QStringLiteral("applications-other"), // icon - { }, // orFilters + QStringLiteral("All packages"), // displayName + QStringLiteral("package-x-generic"), // icon + {}, // orFilters { displayName() }, // pluginName - { }, // subCategories + {}, // subCategories QUrl(), // decoration (what is it?) false // isAddons ); @@ -112,11 +116,23 @@ void AlpineApkBackend::populate() } } -void AlpineApkBackend::toggleFetching() +void AlpineApkBackend::startCheckForUpdates() { - m_fetching = !m_fetching; + if (m_fetching) { + return; + } + qCDebug(LOG_ALPINEAPK) << "startCheckForUpdates()"; + + m_fetching = true; + emit fetchingChanged(); + + // temporary hack - finish updates check in 5 seconds + QTimer::singleShot(5000, this, &AlpineApkBackend::finishCheckForUpdates); +} - qCDebug(LOG_ALPINEAPK) << "fetching..." << m_fetching; +void AlpineApkBackend::finishCheckForUpdates() +{ + m_fetching = false; emit fetchingChanged(); } @@ -212,14 +228,11 @@ Transaction* AlpineApkBackend::removeApplication(AbstractResource *app) void AlpineApkBackend::checkForUpdates() { - if(m_fetching) { + if (m_fetching) { qCDebug(LOG_ALPINEAPK) << "checkForUpdates(): already fetching"; return; } - qCDebug(LOG_ALPINEAPK) << "checkForUpdates() start!"; - toggleFetching(); - // populate(QStringLiteral("Moar")); - QTimer::singleShot(1000, this, &AlpineApkBackend::toggleFetching); + startCheckForUpdates(); } QString AlpineApkBackend::displayName() const diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h index f8d3aa69f..68a47bc92 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h @@ -55,7 +55,8 @@ public: bool hasApplications() const override; public Q_SLOTS: - void toggleFetching(); + void startCheckForUpdates(); + void finishCheckForUpdates(); private: void populate(); @@ -66,7 +67,7 @@ private: QtApk::Database m_apkdb; QVector m_availablePackages; QVector m_installedPackages; - bool m_fetching = true; + bool m_fetching = false; int m_startElements = 0; }; -- GitLab From 4680d6b7e04f35fb6ef50f883b6c994f26f9f537 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Wed, 29 Jan 2020 19:54:04 +0300 Subject: [PATCH 05/61] Add KAuth helper process for privileged operations --- .../AlpineApkBackend/AlpineApkAuthHelper.cpp | 56 +++++++++++++++++++ .../AlpineApkBackend/AlpineApkAuthHelper.h | 35 ++++++++++++ .../backends/AlpineApkBackend/CMakeLists.txt | 26 ++++++++- .../org.kde.discover.alpineapkbackend.actions | 5 ++ 4 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h create mode 100644 libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp new file mode 100644 index 000000000..6783f35cc --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "AlpineApkAuthHelper.h" + +using namespace KAuth; + +AlpineApkAuthHelper::AlpineApkAuthHelper() {} + +ActionReply AlpineApkAuthHelper::test_action(const QVariantMap &args) +{ + const QString txt = args[QStringLiteral("txt")].toString(); + + ActionReply reply = ActionReply::HelperErrorReply(); + QByteArray replyData(QByteArrayLiteral("ok")); + + QFile f(QStringLiteral("/lol.txt")); + if (f.open(QIODevice::ReadWrite | QIODevice::Text)) { + f.write(txt.toUtf8()); + f.close(); + + reply = ActionReply::SuccessReply(); + reply.setData({ + { QStringLiteral("reply"), replyData }, + }); + } + + return reply; +} + +KAUTH_HELPER_MAIN("org.kde.discover.alpineapkbackend", AlpineApkAuthHelper) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h new file mode 100644 index 000000000..86f7d3dcf --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#include +#include +#include + +using namespace KAuth; + +class AlpineApkAuthHelper : public QObject +{ + Q_OBJECT +public: + AlpineApkAuthHelper(); + +public Q_SLOTS: + ActionReply test_action(const QVariantMap &args); +}; diff --git a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt index 7d2b86e82..f67cf84f9 100644 --- a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt +++ b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt @@ -1,4 +1,4 @@ -# add_subdirectory(tests) # no tests yet +find_package(KF5Auth CONFIG REQUIRED) # Probably should be moved to top CMakeLists set(alpineapkbackend_SRCS AlpineApkBackend.cpp @@ -38,12 +38,34 @@ target_link_libraries( apk-qt ) +# KAuth helper exe +add_executable(alpineapk_kauth_helper + AlpineApkAuthHelper.cpp + AlpineApkAuthHelper.h + org.kde.discover.alpineapkbackend.actions +) +set_source_files_properties( + org.kde.discover.alpineapkbackend.actions + PROPERTIES HEADER_FILE_ONLY ON +) +target_link_libraries(alpineapk_kauth_helper + Qt5::Core + KF5::AuthCore + apk-qt +) + +kauth_install_actions(org.kde.discover.alpineapkbackend org.kde.discover.alpineapkbackend.actions) +kauth_install_helper_files(alpineapk_kauth_helper org.kde.discover.alpineapkbackend root) + install( TARGETS alpineapk-backend DESTINATION ${PLUGIN_INSTALL_DIR}/discover ) -# install(FILES alpineapk-backend-categories.xml DESTINATION ${DATA_INSTALL_DIR}/libdiscover/categories) +install( + TARGETS alpineapk_kauth_helper + DESTINATION ${KAUTH_HELPER_INSTALL_DIR} +) # add_library(AlpineApkNotifier MODULE AlpineApkNotifier.cpp) diff --git a/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions new file mode 100644 index 000000000..ba9ede912 --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions @@ -0,0 +1,5 @@ +[org.kde.discover.alpineapkbackend.test_action] +Name=Test Action +Description=Just test +Policy=auth_admin +Persistence=session -- GitLab From 73cac685b90592ac1b8d87102bd55f7d31e5d9c0 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Wed, 29 Jan 2020 20:41:07 +0300 Subject: [PATCH 06/61] AlpineApkBackend: WIP on using KAuth helper --- .../AlpineApkBackend/AlpineApkAuthHelper.cpp | 2 +- .../AlpineApkBackend/AlpineApkAuthHelper.h | 2 +- .../AlpineApkBackend/AlpineApkBackend.cpp | 34 +++++++++++++++++-- .../AlpineApkBackend/AlpineApkBackend.h | 3 ++ .../backends/AlpineApkBackend/CMakeLists.txt | 1 + .../org.kde.discover.alpineapkbackend.actions | 2 +- 6 files changed, 39 insertions(+), 5 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp index 6783f35cc..19236dcb3 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp @@ -32,7 +32,7 @@ using namespace KAuth; AlpineApkAuthHelper::AlpineApkAuthHelper() {} -ActionReply AlpineApkAuthHelper::test_action(const QVariantMap &args) +ActionReply AlpineApkAuthHelper::test(const QVariantMap &args) { const QString txt = args[QStringLiteral("txt")].toString(); diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h index 86f7d3dcf..947dcb7ac 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h @@ -31,5 +31,5 @@ public: AlpineApkAuthHelper(); public Q_SLOTS: - ActionReply test_action(const QVariantMap &args); + ActionReply test(const QVariantMap &args); }; diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp index 41b2e0532..ed016aef3 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -35,6 +35,8 @@ #include #include #include +#include + #include #include #include @@ -116,6 +118,19 @@ void AlpineApkBackend::populate() } } +void AlpineApkBackend::handleKauthHelperReply(KJob *job) +{ + qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; + KAuth::ExecuteJob* reply = static_cast(job); + const QVariantMap replyData = reply->data(); + if (reply->error() == 0) { + qCDebug(LOG_ALPINEAPK) << replyData[QLatin1String("reply")].toString(); + } else { + const QString message = replyData.value(QLatin1String("errorString"), reply->errorString()).toString(); + qCDebug(LOG_ALPINEAPK) << message; + } +} + void AlpineApkBackend::startCheckForUpdates() { if (m_fetching) { @@ -123,11 +138,26 @@ void AlpineApkBackend::startCheckForUpdates() } qCDebug(LOG_ALPINEAPK) << "startCheckForUpdates()"; + // temporary hack - finish updates check in 5 seconds + QTimer::singleShot(5000, this, &AlpineApkBackend::finishCheckForUpdates); + m_fetching = true; emit fetchingChanged(); - // temporary hack - finish updates check in 5 seconds - QTimer::singleShot(5000, this, &AlpineApkBackend::finishCheckForUpdates); + KAuth::Action testAction(QStringLiteral("org.kde.discover.alpineapkbackend.test")); + testAction.setHelperId(QStringLiteral("org.kde.discover.alpineapkbackend")); + testAction.setArguments({ + { QStringLiteral("txt"), QLatin1String("Wooo!") }, + }); + if (!testAction.isValid()) { + qCWarning(LOG_ALPINEAPK) << "kauth action is not valid!"; + return; + } + + KAuth::ExecuteJob *reply = testAction.execute(); + QObject::connect(reply, &KAuth::ExecuteJob::result, + this, &AlpineApkBackend::handleKauthHelperReply); + reply->start(); } void AlpineApkBackend::finishCheckForUpdates() diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h index 68a47bc92..a533cee39 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h @@ -29,6 +29,8 @@ class AlpineApkReviewsBackend; class StandardBackendUpdater; class AlpineApkResource; +class KJob; + class AlpineApkBackend : public AbstractResourcesBackend { Q_OBJECT @@ -55,6 +57,7 @@ public: bool hasApplications() const override; public Q_SLOTS: + void handleKauthHelperReply(KJob *job); void startCheckForUpdates(); void finishCheckForUpdates(); diff --git a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt index f67cf84f9..ede3157f0 100644 --- a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt +++ b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt @@ -34,6 +34,7 @@ target_link_libraries( Qt5::Widgets KF5::CoreAddons KF5::ConfigCore + KF5::AuthCore Discover::Common apk-qt ) diff --git a/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions index ba9ede912..3b9a3116e 100644 --- a/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions +++ b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions @@ -1,4 +1,4 @@ -[org.kde.discover.alpineapkbackend.test_action] +[org.kde.discover.alpineapkbackend.test] Name=Test Action Description=Just test Policy=auth_admin -- GitLab From 9f19dc5398a67ac64e28a9ccc16f378ff42a6862 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Mon, 3 Feb 2020 03:49:52 +0300 Subject: [PATCH 07/61] kath helper: add and implement update() and upgrade() actions --- .../AlpineApkBackend/AlpineApkAuthHelper.cpp | 97 +++++++++++++++++++ .../AlpineApkBackend/AlpineApkAuthHelper.h | 9 ++ .../org.kde.discover.alpineapkbackend.actions | 24 +++++ 3 files changed, 130 insertions(+) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp index 19236dcb3..4ff5c880a 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp @@ -20,14 +20,22 @@ #include #include +#include #include #include #include #include + #include #include "AlpineApkAuthHelper.h" +#ifdef QT_DEBUG +Q_LOGGING_CATEGORY(LOG_AUTHHELPER, "org.kde.discover.alpineapkbackend.authhelper", QtDebugMsg) +#else +Q_LOGGING_CATEGORY(LOG_AUTHHELPER, "org.kde.discover.alpineapkbackend.authhelper", QtWarningMsg) +#endif + using namespace KAuth; AlpineApkAuthHelper::AlpineApkAuthHelper() {} @@ -39,6 +47,7 @@ ActionReply AlpineApkAuthHelper::test(const QVariantMap &args) ActionReply reply = ActionReply::HelperErrorReply(); QByteArray replyData(QByteArrayLiteral("ok")); + // write some text file at the root directory as root, why not QFile f(QStringLiteral("/lol.txt")); if (f.open(QIODevice::ReadWrite | QIODevice::Text)) { f.write(txt.toUtf8()); @@ -53,4 +62,92 @@ ActionReply AlpineApkAuthHelper::test(const QVariantMap &args) return reply; } +ActionReply AlpineApkAuthHelper::update(const QVariantMap &args) +{ + Q_UNUSED(args) + ActionReply reply = ActionReply::HelperErrorReply(); + + HelperSupport::progressStep(10); + + if (!m_apkdb.open(QtApk::Database::QTAPK_OPENF_READWRITE)) { + reply.setErrorDescription(QStringLiteral("Failed to open database!")); + return reply; + } + + bool update_ok = m_apkdb.updatePackageIndex(); + + if (update_ok) { + int updatesCount = m_apkdb.upgradeablePackagesCount(); + reply = ActionReply::SuccessReply(); + reply.setData({ + { QLatin1String("updatesCount"), updatesCount } + }); + } else { + reply.setErrorDescription(QStringLiteral("Repo update failed!")); + reply.setData({ + { QLatin1String("errorString"), QStringLiteral("Repo update failed!") } + }); + } + + m_apkdb.close(); + HelperSupport::progressStep(100); + + return reply; +} + +ActionReply AlpineApkAuthHelper::add(const QVariantMap &args) +{ + ActionReply reply = ActionReply::HelperErrorReply(); + return reply; +} + +ActionReply AlpineApkAuthHelper::del(const QVariantMap &args) +{ + ActionReply reply = ActionReply::HelperErrorReply(); + return reply; +} + +ActionReply AlpineApkAuthHelper::upgrade(const QVariantMap &args) +{ + ActionReply reply = ActionReply::HelperErrorReply(); + + HelperSupport::progressStep(10); + + if (!m_apkdb.open(QtApk::Database::QTAPK_OPENF_READWRITE)) { + reply.setErrorDescription(QStringLiteral("Failed to open database!")); + return reply; + } + + bool onlySimulate = args.value(QLatin1String("onlySimulate"), false).toBool(); + QtApk::Database::DbUpgradeFlags flags = QtApk::Database::QTAPK_UPGRADE_DEFAULT; + if (onlySimulate) { + flags = QtApk::Database::QTAPK_UPGRADE_SIMULATE; + qCDebug(LOG_AUTHHELPER) << "Simulating upgrade run."; + } + + QtApk::Changeset changes; + bool upgrade_ok = m_apkdb.upgrade(flags, &changes); + + if (upgrade_ok) { + reply = ActionReply::SuccessReply(); + QVariantMap replyData; + const QVector ch = changes.changes(); + QVector chVector; + QVector pkgVector; + for (const QtApk::ChangesetItem &it: ch) { + pkgVector << it.newPackage; + } + replyData.insert(QLatin1String("changes"), QVariant::fromValue(pkgVector)); + replyData.insert(QLatin1String("onlySimulate"), onlySimulate); + reply.setData(replyData); + } else { + reply.setErrorDescription(QStringLiteral("Repo upgrade failed!")); + } + + m_apkdb.close(); + HelperSupport::progressStep(100); + + return reply; +} + KAUTH_HELPER_MAIN("org.kde.discover.alpineapkbackend", AlpineApkAuthHelper) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h index 947dcb7ac..5c06e5570 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h @@ -22,6 +22,8 @@ #include #include +#include + using namespace KAuth; class AlpineApkAuthHelper : public QObject @@ -32,4 +34,11 @@ public: public Q_SLOTS: ActionReply test(const QVariantMap &args); + ActionReply update(const QVariantMap &args); + ActionReply add(const QVariantMap &args); + ActionReply del(const QVariantMap &args); + ActionReply upgrade(const QVariantMap &args); + +private: + QtApk::Database m_apkdb; }; diff --git a/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions index 3b9a3116e..10305a1af 100644 --- a/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions +++ b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions @@ -3,3 +3,27 @@ Name=Test Action Description=Just test Policy=auth_admin Persistence=session + +[org.kde.discover.alpineapkbackend.update] +Name=Update repository index +Description=Updates available packages list from repositories +Policy=auth_admin +Persistence=session + +[org.kde.discover.alpineapkbackend.upgrade] +Name=Upgrade all upgradable packages +Description=Upgrade installed packages to latest versions +Policy=auth_admin +Persistence=session + +[org.kde.discover.alpineapkbackend.add] +Name=Install/upgrade package +Description=Installs/upgrades one package +Policy=auth_admin +Persistence=session + +[org.kde.discover.alpineapkbackend.del] +Name=Remove package +Description=Uninstall one package +Policy=auth_admin +Persistence=session -- GitLab From ccce553f6c33c6209dcfaeb702362731f98edb8b Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Mon, 3 Feb 2020 03:51:02 +0300 Subject: [PATCH 08/61] AlpineApkResource: add availableVersion field for updates --- .../backends/AlpineApkBackend/AlpineApkResource.cpp | 8 ++++++-- libdiscover/backends/AlpineApkBackend/AlpineApkResource.h | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp index d827e7b3b..af480b6c6 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp @@ -18,7 +18,6 @@ * along with this program. If not, see . * ***************************************************************************/ -#include #include "AlpineApkResource.h" #include "alpineapk_backend_logging.h" // generated by ECM #include "Transaction/AddonList.h" @@ -39,7 +38,7 @@ QList AlpineApkResource::addonsInformation() QString AlpineApkResource::availableVersion() const { - return m_pkg.version; + return m_availableVersion; } QStringList AlpineApkResource::categories() @@ -180,6 +179,11 @@ void AlpineApkResource::setAddonInstalled(const QString &addon, bool installed) } } +void AlpineApkResource::setAvailableVersion(const QString &av) +{ + m_availableVersion = av; +} + void AlpineApkResource::invokeApplication() const { diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h index 79b100e10..7140786c5 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h @@ -67,11 +67,13 @@ public: void setSection(const QString §ionName); void setAddons(const AddonList &addons); void setAddonInstalled(const QString &addon, bool installed); + void setAvailableVersion(const QString &av); public: AbstractResource::State m_state; const AbstractResource::Type m_type; QtApk::Package m_pkg; + QString m_availableVersion; QString m_category; QString m_originSoruce; QString m_sectionName; -- GitLab From 2bf11351bda1f2c1f28a937b704594221ca4f4cd Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Mon, 3 Feb 2020 03:51:58 +0300 Subject: [PATCH 09/61] Add AlpineApkUpdater class --- .../AlpineApkBackend/AlpineApkUpdater.cpp | 286 ++++++++++++++++++ .../AlpineApkBackend/AlpineApkUpdater.h | 190 ++++++++++++ .../backends/AlpineApkBackend/CMakeLists.txt | 2 + 3 files changed, 478 insertions(+) create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp new file mode 100644 index 000000000..1dd6cdacd --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp @@ -0,0 +1,286 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#include "AlpineApkUpdater.h" +#include "AlpineApkResource.h" +#include "AlpineApkBackend.h" +#include "alpineapk_backend_logging.h" +#include "utils.h" + +#include +#include + +#include + + +AlpineApkUpdater::AlpineApkUpdater(AbstractResourcesBackend *parent) + : AbstractBackendUpdater(parent) + , m_backend(static_cast(parent)) +{ + // +} + +void AlpineApkUpdater::prepare() +{ + qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; + + QtApk::Database *db = m_backend->apkdb(); + + if (db->isOpen()) { + return; + } + + if (!db->open(QtApk::Database::QTAPK_OPENF_READONLY)) { + emit passiveMessage(i18n("Failed to open APK database!")); + return; + } + + if (!db->upgrade(QtApk::Database::QTAPK_UPGRADE_SIMULATE, &m_upgradeable)) { + emit passiveMessage(i18n("Failed to get a list of packages to upgrade!")); + db->close(); + return; + } + // clsoe DB ASAP + db->close(); + + m_updatesCount = m_upgradeable.changes().size(); + qCDebug(LOG_ALPINEAPK) << "updater: prepare: updates count" << m_updatesCount; + + m_allUpdateable.clear(); + m_markedToUpdate.clear(); + QHash *resources = m_backend->resourcesPtr(); + for (const QtApk::ChangesetItem &it : qAsConst(m_upgradeable.changes())) { + const QtApk::Package &oldPkg = it.oldPackage; + const QString newVersion = it.newPackage.version; + AlpineApkResource *res = resources->value(oldPkg.name); + if (res) { + res->setAvailableVersion(newVersion); + m_allUpdateable.insert(res); + m_markedToUpdate.insert(res); + } + } + + // emitting this signal here leads to infinite recursion + // emit updatesCountChanged(m_updatesCount); +} + +bool AlpineApkUpdater::hasUpdates() const +{ + // qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO << m_updatesCount; + return (m_updatesCount > 0); +} + +qreal AlpineApkUpdater::progress() const +{ + qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; + return 0.0; +} + +void AlpineApkUpdater::removeResources(const QList &apps) +{ + // qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; + const QSet checkSet = kToSet(apps); + m_markedToUpdate -= checkSet; +} + +void AlpineApkUpdater::addResources(const QList &apps) +{ + //Q_UNUSED(apps) + //qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; + const QSet checkSet = kToSet(apps); + m_markedToUpdate += checkSet; +} + +QList AlpineApkUpdater::toUpdate() const +{ + // qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; + return m_allUpdateable.values(); +} + +QDateTime AlpineApkUpdater::lastUpdate() const +{ + qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; + return QDateTime(); +} + +bool AlpineApkUpdater::isCancelable() const +{ + qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; + return true; +} + +bool AlpineApkUpdater::isProgressing() const +{ + qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; + return false; +} + +bool AlpineApkUpdater::isMarked(AbstractResource *res) const +{ + // qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; + return m_markedToUpdate.contains(res); + // return true; +} + +void AlpineApkUpdater::fetchChangelog() const +{ + qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; +} + +double AlpineApkUpdater::updateSize() const +{ + // qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; + double sum = 0.0; + for (AbstractResource *res : m_markedToUpdate) { + sum += res->size(); + } + return sum; +} + +quint64 AlpineApkUpdater::downloadSpeed() const +{ + qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; + return 0; +} + +void AlpineApkUpdater::cancel() +{ + qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; +} + +void AlpineApkUpdater::start() +{ + qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; + return; +#if 0 + KAuth::Action upgradeAction(QStringLiteral("org.kde.discover.alpineapkbackend.upgrade")); + upgradeAction.setHelperId(QStringLiteral("org.kde.discover.alpineapkbackend")); + if (!upgradeAction.isValid()) { + qCWarning(LOG_ALPINEAPK) << "kauth upgradeAction is not valid!"; + return; + } + upgradeAction.setTimeout(60 * 1000); // 1 minute + upgradeAction.setDetails(i18n("Get the list of packages to upgrade")); + upgradeAction.addArgument(QLatin1String("onlySimulate"), true); + + // run upgrade check with elevated privileges + KAuth::ExecuteJob *reply = upgradeAction.execute(); + QObject::connect(reply, &KAuth::ExecuteJob::result, + this, &AlpineApkUpdater::handleKAuthUpgradeHelperReply); + + reply->start(); +#endif +} + +void AlpineApkUpdater::proceed() +{ + qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; +} + +int AlpineApkUpdater::updatesCount() +{ + // qDebug(LOG_ALPINEAPK) << Q_FUNC_INFO << m_updatesCount; + return m_updatesCount; +} + +void AlpineApkUpdater::startCheckForUpdates() +{ + KAuth::Action updateAction(QStringLiteral("org.kde.discover.alpineapkbackend.update")); + updateAction.setHelperId(QStringLiteral("org.kde.discover.alpineapkbackend")); + if (!updateAction.isValid()) { + qCWarning(LOG_ALPINEAPK) << "kauth updateAction is not valid!"; + return; + } + updateAction.setTimeout(60 * 1000); // 1 minute + updateAction.setDetails(i18n("Update repositories index")); + + // run updates check with elevated privileges to access + // system package manager files + KAuth::ExecuteJob *reply = updateAction.execute(); + QObject::connect(reply, &KAuth::ExecuteJob::result, + this, &AlpineApkUpdater::handleKAuthUpdateHelperReply); + //QObject::connect(reply, &KAuth::ExecuteJob::newData, + // this, &AlpineApkUpdater::handleKAuthUpdateHelperProgressStep); + QObject::connect(reply, QOverload::of(&KAuth::ExecuteJob::percent), + this, &AlpineApkUpdater::handleKAuthUpdateHelperProgress); + + reply->start(); +} + +void AlpineApkUpdater::handleKAuthUpdateHelperReply(KJob *job) +{ + KAuth::ExecuteJob *reply = static_cast(job); + const QVariantMap &replyData = reply->data(); + if (reply->error() == 0) { + m_updatesCount = replyData.value(QLatin1String("updatesCount")).toInt(); + qCDebug(LOG_ALPINEAPK) << "KAuth helper update reply received, updatesCount:" << m_updatesCount; + Q_EMIT updatesCountChanged(m_updatesCount); + } else { + const QString message = replyData.value(QLatin1String("errorString"), + reply->errorString()).toString(); + qCDebug(LOG_ALPINEAPK) << "KAuth helper returned error:" << message << reply->error(); + if (reply->error() == KAuth::ActionReply::Error::AuthorizationDeniedError) { + Q_EMIT passiveMessage(i18n("Authorization denied")); + } else { + Q_EMIT passiveMessage(i18n("Error") + QStringLiteral(":\n") + message); + } + } + + // we are not in the state "Fetching updates" now, update UI + Q_EMIT checkForUpdatesFinished(); +} + +void AlpineApkUpdater::handleKAuthUpdateHelperProgress(KJob *job, unsigned long percent) +{ + Q_UNUSED(job) + qCDebug(LOG_ALPINEAPK) << " fetch updates progress: " << percent; + Q_EMIT fetchingUpdatesProgressChanged(percent); +} + +void AlpineApkUpdater::handleKAuthUpgradeHelperReply(KJob *job) +{ + KAuth::ExecuteJob *reply = static_cast(job); + const QVariantMap &replyData = reply->data(); + if (reply->error() == 0) { + QVariant pkgsV = replyData.value(QLatin1String("changes")); + bool onlySimulate = replyData.value(QLatin1String("onlySimulate"), false).toBool(); + qCDebug(LOG_ALPINEAPK) << "KAuth helper upgrade reply received:" << onlySimulate; + if (onlySimulate) { + QVector pkgVector = pkgsV.value>(); + qCDebug(LOG_ALPINEAPK) << " num changes:" << pkgVector.size(); + for (const QtApk::Package &pkg : pkgVector) { + qCDebug(LOG_ALPINEAPK) << " " << pkg.name << pkg.version; + } + } + } else { + const QString message = replyData.value(QLatin1String("errorString"), + reply->errorString()).toString(); + qCDebug(LOG_ALPINEAPK) << "KAuth helper returned error:" << message << reply->error(); + if (reply->error() == KAuth::ActionReply::Error::AuthorizationDeniedError) { + Q_EMIT passiveMessage(i18n("Authorization denied")); + } else { + Q_EMIT passiveMessage(i18n("Error") + QStringLiteral(":\n") + message); + } + } + + // we are not in the state "Fetching updates" now, update UI + Q_EMIT checkForUpdatesFinished(); +} + diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h new file mode 100644 index 000000000..504e8c591 --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h @@ -0,0 +1,190 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef ALPINEAPKUPDATER_H +#define ALPINEAPKUPDATER_H + +#include "resources/AbstractBackendUpdater.h" +#include "resources/AbstractResourcesBackend.h" + +#include +#include +#include +#include +#include +#include + +#include + +class AbstractResourcesBackend; +class AlpineApkBackend; +class KJob; + +class AlpineApkUpdater : public AbstractBackendUpdater +{ + Q_OBJECT + Q_PROPERTY(int updatesCount READ updatesCount NOTIFY updatesCountChanged) + +public: + explicit AlpineApkUpdater(AbstractResourcesBackend *parent = nullptr); + + /** + * This method is called, when Muon switches to the updates view. + * Here the backend should mark all upgradeable packages as to be upgraded. + */ + void prepare() override; + + /** + * @returns true if the backend contains packages which can be updated + */ + bool hasUpdates() const override; + /** + * @returns the progress of the update in percent + */ + qreal progress() const override; + + /** + * This method is used to remove resources from the list of packages + * marked to be upgraded. It will potentially be called before \start. + */ + void removeResources(const QList &apps) override; + + /** + * This method is used to add resource to the list of packages marked to be upgraded. + * It will potentially be called before \start. + */ + void addResources(const QList &apps) override; + + /** + * @returns the list of updateable resources in the system + */ + QList toUpdate() const override; + + /** + * @returns the QDateTime when the last update happened + */ + QDateTime lastUpdate() const override; + + /** + * @returns whether the updater can currently be canceled or not + * @see cancelableChanged + */ + bool isCancelable() const override; + + /** + * @returns whether the updater is currently running or not + * this property decides, if there will be progress reporting in the GUI. + * This has to stay true during the whole transaction! + * @see progressingChanged + */ + bool isProgressing() const override; + + /** + * @returns whether @p res is marked for update + */ + bool isMarked(AbstractResource* res) const override; + + void fetchChangelog() const override; + + /** + * @returns the size of all the packages set to update combined + */ + double updateSize() const override; + + /** + * @returns the speed at which we are downloading + */ + quint64 downloadSpeed() const override; + +public Q_SLOTS: + /** + * If \isCancelable is true during the transaction, this method has + * to be implemented and will potentially be called when the user + * wants to cancel the update. + */ + void cancel() override; + + /** + * This method starts the update. All packages which are in \toUpdate + * are going to be updated. + * + * From this moment on the AbstractBackendUpdater should continuously update + * the other methods to show its progress. + * + * @see progress + * @see progressChanged + * @see isProgressing + * @see progressingChanged + */ + void start() override; + + /** + * Answers a proceed request + */ + void proceed() override; + +Q_SIGNALS: + void checkForUpdatesFinished(); + void updatesCountChanged(int updatesCount); + void fetchingUpdatesProgressChanged(int progress); + //void cancelTransaction(); + +public Q_SLOTS: + int updatesCount(); + void startCheckForUpdates(); + + // KAuth handler slots + // update + void handleKAuthUpdateHelperReply(KJob *job); + void handleKAuthUpdateHelperProgress(KJob *job, unsigned long percent); + // upgrade + void handleKAuthUpgradeHelperReply(KJob *job); + + //void transactionRemoved(Transaction* t); + //void cleanup(); + +public: + QVector &changes() { return m_upgradeable.changes(); } + const QVector &changes() const { return m_upgradeable.changes(); } + +private: + AlpineApkBackend *const m_backend; + int m_updatesCount = 0; + QtApk::Changeset m_upgradeable; + QSet m_allUpdateable; + QSet m_markedToUpdate; +// void resourcesChanged(AbstractResource* res, const QVector& props); +// void refreshUpdateable(); +// void transactionAdded(Transaction* newTransaction); +// void transactionProgressChanged(); +// void refreshProgress(); +// QVector transactions() const; + +// QSet m_upgradeable; +// QSet m_pendingResources; +// bool m_settingUp; +// qreal m_progress; +// QDateTime m_lastUpdate; +// QTimer m_timer; +// bool m_canCancel = false; +}; + + +#endif // ALPINEAPKUPDATER_H diff --git a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt index ede3157f0..381849453 100644 --- a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt +++ b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt @@ -9,6 +9,8 @@ set(alpineapkbackend_SRCS AlpineApkReviewsBackend.h AlpineApkSourcesBackend.cpp AlpineApkSourcesBackend.h + AlpineApkUpdater.cpp + AlpineApkUpdater.h AlpineApkTransaction.cpp AlpineApkTransaction.h ) -- GitLab From 15e57246c9602919660ee09ebdbd6e30631d1361 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Mon, 3 Feb 2020 03:52:41 +0300 Subject: [PATCH 10/61] AlpineApkBackend: use our new updater --- .../AlpineApkBackend/AlpineApkBackend.cpp | 158 ++++++++---------- .../AlpineApkBackend/AlpineApkBackend.h | 21 ++- 2 files changed, 85 insertions(+), 94 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp index ed016aef3..757380186 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -23,19 +23,14 @@ #include "AlpineApkReviewsBackend.h" #include "AlpineApkTransaction.h" #include "AlpineApkSourcesBackend.h" +#include "AlpineApkUpdater.h" #include "alpineapk_backend_logging.h" // generated by ECM -#include "resources/StandardBackendUpdater.h" #include "resources/SourcesModel.h" #include "Transaction/Transaction.h" #include "Category/Category.h" -#include #include -#include -#include -#include -#include #include #include @@ -47,45 +42,33 @@ DISCOVER_BACKEND_PLUGIN(AlpineApkBackend) AlpineApkBackend::AlpineApkBackend(QObject *parent) : AbstractResourcesBackend(parent) - , m_updater(new StandardBackendUpdater(this)) + , m_updater(new AlpineApkUpdater(this)) , m_reviews(new AlpineApkReviewsBackend(this)) - , m_startElements(120) + , m_updatesTimeoutTimer(new QTimer(this)) { #ifndef QT_DEBUG const_cast(LOG_ALPINEAPK()).setEnabled(QtDebugMsg, false); #endif - qCDebug(LOG_ALPINEAPK) << "constructing backend!"; + // schedule checking for updates + QTimer::singleShot(1000, this, &AlpineApkBackend::checkForUpdates); - QTimer::singleShot(1000, this, &AlpineApkBackend::startCheckForUpdates); - - QObject::connect(m_updater, &StandardBackendUpdater::updatesCountChanged, + // connections with our updater + QObject::connect(m_updater, &AlpineApkUpdater::updatesCountChanged, this, &AlpineApkBackend::updatesCountChanged); + QObject::connect(m_updater, &AlpineApkUpdater::checkForUpdatesFinished, + this, &AlpineApkBackend::finishCheckForUpdates); + QObject::connect(m_updater, &AlpineApkUpdater::fetchingUpdatesProgressChanged, + this, &AlpineApkBackend::setFetchingUpdatesProgress); - populate(); - - SourcesModel::global()->addSourcesBackend(new AlpineApkSourcesBackend(this)); -} - -QVector AlpineApkBackend::category() const -{ - // single root category - // we could add more, but Alpine apk does not have this concept - static Category *cat = new Category( - QStringLiteral("All packages"), // displayName - QStringLiteral("package-x-generic"), // icon - {}, // orFilters - { displayName() }, // pluginName - {}, // subCategories - QUrl(), // decoration (what is it?) - false // isAddons - ); - return { cat }; -} + // safety measure: make sure update check process can finish in some finite time + QObject::connect(m_updatesTimeoutTimer, &QTimer::timeout, + this, &AlpineApkBackend::finishCheckForUpdates); + m_updatesTimeoutTimer->setTimerType(Qt::CoarseTimer); + m_updatesTimeoutTimer->setSingleShot(true); + m_updatesTimeoutTimer->setInterval(2 * 60 * 1000); // 2minutes -void AlpineApkBackend::populate() -{ - qCDebug(LOG_ALPINEAPK) << "populating resources..."; + qCDebug(LOG_ALPINEAPK) << "backend: populating resources..."; if (m_apkdb.open(QtApk::Database::QTAPK_OPENF_READONLY)) { m_availablePackages = m_apkdb.getAvailablePackages(); @@ -103,7 +86,7 @@ void AlpineApkBackend::populate() m_resources.insert(key, res); connect(res, &AlpineApkResource::stateChanged, this, &AlpineApkBackend::updatesCountChanged); } - qCDebug(LOG_ALPINEAPK) << " available" << m_availablePackages.size() + qCDebug(LOG_ALPINEAPK) << " available" << m_availablePackages.size() << "packages"; } if (m_installedPackages.size() > 0) { @@ -113,62 +96,31 @@ void AlpineApkBackend::populate() m_resources.value(key)->setState(AbstractResource::Installed); } } - qCDebug(LOG_ALPINEAPK) << " installed" << m_installedPackages.size() + qCDebug(LOG_ALPINEAPK) << " installed" << m_installedPackages.size() << "packages"; } -} - -void AlpineApkBackend::handleKauthHelperReply(KJob *job) -{ - qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; - KAuth::ExecuteJob* reply = static_cast(job); - const QVariantMap replyData = reply->data(); - if (reply->error() == 0) { - qCDebug(LOG_ALPINEAPK) << replyData[QLatin1String("reply")].toString(); - } else { - const QString message = replyData.value(QLatin1String("errorString"), reply->errorString()).toString(); - qCDebug(LOG_ALPINEAPK) << message; - } -} - -void AlpineApkBackend::startCheckForUpdates() -{ - if (m_fetching) { - return; - } - qCDebug(LOG_ALPINEAPK) << "startCheckForUpdates()"; - - // temporary hack - finish updates check in 5 seconds - QTimer::singleShot(5000, this, &AlpineApkBackend::finishCheckForUpdates); - - m_fetching = true; - emit fetchingChanged(); - - KAuth::Action testAction(QStringLiteral("org.kde.discover.alpineapkbackend.test")); - testAction.setHelperId(QStringLiteral("org.kde.discover.alpineapkbackend")); - testAction.setArguments({ - { QStringLiteral("txt"), QLatin1String("Wooo!") }, - }); - if (!testAction.isValid()) { - qCWarning(LOG_ALPINEAPK) << "kauth action is not valid!"; - return; - } - KAuth::ExecuteJob *reply = testAction.execute(); - QObject::connect(reply, &KAuth::ExecuteJob::result, - this, &AlpineApkBackend::handleKauthHelperReply); - reply->start(); + SourcesModel::global()->addSourcesBackend(new AlpineApkSourcesBackend(this)); } -void AlpineApkBackend::finishCheckForUpdates() +QVector AlpineApkBackend::category() const { - m_fetching = false; - emit fetchingChanged(); + // single root category + // we could add more, but Alpine apk does not have this concept + static Category *cat = new Category( + i18nc("Root category name", "Alpine packages"), + QStringLiteral("package-x-generic"), // icon + {}, // orFilters + { displayName() }, // pluginName + {}, // subCategories + QUrl(), // decoration (what is it?) + false // isAddons + ); + return { cat }; } int AlpineApkBackend::updatesCount() const { - qCDebug(LOG_ALPINEAPK) << "updatesCount(): " << m_updater->updatesCount(); return m_updater->updatesCount(); } @@ -210,7 +162,7 @@ ResultsStream *AlpineApkBackend::findResourceByPackageName(const QUrl &searchUrl AlpineApkResource *result = nullptr; // QUrl("appstream://org.kde.krita.desktop") - // smart workaround for appstream + // smart workaround for appstream URLs if (searchUrl.scheme() == QLatin1String("appstream")) { // remove leading "org.kde." QString pkgName = searchUrl.host(); @@ -256,18 +208,46 @@ Transaction* AlpineApkBackend::removeApplication(AbstractResource *app) return new AlpineApkTransaction(qobject_cast(app), Transaction::RemoveRole); } +int AlpineApkBackend::fetchingUpdatesProgress() const +{ + if (!m_fetching) return 100; + return m_fetchProgress; +} + void AlpineApkBackend::checkForUpdates() { if (m_fetching) { - qCDebug(LOG_ALPINEAPK) << "checkForUpdates(): already fetching"; + qCDebug(LOG_ALPINEAPK) << "backend: checkForUpdates(): already fetching"; return; } - startCheckForUpdates(); + + qCDebug(LOG_ALPINEAPK) << "backend: start checkForUpdates()"; + + // safety measure - finish updates check in some time + m_updatesTimeoutTimer->start(); + + // let our updater do the job + m_updater->startCheckForUpdates(); + + // update UI + m_fetching = true; + m_fetchProgress = 0; + emit fetchingChanged(); + emit fetchingUpdatesProgressChanged(); +} + +void AlpineApkBackend::finishCheckForUpdates() +{ + m_updatesTimeoutTimer->stop(); // stop safety timer + // update UI + m_fetching = false; + emit fetchingChanged(); + emit fetchingUpdatesProgressChanged(); } QString AlpineApkBackend::displayName() const { - return QStringLiteral("Alpine APK backend"); + return i18nc("Backend plugin display name", "Alpine APK backend"); } bool AlpineApkBackend::hasApplications() const @@ -275,4 +255,10 @@ bool AlpineApkBackend::hasApplications() const return true; } +void AlpineApkBackend::setFetchingUpdatesProgress(int percent) +{ + m_fetchProgress = percent; + emit fetchingUpdatesProgressChanged(); +} + #include "AlpineApkBackend.moc" diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h index a533cee39..d69352575 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h @@ -27,14 +27,14 @@ #include class AlpineApkReviewsBackend; -class StandardBackendUpdater; +class AlpineApkUpdater; class AlpineApkResource; class KJob; +class QTimer; class AlpineApkBackend : public AbstractResourcesBackend { Q_OBJECT - Q_PROPERTY(int startElements MEMBER m_startElements) public: explicit AlpineApkBackend(QObject *parent = nullptr); @@ -46,32 +46,37 @@ public: ResultsStream *search(const AbstractResourcesBackend::Filters &filter) override; ResultsStream *findResourceByPackageName(const QUrl &search); QHash resources() const { return m_resources; } + QHash *resourcesPtr() { return &m_resources; } bool isValid() const override { return true; } // No external file dependencies that could cause runtime errors Transaction *installApplication(AbstractResource *app) override; Transaction *installApplication(AbstractResource *app, const AddonList &addons) override; Transaction *removeApplication(AbstractResource *app) override; bool isFetching() const override { return m_fetching; } + int fetchingUpdatesProgress() const override; void checkForUpdates() override; QString displayName() const override; bool hasApplications() const override; public Q_SLOTS: - void handleKauthHelperReply(KJob *job); - void startCheckForUpdates(); + void setFetchingUpdatesProgress(int percent); + +private Q_SLOTS: void finishCheckForUpdates(); -private: - void populate(); +public: + QtApk::Database *apkdb() { return &m_apkdb; } +private: QHash m_resources; - StandardBackendUpdater *m_updater; + AlpineApkUpdater *m_updater; AlpineApkReviewsBackend *m_reviews; QtApk::Database m_apkdb; QVector m_availablePackages; QVector m_installedPackages; bool m_fetching = false; - int m_startElements = 0; + int m_fetchProgress = 0; + QTimer *m_updatesTimeoutTimer; }; #endif // AlpineApkBackend_H -- GitLab From 82f6d00e2c2530b02a51e0f8377a07801c5b25eb Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Mon, 3 Feb 2020 18:21:04 +0300 Subject: [PATCH 11/61] AlpineApk{Updater,AuthHelper}: pass fakeRoot to helper Env vars are not inherited by elevated helper --- .../backends/AlpineApkBackend/AlpineApkAuthHelper.cpp | 11 ++++++++++- .../backends/AlpineApkBackend/AlpineApkUpdater.cpp | 5 ++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp index 4ff5c880a..aa3dbdae1 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp @@ -64,9 +64,13 @@ ActionReply AlpineApkAuthHelper::test(const QVariantMap &args) ActionReply AlpineApkAuthHelper::update(const QVariantMap &args) { - Q_UNUSED(args) ActionReply reply = ActionReply::HelperErrorReply(); + const QString fakeRoot = args.value(QLatin1String("fakeRoot"), QString()).toString(); + if (!fakeRoot.isEmpty()) { + m_apkdb.setFakeRoot(fakeRoot); + } + HelperSupport::progressStep(10); if (!m_apkdb.open(QtApk::Database::QTAPK_OPENF_READWRITE)) { @@ -125,6 +129,11 @@ ActionReply AlpineApkAuthHelper::upgrade(const QVariantMap &args) qCDebug(LOG_AUTHHELPER) << "Simulating upgrade run."; } + const QString fakeRoot = args.value(QLatin1String("fakeRoot"), QString()).toString(); + if (!fakeRoot.isEmpty()) { + m_apkdb.setFakeRoot(fakeRoot); + } + QtApk::Changeset changes; bool upgrade_ok = m_apkdb.upgrade(flags, &changes); diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp index 1dd6cdacd..d0c0482ef 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp @@ -47,6 +47,7 @@ void AlpineApkUpdater::prepare() return; } + // readonly is fine for a simulation of upgrade if (!db->open(QtApk::Database::QTAPK_OPENF_READONLY)) { emit passiveMessage(i18n("Failed to open APK database!")); return; @@ -57,7 +58,7 @@ void AlpineApkUpdater::prepare() db->close(); return; } - // clsoe DB ASAP + // close DB ASAP db->close(); m_updatesCount = m_upgradeable.changes().size(); @@ -202,6 +203,7 @@ int AlpineApkUpdater::updatesCount() void AlpineApkUpdater::startCheckForUpdates() { + QtApk::Database *db = m_backend->apkdb(); KAuth::Action updateAction(QStringLiteral("org.kde.discover.alpineapkbackend.update")); updateAction.setHelperId(QStringLiteral("org.kde.discover.alpineapkbackend")); if (!updateAction.isValid()) { @@ -210,6 +212,7 @@ void AlpineApkUpdater::startCheckForUpdates() } updateAction.setTimeout(60 * 1000); // 1 minute updateAction.setDetails(i18n("Update repositories index")); + updateAction.addArgument(QLatin1String("fakeRoot"), db->fakeRoot()); // run updates check with elevated privileges to access // system package manager files -- GitLab From 620045720ccb98d358833c9bdf3c9429ef5b27f7 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Mon, 3 Feb 2020 20:14:13 +0300 Subject: [PATCH 12/61] Auth helper: WIP on using progress_fd --- .../AlpineApkBackend/AlpineApkAuthHelper.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp index aa3dbdae1..4c3bf5458 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp @@ -21,9 +21,8 @@ #include #include #include -#include -#include -#include +#include +#include #include #include @@ -134,6 +133,14 @@ ActionReply AlpineApkAuthHelper::upgrade(const QVariantMap &args) m_apkdb.setFakeRoot(fakeRoot); } + int progress_fd = m_apkdb.progressFd(); + qCDebug(LOG_AUTHHELPER) << " progress_fd: " << progress_fd; + + QScopedPointer notifier(new QSocketNotifier(progress_fd, QSocketNotifier::Read)); + QObject::connect(notifier.data(), &QSocketNotifier::activated, notifier.data(), [](int sock) { + qCDebug(LOG_AUTHHELPER) << " read trigger from progress_fd!"; + }); + QtApk::Changeset changes; bool upgrade_ok = m_apkdb.upgrade(flags, &changes); -- GitLab From 46ebea78b0c2f193b8f3df02afc731343901e80c Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Fri, 20 Mar 2020 17:53:01 +0300 Subject: [PATCH 13/61] auth helper: fix usused vars --- libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp index 4c3bf5458..bc9c1ec67 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp @@ -100,12 +100,14 @@ ActionReply AlpineApkAuthHelper::update(const QVariantMap &args) ActionReply AlpineApkAuthHelper::add(const QVariantMap &args) { + Q_UNUSED(args) ActionReply reply = ActionReply::HelperErrorReply(); return reply; } ActionReply AlpineApkAuthHelper::del(const QVariantMap &args) { + Q_UNUSED(args) ActionReply reply = ActionReply::HelperErrorReply(); return reply; } @@ -138,6 +140,7 @@ ActionReply AlpineApkAuthHelper::upgrade(const QVariantMap &args) QScopedPointer notifier(new QSocketNotifier(progress_fd, QSocketNotifier::Read)); QObject::connect(notifier.data(), &QSocketNotifier::activated, notifier.data(), [](int sock) { + Q_UNUSED(sock) qCDebug(LOG_AUTHHELPER) << " read trigger from progress_fd!"; }); -- GitLab From 5eda2a8bfde96267dbbbf69db237093a8c3801b9 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Wed, 8 Apr 2020 05:55:33 +0300 Subject: [PATCH 14/61] AlpineApkBackend: cmake: use namespaced ApkQt --- libdiscover/backends/AlpineApkBackend/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt index 381849453..42e09c5b6 100644 --- a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt +++ b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt @@ -38,7 +38,7 @@ target_link_libraries( KF5::ConfigCore KF5::AuthCore Discover::Common - apk-qt + ApkQt::apk-qt ) # KAuth helper exe @@ -54,7 +54,7 @@ set_source_files_properties( target_link_libraries(alpineapk_kauth_helper Qt5::Core KF5::AuthCore - apk-qt + ApkQt::apk-qt ) kauth_install_actions(org.kde.discover.alpineapkbackend org.kde.discover.alpineapkbackend.actions) -- GitLab From 40c0d3b188b576fba86d7e96946ec4a31b1bdf28 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Thu, 9 Apr 2020 21:18:33 +0300 Subject: [PATCH 15/61] AlpineApkBackend: mark pkgs as Technical type and fix searhes This makes updates appear as "System updates" in updates view and also makes them correctly categorized in top level categories when other backend is installed (Flatpak, KNewStuff) --- .../AlpineApkBackend/AlpineApkBackend.cpp | 62 ++++++++++++------- .../AlpineApkBackend/AlpineApkResource.cpp | 2 +- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp index 757380186..750d2820c 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -79,9 +79,12 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) if (m_availablePackages.size() > 0) { for (const QtApk::Package &pkg: m_availablePackages) { AlpineApkResource *res = new AlpineApkResource(pkg, this); - res->setCategoryName(QStringLiteral("all")); + res->setCategoryName(QStringLiteral("alpine_packages")); res->setOriginSource(QStringLiteral("apk")); res->setSection(QStringLiteral("dummy")); + // here is the place to set a proper type of package + // AlpineApkResource defaults to AbstractResource::Technical, + // which places it into "System updates" section const QString key = pkg.name.toLower(); m_resources.insert(key, res); connect(res, &AlpineApkResource::stateChanged, this, &AlpineApkBackend::updatesCountChanged); @@ -105,18 +108,22 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) QVector AlpineApkBackend::category() const { - // single root category + static QPair s_apkFlt( + FilterType::CategoryFilter, QLatin1String("alpine_packages")); + + // Display a single root category // we could add more, but Alpine apk does not have this concept - static Category *cat = new Category( - i18nc("Root category name", "Alpine packages"), - QStringLiteral("package-x-generic"), // icon - {}, // orFilters - { displayName() }, // pluginName - {}, // subCategories - QUrl(), // decoration (what is it?) - false // isAddons + static Category *s_rootCat = new Category( + i18nc("Root category name", "Alpine packages"), + QStringLiteral("package-x-generic"), // icon + { s_apkFlt }, // orFilters - include packages that match filter + { displayName() }, // pluginName + {}, // subCategories - none + QUrl(), // decoration (what is it?) + false // isAddons ); - return { cat }; + + return { s_rootCat }; } int AlpineApkBackend::updatesCount() const @@ -130,17 +137,23 @@ ResultsStream *AlpineApkBackend::search(const AbstractResourcesBackend::Filters if (!filter.resourceUrl.isEmpty()) { return findResourceByPackageName(filter.resourceUrl); } else { - for (AbstractResource *r: qAsConst(m_resources)) { - if (r->type() == AbstractResource::Technical - && filter.state != AbstractResource::Upgradeable) { - continue; - } - if (r->state() < filter.state) { + for (AbstractResource *resource: qAsConst(m_resources)) { + // skip technical package types (not apps/addons) + // that are not upgradeable + // (does not work because for now all Alpine packages are "technical" + // if (resource->type() == AbstractResource::Technical + // && filter.state != AbstractResource::Upgradeable) { + // continue; + // } + + // skip not-requested states + if (resource->state() < filter.state) { continue; } - if(r->name().contains(filter.search, Qt::CaseInsensitive) - || r->comment().contains(filter.search, Qt::CaseInsensitive)) { - ret += r; + + if(resource->name().contains(filter.search, Qt::CaseInsensitive) + || resource->comment().contains(filter.search, Qt::CaseInsensitive)) { + ret += resource; } } } @@ -162,7 +175,7 @@ ResultsStream *AlpineApkBackend::findResourceByPackageName(const QUrl &searchUrl AlpineApkResource *result = nullptr; // QUrl("appstream://org.kde.krita.desktop") - // smart workaround for appstream URLs + // smart workaround for appstream URLs - handle "featured" apps if (searchUrl.scheme() == QLatin1String("appstream")) { // remove leading "org.kde." QString pkgName = searchUrl.host(); @@ -177,6 +190,13 @@ ResultsStream *AlpineApkBackend::findResourceByPackageName(const QUrl &searchUrl result = m_resources.value(pkgName); } + // QUrl("apk://krita") + // handle packages from Alpine repos + if (searchUrl.scheme() == QLatin1String("apk")) { + const QString pkgName = searchUrl.host(); + result = m_resources.value(pkgName); + } + if (!result) { return new ResultsStream(QStringLiteral("AlpineApkStream"), {}); } diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp index af480b6c6..346a28b28 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp @@ -26,7 +26,7 @@ AlpineApkResource::AlpineApkResource(const QtApk::Package &apkPkg, AbstractResourcesBackend *parent) : AbstractResource(parent) , m_state(AbstractResource::State::None) - , m_type(Application) + , m_type(AbstractResource::Type::Technical) , m_pkg(apkPkg) { } -- GitLab From 3d7f28c6cafa50edf98213275029e2fb85a7c5cc Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Fri, 10 Apr 2020 09:00:29 +0300 Subject: [PATCH 16/61] KAuth helper: disable progress_fd for now --- .../AlpineApkBackend/AlpineApkAuthHelper.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp index bc9c1ec67..11e267866 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp @@ -26,6 +26,7 @@ #include #include +#include #include "AlpineApkAuthHelper.h" @@ -135,14 +136,15 @@ ActionReply AlpineApkAuthHelper::upgrade(const QVariantMap &args) m_apkdb.setFakeRoot(fakeRoot); } - int progress_fd = m_apkdb.progressFd(); - qCDebug(LOG_AUTHHELPER) << " progress_fd: " << progress_fd; + // no progress notifications for now + //int progress_fd = m_apkdb.progressFd(); + //qCDebug(LOG_AUTHHELPER) << " progress_fd: " << progress_fd; - QScopedPointer notifier(new QSocketNotifier(progress_fd, QSocketNotifier::Read)); - QObject::connect(notifier.data(), &QSocketNotifier::activated, notifier.data(), [](int sock) { - Q_UNUSED(sock) - qCDebug(LOG_AUTHHELPER) << " read trigger from progress_fd!"; - }); + //QScopedPointer notifier(new QSocketNotifier(progress_fd, QSocketNotifier::Read)); + //QObject::connect(notifier.data(), &QSocketNotifier::activated, notifier.data(), [](int sock) { + // Q_UNUSED(sock) + // qCDebug(LOG_AUTHHELPER) << " read trigger from progress_fd!"; + //}); QtApk::Changeset changes; bool upgrade_ok = m_apkdb.upgrade(flags, &changes); -- GitLab From 075c2ed48177f6a6449ef5a3564724bc89eab092 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Fri, 10 Apr 2020 09:06:17 +0300 Subject: [PATCH 17/61] AlpineApkUpdater: refactor out helper error handler func --- .../AlpineApkBackend/AlpineApkUpdater.cpp | 37 +++++++++---------- .../AlpineApkBackend/AlpineApkUpdater.h | 6 +++ 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp index d0c0482ef..173b7e48c 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp @@ -215,12 +215,11 @@ void AlpineApkUpdater::startCheckForUpdates() updateAction.addArgument(QLatin1String("fakeRoot"), db->fakeRoot()); // run updates check with elevated privileges to access - // system package manager files + // system package manager files KAuth::ExecuteJob *reply = updateAction.execute(); QObject::connect(reply, &KAuth::ExecuteJob::result, this, &AlpineApkUpdater::handleKAuthUpdateHelperReply); - //QObject::connect(reply, &KAuth::ExecuteJob::newData, - // this, &AlpineApkUpdater::handleKAuthUpdateHelperProgressStep); + // qOverload is needed because of conflict with getter named percent() QObject::connect(reply, QOverload::of(&KAuth::ExecuteJob::percent), this, &AlpineApkUpdater::handleKAuthUpdateHelperProgress); @@ -236,14 +235,7 @@ void AlpineApkUpdater::handleKAuthUpdateHelperReply(KJob *job) qCDebug(LOG_ALPINEAPK) << "KAuth helper update reply received, updatesCount:" << m_updatesCount; Q_EMIT updatesCountChanged(m_updatesCount); } else { - const QString message = replyData.value(QLatin1String("errorString"), - reply->errorString()).toString(); - qCDebug(LOG_ALPINEAPK) << "KAuth helper returned error:" << message << reply->error(); - if (reply->error() == KAuth::ActionReply::Error::AuthorizationDeniedError) { - Q_EMIT passiveMessage(i18n("Authorization denied")); - } else { - Q_EMIT passiveMessage(i18n("Error") + QStringLiteral(":\n") + message); - } + handleKAuthHelperError(reply, replyData); } // we are not in the state "Fetching updates" now, update UI @@ -273,17 +265,24 @@ void AlpineApkUpdater::handleKAuthUpgradeHelperReply(KJob *job) } } } else { - const QString message = replyData.value(QLatin1String("errorString"), - reply->errorString()).toString(); - qCDebug(LOG_ALPINEAPK) << "KAuth helper returned error:" << message << reply->error(); - if (reply->error() == KAuth::ActionReply::Error::AuthorizationDeniedError) { - Q_EMIT passiveMessage(i18n("Authorization denied")); - } else { - Q_EMIT passiveMessage(i18n("Error") + QStringLiteral(":\n") + message); - } + handleKAuthHelperError(reply, replyData); } // we are not in the state "Fetching updates" now, update UI Q_EMIT checkForUpdatesFinished(); } +void AlpineApkUpdater::handleKAuthHelperError( + KAuth::ExecuteJob *reply, + const QVariantMap &replyData) +{ + const QString message = replyData.value(QLatin1String("errorString"), + reply->errorString()).toString(); + qCDebug(LOG_ALPINEAPK) << "KAuth helper returned error:" << message << reply->error(); + if (reply->error() == KAuth::ActionReply::Error::AuthorizationDeniedError) { + Q_EMIT passiveMessage(i18n("Authorization denied")); + } else { + Q_EMIT passiveMessage(i18n("Error") + QStringLiteral(":\n") + message); + } +} + diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h index 504e8c591..77140ca25 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h @@ -36,6 +36,9 @@ class AbstractResourcesBackend; class AlpineApkBackend; class KJob; +namespace KAuth { + class ExecuteJob; +} class AlpineApkUpdater : public AbstractBackendUpdater { @@ -164,6 +167,9 @@ public: QVector &changes() { return m_upgradeable.changes(); } const QVector &changes() const { return m_upgradeable.changes(); } +protected: + void handleKAuthHelperError(KAuth::ExecuteJob *reply, const QVariantMap &replyData); + private: AlpineApkBackend *const m_backend; int m_updatesCount = 0; -- GitLab From 669b64558b7ac5c72b8ea20bc507f89194c2edc1 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Fri, 10 Apr 2020 09:07:38 +0300 Subject: [PATCH 18/61] AlpineApkUpdater: use non-deprecated version of setDetails(V2) since KF 5.68 --- .../AlpineApkBackend/AlpineApkUpdater.cpp | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp index 173b7e48c..dae6c2a31 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp @@ -25,6 +25,7 @@ #include "utils.h" #include +#include #include #include @@ -178,8 +179,15 @@ void AlpineApkUpdater::start() return; } upgradeAction.setTimeout(60 * 1000); // 1 minute - upgradeAction.setDetails(i18n("Get the list of packages to upgrade")); - upgradeAction.addArgument(QLatin1String("onlySimulate"), true); +#if KAUTH_VERSION < QT_VERSION_CHECK(5, 68, 0) + upgradeAction.setDetails(i18n("Upgrade currently installed packages")); +#else + static const KAuth::Action::DetailsMap details{ + { KAuth::Action::AuthDetail::DetailMessage, i18n("Upgrade currently installed packages") } + }; + upgradeAction.setDetailsV2(details); +#endif + // upgradeAction.addArgument(QLatin1String("onlySimulate"), true); // run upgrade check with elevated privileges KAuth::ExecuteJob *reply = upgradeAction.execute(); @@ -211,7 +219,15 @@ void AlpineApkUpdater::startCheckForUpdates() return; } updateAction.setTimeout(60 * 1000); // 1 minute + // setDetails deprecated since KF 5.68, use setDetailsV2() with DetailsMap. +#if KAUTH_VERSION < QT_VERSION_CHECK(5, 68, 0) updateAction.setDetails(i18n("Update repositories index")); +#else + static const KAuth::Action::DetailsMap details{ + { KAuth::Action::AuthDetail::DetailMessage, i18n("Update repositories index") } + }; + updateAction.setDetailsV2(details); +#endif updateAction.addArgument(QLatin1String("fakeRoot"), db->fakeRoot()); // run updates check with elevated privileges to access -- GitLab From 919af82f2b582c70039b90f7ffc6c744b03d3ac6 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Fri, 10 Apr 2020 09:09:13 +0300 Subject: [PATCH 19/61] WIP: AlpineApkUpdater: enable upgrade action again --- .../backends/AlpineApkBackend/AlpineApkUpdater.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp index dae6c2a31..89a816c44 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp @@ -170,15 +170,17 @@ void AlpineApkUpdater::cancel() void AlpineApkUpdater::start() { qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; - return; -#if 0 + //return; +//#if 0 KAuth::Action upgradeAction(QStringLiteral("org.kde.discover.alpineapkbackend.upgrade")); upgradeAction.setHelperId(QStringLiteral("org.kde.discover.alpineapkbackend")); + if (!upgradeAction.isValid()) { qCWarning(LOG_ALPINEAPK) << "kauth upgradeAction is not valid!"; return; } - upgradeAction.setTimeout(60 * 1000); // 1 minute + + upgradeAction.setTimeout(30 * 60 * 1000); // 30 min #if KAUTH_VERSION < QT_VERSION_CHECK(5, 68, 0) upgradeAction.setDetails(i18n("Upgrade currently installed packages")); #else @@ -189,13 +191,13 @@ void AlpineApkUpdater::start() #endif // upgradeAction.addArgument(QLatin1String("onlySimulate"), true); - // run upgrade check with elevated privileges + // run upgrade with elevated privileges KAuth::ExecuteJob *reply = upgradeAction.execute(); QObject::connect(reply, &KAuth::ExecuteJob::result, this, &AlpineApkUpdater::handleKAuthUpgradeHelperReply); reply->start(); -#endif +//#endif } void AlpineApkUpdater::proceed() @@ -272,7 +274,7 @@ void AlpineApkUpdater::handleKAuthUpgradeHelperReply(KJob *job) if (reply->error() == 0) { QVariant pkgsV = replyData.value(QLatin1String("changes")); bool onlySimulate = replyData.value(QLatin1String("onlySimulate"), false).toBool(); - qCDebug(LOG_ALPINEAPK) << "KAuth helper upgrade reply received:" << onlySimulate; + qCDebug(LOG_ALPINEAPK) << "KAuth helper upgrade reply received, onlySimulate:" << onlySimulate; if (onlySimulate) { QVector pkgVector = pkgsV.value>(); qCDebug(LOG_ALPINEAPK) << " num changes:" << pkgVector.size(); -- GitLab From 293fe5e14ad3e4812dd764bb834c207297f4b31f Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Thu, 11 Jun 2020 22:39:09 +0300 Subject: [PATCH 20/61] Adapt to changes in libapk-qt 0.3 * Rename link target to ApkQt::ApkQt * rename header include => --- .../backends/AlpineApkBackend/AlpineApkAuthHelper.cpp | 8 ++++---- .../backends/AlpineApkBackend/AlpineApkAuthHelper.h | 2 +- .../backends/AlpineApkBackend/AlpineApkBackend.cpp | 2 +- libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h | 2 +- .../backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp | 2 +- .../backends/AlpineApkBackend/AlpineApkUpdater.cpp | 6 +++--- libdiscover/backends/AlpineApkBackend/CMakeLists.txt | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp index 11e267866..0496c8f28 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp @@ -73,7 +73,7 @@ ActionReply AlpineApkAuthHelper::update(const QVariantMap &args) HelperSupport::progressStep(10); - if (!m_apkdb.open(QtApk::Database::QTAPK_OPENF_READWRITE)) { + if (!m_apkdb.open(QtApk::QTAPK_OPENF_READWRITE)) { reply.setErrorDescription(QStringLiteral("Failed to open database!")); return reply; } @@ -119,15 +119,15 @@ ActionReply AlpineApkAuthHelper::upgrade(const QVariantMap &args) HelperSupport::progressStep(10); - if (!m_apkdb.open(QtApk::Database::QTAPK_OPENF_READWRITE)) { + if (!m_apkdb.open(QtApk::QTAPK_OPENF_READWRITE)) { reply.setErrorDescription(QStringLiteral("Failed to open database!")); return reply; } bool onlySimulate = args.value(QLatin1String("onlySimulate"), false).toBool(); - QtApk::Database::DbUpgradeFlags flags = QtApk::Database::QTAPK_UPGRADE_DEFAULT; + QtApk::DbUpgradeFlags flags = QtApk::QTAPK_UPGRADE_DEFAULT; if (onlySimulate) { - flags = QtApk::Database::QTAPK_UPGRADE_SIMULATE; + flags = QtApk::QTAPK_UPGRADE_SIMULATE; qCDebug(LOG_AUTHHELPER) << "Simulating upgrade run."; } diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h index 5c06e5570..a634ce233 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h @@ -22,7 +22,7 @@ #include #include -#include +#include using namespace KAuth; diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp index 750d2820c..a089afea4 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -70,7 +70,7 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) qCDebug(LOG_ALPINEAPK) << "backend: populating resources..."; - if (m_apkdb.open(QtApk::Database::QTAPK_OPENF_READONLY)) { + if (m_apkdb.open(QtApk::QTAPK_OPENF_READONLY)) { m_availablePackages = m_apkdb.getAvailablePackages(); m_installedPackages = m_apkdb.getInstalledPackages(); m_apkdb.close(); diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h index d69352575..755cf6a5a 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h @@ -24,7 +24,7 @@ #include #include -#include +#include class AlpineApkReviewsBackend; class AlpineApkUpdater; diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp index b964b40d1..225fb4436 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp @@ -29,7 +29,7 @@ #include // libapk-qt -#include +#include AlpineApkSourcesBackend::AlpineApkSourcesBackend(AbstractResourcesBackend *parent) : AbstractSourcesBackend(parent) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp index 89a816c44..0083f340d 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp @@ -28,7 +28,7 @@ #include #include -#include +#include AlpineApkUpdater::AlpineApkUpdater(AbstractResourcesBackend *parent) @@ -49,12 +49,12 @@ void AlpineApkUpdater::prepare() } // readonly is fine for a simulation of upgrade - if (!db->open(QtApk::Database::QTAPK_OPENF_READONLY)) { + if (!db->open(QtApk::QTAPK_OPENF_READONLY)) { emit passiveMessage(i18n("Failed to open APK database!")); return; } - if (!db->upgrade(QtApk::Database::QTAPK_UPGRADE_SIMULATE, &m_upgradeable)) { + if (!db->upgrade(QtApk::QTAPK_UPGRADE_SIMULATE, &m_upgradeable)) { emit passiveMessage(i18n("Failed to get a list of packages to upgrade!")); db->close(); return; diff --git a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt index 42e09c5b6..319c7ad2f 100644 --- a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt +++ b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt @@ -38,7 +38,7 @@ target_link_libraries( KF5::ConfigCore KF5::AuthCore Discover::Common - ApkQt::apk-qt + ApkQt::ApkQt ) # KAuth helper exe @@ -54,7 +54,7 @@ set_source_files_properties( target_link_libraries(alpineapk_kauth_helper Qt5::Core KF5::AuthCore - ApkQt::apk-qt + ApkQt::ApkQt ) kauth_install_actions(org.kde.discover.alpineapkbackend org.kde.discover.alpineapkbackend.actions) -- GitLab From 0266f6289bbfc6946244316b69775a32a361a011 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sun, 28 Jun 2020 03:38:15 +0300 Subject: [PATCH 21/61] AlpineApkAuthHelper: implement all operations in async mode also delete useless test() action --- .../AlpineApkBackend/AlpineApkAuthHelper.cpp | 234 ++++++++++++------ .../AlpineApkBackend/AlpineApkAuthHelper.h | 19 +- .../org.kde.discover.alpineapkbackend.actions | 6 - 3 files changed, 181 insertions(+), 78 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp index 0496c8f28..a3236011f 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp @@ -40,88 +40,194 @@ using namespace KAuth; AlpineApkAuthHelper::AlpineApkAuthHelper() {} -ActionReply AlpineApkAuthHelper::test(const QVariantMap &args) +bool AlpineApkAuthHelper::openDatabase(const QVariantMap &args, bool readwrite) { - const QString txt = args[QStringLiteral("txt")].toString(); - - ActionReply reply = ActionReply::HelperErrorReply(); - QByteArray replyData(QByteArrayLiteral("ok")); + // maybe set fakeRoot (needs to be done before Database::open() + const QString fakeRoot = args.value(QLatin1String("fakeRoot"), QString()).toString(); + if (!fakeRoot.isEmpty()) { + m_apkdb.setFakeRoot(fakeRoot); + } - // write some text file at the root directory as root, why not - QFile f(QStringLiteral("/lol.txt")); - if (f.open(QIODevice::ReadWrite | QIODevice::Text)) { - f.write(txt.toUtf8()); - f.close(); + // calculate flags to use during open + QtApk::DbOpenFlags fl = QtApk::QTAPK_OPENF_ENABLE_PROGRESSFD; + if (readwrite) { + fl |= QtApk::QTAPK_OPENF_READWRITE; + } - reply = ActionReply::SuccessReply(); - reply.setData({ - { QStringLiteral("reply"), replyData }, - }); + if (!m_apkdb.open(fl)) { + return false; } + return true; +} - return reply; +void AlpineApkAuthHelper::closeDatabase() +{ + m_apkdb.close(); } -ActionReply AlpineApkAuthHelper::update(const QVariantMap &args) +void AlpineApkAuthHelper::setupTransactionPostCreate(QtApk::Transaction *trans) { - ActionReply reply = ActionReply::HelperErrorReply(); + // receive progress notifications + QObject::connect(trans, &QtApk::Transaction::progressChanged, + this, &AlpineApkAuthHelper::reportProgress); - const QString fakeRoot = args.value(QLatin1String("fakeRoot"), QString()).toString(); - if (!fakeRoot.isEmpty()) { - m_apkdb.setFakeRoot(fakeRoot); - } + // receive error messages + QObject::connect(trans, &QtApk::Transaction::errorOccured, + this, &AlpineApkAuthHelper::onTransactionError); - HelperSupport::progressStep(10); + // what to do when transaction is complete + QObject::connect(trans, &QtApk::Transaction::finished, + this, &AlpineApkAuthHelper::onTransactionFinished); +} - if (!m_apkdb.open(QtApk::QTAPK_OPENF_READWRITE)) { - reply.setErrorDescription(QStringLiteral("Failed to open database!")); - return reply; +void AlpineApkAuthHelper::reportProgress(float percent) +{ + int p = static_cast(percent); + if (p < 0) p = 0; + if (p > 100) p = 100; + HelperSupport::progressStep(p); +} + +void AlpineApkAuthHelper::onTransactionError(const QString &msg) +{ + qCWarning(LOG_AUTHHELPER).nospace() << "ERROR occured in transaction \"" + << m_currentTransaction->desc() + << "\": " << msg; + const QString errMsg = m_currentTransaction->desc() + QLatin1String(" failed: ") + msg; + m_actionReply.setErrorDescription(errMsg); + m_actionReply.setData({ + { QLatin1String("errorString"), errMsg } + }); + m_trans_ok = false; +} + +void AlpineApkAuthHelper::onTransactionFinished() +{ + m_lastChangeset = m_currentTransaction->changeset(); + m_currentTransaction->deleteLater(); + m_currentTransaction = nullptr; + m_loop.quit(); +} + +ActionReply AlpineApkAuthHelper::update(const QVariantMap &args) +{ + // return error by default + m_actionReply = ActionReply::HelperErrorReply(); + + HelperSupport::progressStep(0); + + if (!openDatabase(args)) { + m_actionReply.setErrorDescription(QStringLiteral("Failed to open database!")); + return m_actionReply; } - bool update_ok = m_apkdb.updatePackageIndex(); + m_trans_ok = true; + QtApk::Transaction *trans = m_apkdb.updatePackageIndex(); + setupTransactionPostCreate(trans); - if (update_ok) { + trans->start(); + m_loop.exec(); + + if (m_trans_ok) { int updatesCount = m_apkdb.upgradeablePackagesCount(); - reply = ActionReply::SuccessReply(); - reply.setData({ + m_actionReply = ActionReply::SuccessReply(); + m_actionReply.setData({ { QLatin1String("updatesCount"), updatesCount } }); - } else { - reply.setErrorDescription(QStringLiteral("Repo update failed!")); - reply.setData({ - { QLatin1String("errorString"), QStringLiteral("Repo update failed!") } - }); } - m_apkdb.close(); + closeDatabase(); HelperSupport::progressStep(100); - return reply; + return m_actionReply; } ActionReply AlpineApkAuthHelper::add(const QVariantMap &args) { - Q_UNUSED(args) - ActionReply reply = ActionReply::HelperErrorReply(); - return reply; + // return error by default + m_actionReply = ActionReply::HelperErrorReply(); + + HelperSupport::progressStep(0); + + if (!openDatabase(args)) { + m_actionReply.setErrorDescription(QStringLiteral("Failed to open database!")); + return m_actionReply; + } + + const QString pkgName = args.value(QLatin1String("pkgName"), QString()).toString(); + if (pkgName.isEmpty()) { + m_actionReply.setErrorDescription(QStringLiteral("Specify pkgName for adding!")); + return m_actionReply; + } + + m_trans_ok = true; + QtApk::Transaction *trans = m_apkdb.add(pkgName); + setupTransactionPostCreate(trans); + + trans->start(); + m_loop.exec(); + + if (m_trans_ok) { + m_actionReply = ActionReply::SuccessReply(); + } + + closeDatabase(); + HelperSupport::progressStep(100); + + return m_actionReply; } ActionReply AlpineApkAuthHelper::del(const QVariantMap &args) { - Q_UNUSED(args) - ActionReply reply = ActionReply::HelperErrorReply(); - return reply; + // return error by default + m_actionReply = ActionReply::HelperErrorReply(); + + HelperSupport::progressStep(0); + + if (!openDatabase(args)) { + m_actionReply.setErrorDescription(QStringLiteral("Failed to open database!")); + return m_actionReply; + } + + const QString pkgName = args.value(QLatin1String("pkgName"), QString()).toString(); + if (pkgName.isEmpty()) { + m_actionReply.setErrorDescription(QStringLiteral("Specify pkgName for removing!")); + return m_actionReply; + } + + const bool delRdepends = args.value(QLatin1String("delRdepends"), false).toBool(); + + QtApk::DbDelFlags delFlags = QtApk::QTAPK_DEL_DEFAULT; + if (delRdepends) { + delFlags = QtApk::QTAPK_DEL_RDEPENDS; + } + + m_trans_ok = true; + QtApk::Transaction *trans = m_apkdb.del(pkgName, delFlags); + setupTransactionPostCreate(trans); + + trans->start(); + m_loop.exec(); + + if (m_trans_ok) { + m_actionReply = ActionReply::SuccessReply(); + } + + closeDatabase(); + HelperSupport::progressStep(100); + + return m_actionReply; } ActionReply AlpineApkAuthHelper::upgrade(const QVariantMap &args) { - ActionReply reply = ActionReply::HelperErrorReply(); + m_actionReply = ActionReply::HelperErrorReply(); - HelperSupport::progressStep(10); + HelperSupport::progressStep(0); - if (!m_apkdb.open(QtApk::QTAPK_OPENF_READWRITE)) { - reply.setErrorDescription(QStringLiteral("Failed to open database!")); - return reply; + if (!openDatabase(args)) { + m_actionReply.setErrorDescription(QStringLiteral("Failed to open database!")); + return m_actionReply; } bool onlySimulate = args.value(QLatin1String("onlySimulate"), false).toBool(); @@ -131,28 +237,18 @@ ActionReply AlpineApkAuthHelper::upgrade(const QVariantMap &args) qCDebug(LOG_AUTHHELPER) << "Simulating upgrade run."; } - const QString fakeRoot = args.value(QLatin1String("fakeRoot"), QString()).toString(); - if (!fakeRoot.isEmpty()) { - m_apkdb.setFakeRoot(fakeRoot); - } - - // no progress notifications for now - //int progress_fd = m_apkdb.progressFd(); - //qCDebug(LOG_AUTHHELPER) << " progress_fd: " << progress_fd; + m_trans_ok = true; - //QScopedPointer notifier(new QSocketNotifier(progress_fd, QSocketNotifier::Read)); - //QObject::connect(notifier.data(), &QSocketNotifier::activated, notifier.data(), [](int sock) { - // Q_UNUSED(sock) - // qCDebug(LOG_AUTHHELPER) << " read trigger from progress_fd!"; - //}); + QtApk::Transaction *trans = m_apkdb.upgrade(flags); + setupTransactionPostCreate(trans); - QtApk::Changeset changes; - bool upgrade_ok = m_apkdb.upgrade(flags, &changes); + trans->start(); + m_loop.exec(); - if (upgrade_ok) { - reply = ActionReply::SuccessReply(); + if (m_trans_ok) { + m_actionReply = ActionReply::SuccessReply(); QVariantMap replyData; - const QVector ch = changes.changes(); + const QVector ch = m_lastChangeset.changes(); QVector chVector; QVector pkgVector; for (const QtApk::ChangesetItem &it: ch) { @@ -160,15 +256,13 @@ ActionReply AlpineApkAuthHelper::upgrade(const QVariantMap &args) } replyData.insert(QLatin1String("changes"), QVariant::fromValue(pkgVector)); replyData.insert(QLatin1String("onlySimulate"), onlySimulate); - reply.setData(replyData); - } else { - reply.setErrorDescription(QStringLiteral("Repo upgrade failed!")); + m_actionReply.setData(replyData); } - m_apkdb.close(); + closeDatabase(); HelperSupport::progressStep(100); - return reply; + return m_actionReply; } KAUTH_HELPER_MAIN("org.kde.discover.alpineapkbackend", AlpineApkAuthHelper) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h index a634ce233..a670a2aa1 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h @@ -18,6 +18,7 @@ * along with this program. If not, see . * ***************************************************************************/ +#include #include #include #include @@ -33,12 +34,26 @@ public: AlpineApkAuthHelper(); public Q_SLOTS: - ActionReply test(const QVariantMap &args); ActionReply update(const QVariantMap &args); ActionReply add(const QVariantMap &args); ActionReply del(const QVariantMap &args); ActionReply upgrade(const QVariantMap &args); +protected: + bool openDatabase(const QVariantMap &args, bool readwrite = true); + void closeDatabase(); + void setupTransactionPostCreate(QtApk::Transaction *trans); + +protected Q_SLOTS: + void reportProgress(float percent); + void onTransactionError(const QString &msg); + void onTransactionFinished(); + private: - QtApk::Database m_apkdb; + QtApk::DatabaseAsync m_apkdb; // runs transactions in bg thread + QtApk::Transaction *m_currentTransaction = nullptr; + QEventLoop m_loop; // event loop that will run and wait while bg transaction is in progress + ActionReply m_actionReply; // return value for main action slots + bool m_trans_ok = true; // flag to indicate if bg transaction was successful + QtApk::Changeset m_lastChangeset; // changeset from last completed transaction }; diff --git a/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions index 10305a1af..0755c4157 100644 --- a/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions +++ b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions @@ -1,9 +1,3 @@ -[org.kde.discover.alpineapkbackend.test] -Name=Test Action -Description=Just test -Policy=auth_admin -Persistence=session - [org.kde.discover.alpineapkbackend.update] Name=Update repository index Description=Updates available packages list from repositories -- GitLab From de82618fb4cf7a1e135baea349ffd6bcb84b3cde Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Mon, 29 Jun 2020 03:22:42 +0300 Subject: [PATCH 22/61] AlpineApkAuthHelper: add method to write repositories config --- .../AlpineApkBackend/AlpineApkAuthHelper.cpp | 21 +++++++++++++++++++ .../AlpineApkBackend/AlpineApkAuthHelper.h | 1 + .../org.kde.discover.alpineapkbackend.actions | 6 ++++++ 3 files changed, 28 insertions(+) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp index a3236011f..81c18255c 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp @@ -265,4 +265,25 @@ ActionReply AlpineApkAuthHelper::upgrade(const QVariantMap &args) return m_actionReply; } +ActionReply AlpineApkAuthHelper::repoconfig(const QVariantMap &args) +{ + m_actionReply = ActionReply::HelperErrorReply(); + HelperSupport::progressStep(10); + + if (args.contains(QLatin1String("repoList"))) { + const QVariant v = args.value(QLatin1String("repoList")); + const QVector repoVec = v.value>(); + if (QtApk::Database::saveRepositories(repoVec)) { + m_actionReply = ActionReply::SuccessReply(); // OK + } else { + m_actionReply.setErrorDescription(QStringLiteral("Failed to write repositories config!")); + } + } else { + m_actionReply.setErrorDescription(QStringLiteral("repoList parameter is missing in request!")); + } + + HelperSupport::progressStep(100); + return m_actionReply; +} + KAUTH_HELPER_MAIN("org.kde.discover.alpineapkbackend", AlpineApkAuthHelper) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h index a670a2aa1..acce56488 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h @@ -38,6 +38,7 @@ public Q_SLOTS: ActionReply add(const QVariantMap &args); ActionReply del(const QVariantMap &args); ActionReply upgrade(const QVariantMap &args); + ActionReply repoconfig(const QVariantMap &args); protected: bool openDatabase(const QVariantMap &args, bool readwrite = true); diff --git a/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions index 0755c4157..f1bffe654 100644 --- a/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions +++ b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions @@ -21,3 +21,9 @@ Name=Remove package Description=Uninstall one package Policy=auth_admin Persistence=session + +[org.kde.discover.alpineapkbackend.repoconfig] +Name=Remove package +Description=Configure repositories URLs +Policy=auth_admin +Persistence=session -- GitLab From 0cbe2f3f96dfd72cf451187da4b8b95261912908 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Mon, 29 Jun 2020 06:52:42 +0300 Subject: [PATCH 23/61] AlpineApkAuthHelper: create event loop only when needed It fixes error "QEventLoop: requires QApplication", this makes it creation delayed to moment *after* QCoreApplication object is created by KAuthCore internals. --- .../AlpineApkBackend/AlpineApkAuthHelper.cpp | 14 +++++++++----- .../AlpineApkBackend/AlpineApkAuthHelper.h | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp index 81c18255c..b436aa73b 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp @@ -78,6 +78,10 @@ void AlpineApkAuthHelper::setupTransactionPostCreate(QtApk::Transaction *trans) // what to do when transaction is complete QObject::connect(trans, &QtApk::Transaction::finished, this, &AlpineApkAuthHelper::onTransactionFinished); + + if (!m_loop) { + m_loop = new QEventLoop(this); + } } void AlpineApkAuthHelper::reportProgress(float percent) @@ -106,7 +110,7 @@ void AlpineApkAuthHelper::onTransactionFinished() m_lastChangeset = m_currentTransaction->changeset(); m_currentTransaction->deleteLater(); m_currentTransaction = nullptr; - m_loop.quit(); + m_loop->quit(); } ActionReply AlpineApkAuthHelper::update(const QVariantMap &args) @@ -126,7 +130,7 @@ ActionReply AlpineApkAuthHelper::update(const QVariantMap &args) setupTransactionPostCreate(trans); trans->start(); - m_loop.exec(); + m_loop->exec(); if (m_trans_ok) { int updatesCount = m_apkdb.upgradeablePackagesCount(); @@ -165,7 +169,7 @@ ActionReply AlpineApkAuthHelper::add(const QVariantMap &args) setupTransactionPostCreate(trans); trans->start(); - m_loop.exec(); + m_loop->exec(); if (m_trans_ok) { m_actionReply = ActionReply::SuccessReply(); @@ -207,7 +211,7 @@ ActionReply AlpineApkAuthHelper::del(const QVariantMap &args) setupTransactionPostCreate(trans); trans->start(); - m_loop.exec(); + m_loop->exec(); if (m_trans_ok) { m_actionReply = ActionReply::SuccessReply(); @@ -243,7 +247,7 @@ ActionReply AlpineApkAuthHelper::upgrade(const QVariantMap &args) setupTransactionPostCreate(trans); trans->start(); - m_loop.exec(); + m_loop->exec(); if (m_trans_ok) { m_actionReply = ActionReply::SuccessReply(); diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h index acce56488..eed39f283 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h @@ -53,7 +53,7 @@ protected Q_SLOTS: private: QtApk::DatabaseAsync m_apkdb; // runs transactions in bg thread QtApk::Transaction *m_currentTransaction = nullptr; - QEventLoop m_loop; // event loop that will run and wait while bg transaction is in progress + QEventLoop *m_loop = nullptr; // event loop that will run and wait while bg transaction is in progress ActionReply m_actionReply; // return value for main action slots bool m_trans_ok = true; // flag to indicate if bg transaction was successful QtApk::Changeset m_lastChangeset; // changeset from last completed transaction -- GitLab From ed22afd4b4880b213e7f78cb7e9a014604c2e782 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Mon, 29 Jun 2020 06:55:59 +0300 Subject: [PATCH 24/61] AlpineApkAuthHelper: fix nullptr deref in onTransactionFinished m_currentTransaction was never assigned --- libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp index b436aa73b..f5d8e17e5 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp @@ -67,6 +67,8 @@ void AlpineApkAuthHelper::closeDatabase() void AlpineApkAuthHelper::setupTransactionPostCreate(QtApk::Transaction *trans) { + m_currentTransaction = trans; // remember current transaction here + // receive progress notifications QObject::connect(trans, &QtApk::Transaction::progressChanged, this, &AlpineApkAuthHelper::reportProgress); -- GitLab From 752de647cf2fd54361abed43288f0200e9e4c028 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Tue, 30 Jun 2020 05:39:42 +0300 Subject: [PATCH 25/61] AlpineApkSourcesBackend: fully functional sources editor * allows enable/disable repo * allows to add/remove repo * allows to change sources order * allows to reload/save changes --- .../AlpineApkSourcesBackend.cpp | 130 +++++++++++++----- .../AlpineApkSourcesBackend.h | 7 + 2 files changed, 102 insertions(+), 35 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp index 225fb4436..28f08ef86 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp @@ -26,6 +26,8 @@ #include // KF5 +#include +#include #include // libapk-qt @@ -35,16 +37,19 @@ AlpineApkSourcesBackend::AlpineApkSourcesBackend(AbstractResourcesBackend *paren : AbstractSourcesBackend(parent) , m_sourcesModel(new QStandardItemModel(this)) , m_refreshAction(new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), - QStringLiteral("Refresh"), this)) + QStringLiteral("Reload"), this)) + , m_saveAction(new QAction(QIcon::fromTheme(QStringLiteral("document-save")), + QStringLiteral("Save"), this)) + // ^^ unfortunately QML side ignores icons for custom actions { loadSources(); QObject::connect(m_refreshAction, &QAction::triggered, this, &AlpineApkSourcesBackend::loadSources); - - // can be used to track enabling/disabling repo source - // QObject::connect(m_sourcesModel, &QStandardItemModel::itemChanged, this, [](QStandardItem* item) { - // qCDebug(LOG_ALPINEAPK) << "source backend: DummySource changed" << item << item->checkState(); - // }); + QObject::connect(m_saveAction, &QAction::triggered, + this, &AlpineApkSourcesBackend::saveSources); + // track enabling/disabling repo source + QObject::connect(m_sourcesModel, &QStandardItemModel::itemChanged, + this, &AlpineApkSourcesBackend::onItemChanged); } QAbstractItemModel *AlpineApkSourcesBackend::sources() @@ -52,48 +57,97 @@ QAbstractItemModel *AlpineApkSourcesBackend::sources() return m_sourcesModel; } +QStandardItem *AlpineApkSourcesBackend::sourceForId(const QString& id) const +{ + for (int i = 0; i < m_sourcesModel->rowCount(); ++i) { + QStandardItem *item = m_sourcesModel->item(i, 0); + if (item->data(AbstractSourcesBackend::IdRole) == id) { + return item; + } + } + return nullptr; +} + bool AlpineApkSourcesBackend::addSource(const QString &id) { - return addSourceFull(id, QString(), true); + m_repos.append(QtApk::Repository(id, QString(), true)); + fillModelFromRepos(); + return true; } -QStandardItem *AlpineApkSourcesBackend::sourceForId(const QString& id) const +void AlpineApkSourcesBackend::loadSources() +{ + m_repos = QtApk::Database::getRepositories(); + fillModelFromRepos(); +} + +void AlpineApkSourcesBackend::fillModelFromRepos() { - for (int i = 0, c = m_sourcesModel->rowCount(); i < c; ++i) { - QStandardItem *it = m_sourcesModel->item(i, 0); - if (it->text() == id) { - return it; + m_sourcesModel->clear(); + for (const QtApk::Repository &repo: m_repos) { + if (repo.url.isEmpty()) { + continue; } + qCDebug(LOG_ALPINEAPK) << "source backend: Adding source:" << repo.url << repo.enabled; + QStandardItem *it = new QStandardItem(repo.url); + it->setData(repo.url, AbstractSourcesBackend::IdRole); + it->setData(repo.comment, Qt::ToolTipRole); + it->setCheckable(true); + it->setCheckState(repo.enabled ? Qt::Checked : Qt::Unchecked); + m_sourcesModel->appendRow(it); } - return nullptr; } -bool AlpineApkSourcesBackend::addSourceFull(const QString &id, const QString &comment, bool enabled) +void AlpineApkSourcesBackend::saveSources() { - if (id.isEmpty()) { - return false; + KAuth::Action repoConfigAction(QStringLiteral("org.kde.discover.alpineapkbackend.repoconfig")); + repoConfigAction.setHelperId(QStringLiteral("org.kde.discover.alpineapkbackend")); + if (!repoConfigAction.isValid()) { + qCWarning(LOG_ALPINEAPK) << "repoConfigAction is not valid!"; + return; } - qCDebug(LOG_ALPINEAPK) << "source backend: Adding source:" << id; + repoConfigAction.setTimeout(1 * 60 * 1000); // 1 min +#if KAUTH_VERSION < QT_VERSION_CHECK(5, 68, 0) + upgradeAction.setDetails(i18n("Configure repositories URLs")); +#else + static const KAuth::Action::DetailsMap details{ + { KAuth::Action::AuthDetail::DetailMessage, i18n("Configure repositories URLs") } + }; + repoConfigAction.setDetailsV2(details); +#endif + // pass in new repositories list + repoConfigAction.addArgument(QLatin1String("repoList"), + QVariant::fromValue>(m_repos)); + + // run with elevated privileges + KAuth::ExecuteJob *reply = repoConfigAction.execute(); + QObject::connect(reply, &KAuth::ExecuteJob::result, this, [this] (KJob *job) { + KAuth::ExecuteJob *reply = static_cast(job); + if (reply->error() != 0) { + const QString errMessage = reply->errorString(); + qCWarning(LOG_ALPINEAPK) << "KAuth helper returned error:" + << reply->error() << errMessage; + if (reply->error() == KAuth::ActionReply::Error::AuthorizationDeniedError) { + Q_EMIT passiveMessage(i18n("Authorization denied")); + } else { + Q_EMIT passiveMessage(i18n("Error: ") + errMessage); + } + } + this->loadSources(); + }); - QStandardItem *it = new QStandardItem(id); - it->setData(id, AbstractSourcesBackend::IdRole); - it->setData(comment, Qt::ToolTipRole); - it->setCheckable(true); - it->setCheckState(enabled ? Qt::Checked : Qt::Unchecked); - // for now, disable editing sources - it->setFlags(it->flags() & ~Qt::ItemIsEnabled); - m_sourcesModel->appendRow(it); - return true; + reply->start(); } -void AlpineApkSourcesBackend::loadSources() +void AlpineApkSourcesBackend::onItemChanged(QStandardItem *item) { - QVector repos = QtApk::Database::getRepositories(); - m_sourcesModel->clear(); - for (const QtApk::Repository &repo: repos) { - addSourceFull(repo.url, repo.comment, repo.enabled); - } + // update internal storage vector and relaod model from it + // otherwise checks state are not updated in UI + const Qt::CheckState cs = item->checkState(); + const QModelIndex idx = m_sourcesModel->indexFromItem(item); + m_repos[idx.row()].enabled = (cs == Qt::Checked); + fillModelFromRepos(); } bool AlpineApkSourcesBackend::removeSource(const QString &id) @@ -103,18 +157,20 @@ bool AlpineApkSourcesBackend::removeSource(const QString &id) qCWarning(LOG_ALPINEAPK) << "source backend: couldn't find " << id; return false; } + m_repos.remove(it->row()); return m_sourcesModel->removeRow(it->row()); } QString AlpineApkSourcesBackend::idDescription() { - return i18nc("Adding repo", "Enter apk repository URL, for example: " + return i18nc("Adding repo", "Enter Alpine repository URL, for example: " "http://dl-cdn.alpinelinux.org/alpine/edge/testing/"); } QVariantList AlpineApkSourcesBackend::actions() const { static const QVariantList s_actions { + QVariant::fromValue(m_saveAction), QVariant::fromValue(m_refreshAction), }; return s_actions; @@ -122,12 +178,12 @@ QVariantList AlpineApkSourcesBackend::actions() const bool AlpineApkSourcesBackend::supportsAdding() const { - return false; // for now, disable editing sources + return true; } bool AlpineApkSourcesBackend::canMoveSources() const { - return false; // for now, disable editing sources + return true; } bool AlpineApkSourcesBackend::moveSource(const QString& sourceId, int delta) @@ -147,5 +203,9 @@ bool AlpineApkSourcesBackend::moveSource(const QString& sourceId, int delta) || row == (m_sourcesModel->rowCount() - 1)) { Q_EMIT lastSourceIdChanged(); } + + // swap also items in internal storage vector + m_repos.swapItemsAt(row, destRow); + return true; } diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.h b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.h index 578945917..eacda22dc 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.h @@ -24,6 +24,8 @@ #include #include +#include + class AlpineApkSourcesBackend : public AbstractSourcesBackend { public: @@ -42,9 +44,14 @@ private: QStandardItem *sourceForId(const QString &id) const; bool addSourceFull(const QString &id, const QString &comment, bool enabled); void loadSources(); + void saveSources(); + void fillModelFromRepos(); + void onItemChanged(QStandardItem* item); QStandardItemModel *m_sourcesModel = nullptr; QAction *m_refreshAction = nullptr; + QAction *m_saveAction = nullptr; + QVector m_repos; }; #endif // ALPINEAPKSOURCESBACKEND_H -- GitLab From 8197270ee138ad47c98d9bf5723d09b2c9d9ea01 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Wed, 1 Jul 2020 08:34:10 +0300 Subject: [PATCH 26/61] AlpineApkUpdater: update progressing status --- .../AlpineApkBackend/AlpineApkUpdater.cpp | 25 +++++++++++-------- .../AlpineApkBackend/AlpineApkUpdater.h | 2 +- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp index 0083f340d..2f3d184fb 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp @@ -125,20 +125,18 @@ QDateTime AlpineApkUpdater::lastUpdate() const bool AlpineApkUpdater::isCancelable() const { qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; - return true; + return false; } bool AlpineApkUpdater::isProgressing() const { - qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; - return false; + qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO << m_progressing; + return m_progressing; } bool AlpineApkUpdater::isMarked(AbstractResource *res) const { - // qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; return m_markedToUpdate.contains(res); - // return true; } void AlpineApkUpdater::fetchChangelog() const @@ -148,7 +146,6 @@ void AlpineApkUpdater::fetchChangelog() const double AlpineApkUpdater::updateSize() const { - // qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; double sum = 0.0; for (AbstractResource *res : m_markedToUpdate) { sum += res->size(); @@ -170,8 +167,6 @@ void AlpineApkUpdater::cancel() void AlpineApkUpdater::start() { qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; - //return; -//#if 0 KAuth::Action upgradeAction(QStringLiteral("org.kde.discover.alpineapkbackend.upgrade")); upgradeAction.setHelperId(QStringLiteral("org.kde.discover.alpineapkbackend")); @@ -196,8 +191,10 @@ void AlpineApkUpdater::start() QObject::connect(reply, &KAuth::ExecuteJob::result, this, &AlpineApkUpdater::handleKAuthUpgradeHelperReply); + m_progressing = true; + Q_EMIT progressingChanged(m_progressing); + reply->start(); -//#endif } void AlpineApkUpdater::proceed() @@ -207,7 +204,7 @@ void AlpineApkUpdater::proceed() int AlpineApkUpdater::updatesCount() { - // qDebug(LOG_ALPINEAPK) << Q_FUNC_INFO << m_updatesCount; + qDebug(LOG_ALPINEAPK) << Q_FUNC_INFO << m_updatesCount; return m_updatesCount; } @@ -241,6 +238,10 @@ void AlpineApkUpdater::startCheckForUpdates() QObject::connect(reply, QOverload::of(&KAuth::ExecuteJob::percent), this, &AlpineApkUpdater::handleKAuthUpdateHelperProgress); + m_progressing = true; + Q_EMIT progressingChanged(m_progressing); + Q_EMIT progressChanged(0); + reply->start(); } @@ -256,6 +257,9 @@ void AlpineApkUpdater::handleKAuthUpdateHelperReply(KJob *job) handleKAuthHelperError(reply, replyData); } + m_progressing = false; + Q_EMIT progressingChanged(m_progressing); + // we are not in the state "Fetching updates" now, update UI Q_EMIT checkForUpdatesFinished(); } @@ -265,6 +269,7 @@ void AlpineApkUpdater::handleKAuthUpdateHelperProgress(KJob *job, unsigned long Q_UNUSED(job) qCDebug(LOG_ALPINEAPK) << " fetch updates progress: " << percent; Q_EMIT fetchingUpdatesProgressChanged(percent); + Q_EMIT progressChanged(static_cast(percent)); } void AlpineApkUpdater::handleKAuthUpgradeHelperReply(KJob *job) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h index 77140ca25..0ee2fcb47 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h @@ -185,7 +185,7 @@ private: // QSet m_upgradeable; // QSet m_pendingResources; -// bool m_settingUp; + bool m_progressing = false; // qreal m_progress; // QDateTime m_lastUpdate; // QTimer m_timer; -- GitLab From adddff43ebd74d8a22ae7953e02fd55ab500eed2 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Wed, 1 Jul 2020 10:06:03 +0300 Subject: [PATCH 27/61] AlpineApkTransaction: transaction should know its backend --- .../AlpineApkBackend/AlpineApkTransaction.cpp | 28 +++++++++---------- .../AlpineApkBackend/AlpineApkTransaction.h | 27 ++++++++++-------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp index b4f90df56..fdc1d0534 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp @@ -26,7 +26,6 @@ #include #include -// #define TEST_PROCEED AlpineApkTransaction::AlpineApkTransaction(AlpineApkResource *app, Role role) : AlpineApkTransaction(app, {}, role) @@ -36,12 +35,25 @@ AlpineApkTransaction::AlpineApkTransaction(AlpineApkResource *app, Role role) AlpineApkTransaction::AlpineApkTransaction(AlpineApkResource *app, const AddonList &addons, Transaction::Role role) : Transaction(app->backend(), app, role, addons) , m_app(app) + , m_backend(static_cast(app->backend())) { - setCancellable(true); + setCancellable(false); setStatus(DownloadingStatus); iterateTransaction(); } +void AlpineApkTransaction::proceed() +{ + finishTransaction(); +} + +void AlpineApkTransaction::cancel() +{ + m_iterate = false; + + setStatus(CancelledStatus); +} + void AlpineApkTransaction::iterateTransaction() { if (!m_iterate) { @@ -59,18 +71,6 @@ void AlpineApkTransaction::iterateTransaction() } } -void AlpineApkTransaction::proceed() -{ - finishTransaction(); -} - -void AlpineApkTransaction::cancel() -{ - m_iterate = false; - - setStatus(CancelledStatus); -} - void AlpineApkTransaction::finishTransaction() { AbstractResource::State newState; diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.h b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.h index 63aeef8d7..ba0a77502 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.h @@ -23,24 +23,27 @@ #include +class AlpineApkBackend; class AlpineApkResource; + class AlpineApkTransaction : public Transaction { - Q_OBJECT - public: - AlpineApkTransaction(AlpineApkResource *app, Role role); - AlpineApkTransaction(AlpineApkResource *app, const AddonList &list, Role role); +Q_OBJECT +public: + AlpineApkTransaction(AlpineApkResource *app, Role role); + AlpineApkTransaction(AlpineApkResource *app, const AddonList &list, Role role); - void cancel() override; - void proceed() override; + void cancel() override; + void proceed() override; - private Q_SLOTS: - void iterateTransaction(); - void finishTransaction(); +private Q_SLOTS: + void iterateTransaction(); + void finishTransaction(); - private: - bool m_iterate = true; - AlpineApkResource *m_app; +private: + bool m_iterate = true; + AlpineApkResource *m_app; + AlpineApkBackend *m_backend; }; #endif // ALPINEAPKTRANSACTION_H -- GitLab From 6601841be4d72448e1d407da455f6b63d42e6f4a Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Tue, 28 Jul 2020 21:05:11 +0300 Subject: [PATCH 28/61] AlpineApkBackend: style fixes --- .../AlpineApkBackend/AlpineApkBackend.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp index a089afea4..b695e5e6b 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -66,7 +66,7 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) this, &AlpineApkBackend::finishCheckForUpdates); m_updatesTimeoutTimer->setTimerType(Qt::CoarseTimer); m_updatesTimeoutTimer->setSingleShot(true); - m_updatesTimeoutTimer->setInterval(2 * 60 * 1000); // 2minutes + m_updatesTimeoutTimer->setInterval(5 * 60 * 1000); // 5 minutes qCDebug(LOG_ALPINEAPK) << "backend: populating resources..."; @@ -87,7 +87,8 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) // which places it into "System updates" section const QString key = pkg.name.toLower(); m_resources.insert(key, res); - connect(res, &AlpineApkResource::stateChanged, this, &AlpineApkBackend::updatesCountChanged); + connect(res, &AlpineApkResource::stateChanged, + this, &AlpineApkBackend::updatesCountChanged); } qCDebug(LOG_ALPINEAPK) << " available" << m_availablePackages.size() << "packages"; @@ -215,17 +216,20 @@ AbstractReviewsBackend *AlpineApkBackend::reviewsBackend() const Transaction* AlpineApkBackend::installApplication(AbstractResource *app, const AddonList &addons) { - return new AlpineApkTransaction(qobject_cast(app), addons, Transaction::InstallRole); + return new AlpineApkTransaction(qobject_cast(app), + addons, Transaction::InstallRole); } Transaction* AlpineApkBackend::installApplication(AbstractResource *app) { - return new AlpineApkTransaction(qobject_cast(app), Transaction::InstallRole); + return new AlpineApkTransaction(qobject_cast(app), + Transaction::InstallRole); } Transaction* AlpineApkBackend::removeApplication(AbstractResource *app) { - return new AlpineApkTransaction(qobject_cast(app), Transaction::RemoveRole); + return new AlpineApkTransaction(qobject_cast(app), + Transaction::RemoveRole); } int AlpineApkBackend::fetchingUpdatesProgress() const @@ -281,4 +285,5 @@ void AlpineApkBackend::setFetchingUpdatesProgress(int percent) emit fetchingUpdatesProgressChanged(); } +// needed because DISCOVER_BACKEND_PLUGIN(AlpineApkBackend) contains Q_OBJECT #include "AlpineApkBackend.moc" -- GitLab From 3f085e209657cff475812f4bbe8c64248a8aa0ce Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Wed, 29 Jul 2020 03:48:50 +0300 Subject: [PATCH 29/61] AlpineApk KAuth backend: fix repoconfig action name --- .../AlpineApkBackend/org.kde.discover.alpineapkbackend.actions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions index f1bffe654..5f7f76771 100644 --- a/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions +++ b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions @@ -23,7 +23,7 @@ Policy=auth_admin Persistence=session [org.kde.discover.alpineapkbackend.repoconfig] -Name=Remove package +Name=Configure repositories Description=Configure repositories URLs Policy=auth_admin Persistence=session -- GitLab From 53f84db288e1daea6f40815a750ae2d4b5122ddd Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Wed, 29 Jul 2020 03:50:27 +0300 Subject: [PATCH 30/61] AlpineApkAuthHelper: clsoe database only on helper exit Don't be so quick to close database after each operation, close it in Helper class destructor, on helper exit. --- .../AlpineApkBackend/AlpineApkAuthHelper.cpp | 24 ++++++++++++------- .../AlpineApkBackend/AlpineApkAuthHelper.h | 1 + 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp index f5d8e17e5..e6e6cb7a1 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp @@ -40,8 +40,18 @@ using namespace KAuth; AlpineApkAuthHelper::AlpineApkAuthHelper() {} +AlpineApkAuthHelper::~AlpineApkAuthHelper() +{ + closeDatabase(); +} + bool AlpineApkAuthHelper::openDatabase(const QVariantMap &args, bool readwrite) { + // is already opened? + if (m_apkdb.isOpen()) { + return true; + } + // maybe set fakeRoot (needs to be done before Database::open() const QString fakeRoot = args.value(QLatin1String("fakeRoot"), QString()).toString(); if (!fakeRoot.isEmpty()) { @@ -62,7 +72,11 @@ bool AlpineApkAuthHelper::openDatabase(const QVariantMap &args, bool readwrite) void AlpineApkAuthHelper::closeDatabase() { - m_apkdb.close(); + // close database only if opened + if (m_apkdb.isOpen()) { + // this also stops bg thread + m_apkdb.close(); + } } void AlpineApkAuthHelper::setupTransactionPostCreate(QtApk::Transaction *trans) @@ -142,9 +156,7 @@ ActionReply AlpineApkAuthHelper::update(const QVariantMap &args) }); } - closeDatabase(); HelperSupport::progressStep(100); - return m_actionReply; } @@ -177,9 +189,7 @@ ActionReply AlpineApkAuthHelper::add(const QVariantMap &args) m_actionReply = ActionReply::SuccessReply(); } - closeDatabase(); HelperSupport::progressStep(100); - return m_actionReply; } @@ -219,9 +229,7 @@ ActionReply AlpineApkAuthHelper::del(const QVariantMap &args) m_actionReply = ActionReply::SuccessReply(); } - closeDatabase(); HelperSupport::progressStep(100); - return m_actionReply; } @@ -265,9 +273,7 @@ ActionReply AlpineApkAuthHelper::upgrade(const QVariantMap &args) m_actionReply.setData(replyData); } - closeDatabase(); HelperSupport::progressStep(100); - return m_actionReply; } diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h index eed39f283..7fd69a8f3 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h @@ -32,6 +32,7 @@ class AlpineApkAuthHelper : public QObject Q_OBJECT public: AlpineApkAuthHelper(); + ~AlpineApkAuthHelper() override; public Q_SLOTS: ActionReply update(const QVariantMap &args); -- GitLab From de003cb72d794fe2941bd7e912cc5ab8a014c181 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Wed, 29 Jul 2020 03:59:10 +0300 Subject: [PATCH 31/61] AlpineApkTransaction: implement add/del packages. --- .../AlpineApkBackend/AlpineApkTransaction.cpp | 120 ++++++++++++++---- .../AlpineApkBackend/AlpineApkTransaction.h | 12 +- 2 files changed, 101 insertions(+), 31 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp index fdc1d0534..ffd442927 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp @@ -22,56 +22,118 @@ #include "AlpineApkBackend.h" #include "AlpineApkResource.h" #include "alpineapk_backend_logging.h" // generated by ECM -#include + +// Qt #include -#include +#include +// KF5 +#include +#include +#include -AlpineApkTransaction::AlpineApkTransaction(AlpineApkResource *app, Role role) - : AlpineApkTransaction(app, {}, role) +AlpineApkTransaction::AlpineApkTransaction(AlpineApkResource *res, Role role) + : AlpineApkTransaction(res, {}, role) { } -AlpineApkTransaction::AlpineApkTransaction(AlpineApkResource *app, const AddonList &addons, Transaction::Role role) - : Transaction(app->backend(), app, role, addons) - , m_app(app) - , m_backend(static_cast(app->backend())) +AlpineApkTransaction::AlpineApkTransaction(AlpineApkResource *res, const AddonList &addons, Transaction::Role role) + : Transaction(res->backend(), res, role, addons) + , m_resource(res) + , m_backend(static_cast(res->backend())) { setCancellable(false); - setStatus(DownloadingStatus); - iterateTransaction(); + setStatus(QueuedStatus); + // seems like Discover's transactions are supposed to start + // automatically; no dedicated method to start transaction? + startTransaction(); } void AlpineApkTransaction::proceed() { - finishTransaction(); + startTransaction(); } void AlpineApkTransaction::cancel() { - m_iterate = false; - setStatus(CancelledStatus); } -void AlpineApkTransaction::iterateTransaction() +void AlpineApkTransaction::startTransaction() { - if (!m_iterate) { + KAuth::Action authAction; + QString actionDescription(i18n("Install package")); + switch(role()) { + case InstallRole: + authAction.setName(QStringLiteral("org.kde.discover.alpineapkbackend.add")); + break; + case RemoveRole: + authAction.setName(QStringLiteral("org.kde.discover.alpineapkbackend.del")); + actionDescription = i18n("Remove package"); + break; + case ChangeAddonsRole: + qCWarning(LOG_ALPINEAPK) << "Addons are not supported by Alpine APK Backend!"; return; + break; } + authAction.setHelperId(QStringLiteral("org.kde.discover.alpineapkbackend")); - if(progress() < 100) { - setProgress(qBound(0, progress() + (KRandom::random() % 30), 100)); - QTimer::singleShot(/*KRandom::random()%*/100, this, &AlpineApkTransaction::iterateTransaction); - } else if (status() == DownloadingStatus) { - setStatus(CommittingStatus); - QTimer::singleShot(/*KRandom::random()%*/100, this, &AlpineApkTransaction::iterateTransaction); + if (!authAction.isValid()) { + qCWarning(LOG_ALPINEAPK) << "kauth addAction is not valid!"; + return; + } + + authAction.setTimeout(60 * 60 * 1000); // 60 min +#if KAUTH_VERSION < QT_VERSION_CHECK(5, 68, 0) + addAction.setDetails(actionDescription); +#else + const KAuth::Action::DetailsMap details{ + { KAuth::Action::AuthDetail::DetailMessage, actionDescription } + }; + authAction.setDetailsV2(details); +#endif + authAction.addArgument(QLatin1String("pkgName"), m_resource->m_pkg.name); + + // run action with elevated privileges + KAuth::ExecuteJob *reply = authAction.execute(); + + // get result of this job + QObject::connect(reply, &KAuth::ExecuteJob::result, this, [this](KJob *job) { + KAuth::ExecuteJob *reply = static_cast(job); + const QVariantMap &replyData = reply->data(); + if (reply->error() == 0) { + finishTransactionOK(); + } else { + QString message = replyData.value(QLatin1String("errorString"), + reply->errorString()).toString(); + if (reply->error() == KAuth::ActionReply::Error::AuthorizationDeniedError) { + message = i18n("Error: Authorization denied"); + } + finishTransactionWithError(message); + } + }); + + // get progress reports for this job + QObject::connect(reply, QOverload::of(&KAuth::ExecuteJob::percent), this, + [this](KJob *job, unsigned long percent) { + Q_UNUSED(job) + if (percent >= 40 && role() == InstallRole) { + setStatus(CommittingStatus); + } + setProgress(static_cast(percent)); + }); + + setProgress(0); + if (role() == InstallRole) { + setStatus(DownloadingStatus); } else { - finishTransaction(); + setStatus(CommittingStatus); } + + reply->start(); } -void AlpineApkTransaction::finishTransaction() +void AlpineApkTransaction::finishTransactionOK() { AbstractResource::State newState; switch(role()) { @@ -83,8 +145,16 @@ void AlpineApkTransaction::finishTransaction() newState = AbstractResource::None; break; } - m_app->setAddons(addons()); - m_app->setState(newState); + m_resource->setAddons(addons()); + m_resource->setState(newState); setStatus(DoneStatus); deleteLater(); } + +void AlpineApkTransaction::finishTransactionWithError(const QString &errMsg) +{ + qCWarning(LOG_ALPINEAPK) << "Transaction finished with error:" << errMsg; + Q_EMIT passiveMessage(i18n("Error") + QStringLiteral(":\n") + errMsg); + setStatus(DoneWithErrorStatus); + deleteLater(); +} diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.h b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.h index ba0a77502..cab1f6b99 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.h @@ -30,19 +30,19 @@ class AlpineApkTransaction : public Transaction { Q_OBJECT public: - AlpineApkTransaction(AlpineApkResource *app, Role role); - AlpineApkTransaction(AlpineApkResource *app, const AddonList &list, Role role); + AlpineApkTransaction(AlpineApkResource *res, Role role); + AlpineApkTransaction(AlpineApkResource *res, const AddonList &list, Role role); void cancel() override; void proceed() override; private Q_SLOTS: - void iterateTransaction(); - void finishTransaction(); + void startTransaction(); + void finishTransactionOK(); + void finishTransactionWithError(const QString &errMsg); private: - bool m_iterate = true; - AlpineApkResource *m_app; + AlpineApkResource *m_resource; AlpineApkBackend *m_backend; }; -- GitLab From caf82306b91473a61ae016d9968f82f61a5bc955 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Thu, 30 Jul 2020 03:41:57 +0300 Subject: [PATCH 32/61] AlpineApkBackend: build with AppStreamQt support provide list of AppStreamQt components, load them once at startup --- .../AlpineApkBackend/AlpineApkBackend.cpp | 88 +++++++++++++++++-- .../AlpineApkBackend/AlpineApkBackend.h | 4 + .../backends/AlpineApkBackend/CMakeLists.txt | 1 + 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp index b695e5e6b..ac91a6efa 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -32,11 +32,14 @@ #include +#include + +#include #include #include +#include #include #include -#include DISCOVER_BACKEND_PLUGIN(AlpineApkBackend) @@ -68,6 +71,36 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) m_updatesTimeoutTimer->setSingleShot(true); m_updatesTimeoutTimer->setInterval(5 * 60 * 1000); // 5 minutes + qCDebug(LOG_ALPINEAPK) << "backend: loading AppStream metadata..."; + AppStream::Pool *appStreamPool = new AppStream::Pool(this); + appStreamPool->setFlags(AppStream::Pool::FlagReadCollection | + AppStream::Pool::FlagReadMetainfo | + AppStream::Pool::FlagReadDesktopFiles); + appStreamPool->setCacheFlags(AppStream::Pool::CacheFlagUseUser | + AppStream::Pool::CacheFlagUseSystem); + if (!appStreamPool->load()) { + qCWarning(LOG_ALPINEAPK) << "backend: Failed to load appstream data:" + << appStreamPool->lastError(); + } else { + m_appStreamComponents = appStreamPool->components(); + qCDebug(LOG_ALPINEAPK) << "backend: loaded AppStream metadata OK:" + << m_appStreamComponents.size() << "components."; + // collect all categories present in appstream metadata +// QSet collectedCategories; +// for (const AppStream::Component &component : m_appStreamComponents) { +// const QStringList cats = component.categories(); +// for (const QString &cat : cats) { +// collectedCategories.insert(cat); +// } +// } +// for (const QString &cat : collectedCategories) { +// qCDebug(LOG_ALPINEAPK) << " collected category: " << cat; +// m_collectedCategories << cat; +// } + } + delete appStreamPool; + appStreamPool = nullptr; + qCDebug(LOG_ALPINEAPK) << "backend: populating resources..."; if (m_apkdb.open(QtApk::QTAPK_OPENF_READONLY)) { @@ -78,13 +111,38 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) if (m_availablePackages.size() > 0) { for (const QtApk::Package &pkg: m_availablePackages) { - AlpineApkResource *res = new AlpineApkResource(pkg, this); + + // try to find appstream data for this package + AppStream::Component appstreamComponent; + AbstractResource::Type resType = AbstractResource::Type::Technical; // default + for (const auto& appsC : m_appStreamComponents) { + // find result which package name is exactly the one we want + if (appsC.packageNames().contains(pkg.name)) { + // found! + appstreamComponent = appsC; + // determine resource type here + switch (appsC.kind()) { + case AppStream::Component::KindDesktopApp: + case AppStream::Component::KindConsoleApp: + case AppStream::Component::KindWebApp: + resType = AbstractResource::Type::Application; + break; + case AppStream::Component::KindAddon: + resType = AbstractResource::Type::Addon; + break; + default: + resType = AbstractResource::Type::Technical; + break; + } + break; // exit for() loop + } + } + + AlpineApkResource *res = new AlpineApkResource(pkg, appstreamComponent, resType, this); res->setCategoryName(QStringLiteral("alpine_packages")); res->setOriginSource(QStringLiteral("apk")); res->setSection(QStringLiteral("dummy")); - // here is the place to set a proper type of package - // AlpineApkResource defaults to AbstractResource::Technical, - // which places it into "System updates" section + const QString key = pkg.name.toLower(); m_resources.insert(key, res); connect(res, &AlpineApkResource::stateChanged, @@ -125,6 +183,26 @@ QVector AlpineApkBackend::category() const ); return { s_rootCat }; + +// static QVector s_cats; +// if (s_cats.isEmpty()) { +// // fill only once +// s_cats << s_rootCat; +// for (const QString &scat : m_collectedCategories) { +// Category *cat = new Category( +// scat, // name +// QStringLiteral("package-x-generic"), // icon +// {}, // orFilters +// { displayName() }, // pluginName +// {}, // subcategories +// QUrl(), // decoration +// false // isAddons +// ); +// s_cats << cat; +// } +// } +// return s_cats; + // ^^ causes deep hang in discover in recalculating QML bindings } int AlpineApkBackend::updatesCount() const diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h index 755cf6a5a..efcaf2ea8 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h @@ -26,6 +26,8 @@ #include +#include + class AlpineApkReviewsBackend; class AlpineApkUpdater; class AlpineApkResource; @@ -77,6 +79,8 @@ private: bool m_fetching = false; int m_fetchProgress = 0; QTimer *m_updatesTimeoutTimer; + QList m_appStreamComponents; + // QVector m_collectedCategories; }; #endif // AlpineApkBackend_H diff --git a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt index 319c7ad2f..536525381 100644 --- a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt +++ b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt @@ -39,6 +39,7 @@ target_link_libraries( KF5::AuthCore Discover::Common ApkQt::ApkQt + AppStreamQt ) # KAuth helper exe -- GitLab From d86644a30ae2a9ffe22b8712c2b91ca572e8ab8d Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Thu, 30 Jul 2020 03:44:55 +0300 Subject: [PATCH 33/61] AlpineApkResource: AppStream integration return some data from appstream metadata, if available --- .../AlpineApkBackend/AlpineApkResource.cpp | 136 +++++++++++++++++- .../AlpineApkBackend/AlpineApkResource.h | 13 +- 2 files changed, 141 insertions(+), 8 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp index 346a28b28..a6f4bc0fa 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp @@ -20,14 +20,29 @@ #include "AlpineApkResource.h" #include "alpineapk_backend_logging.h" // generated by ECM + +#include +#include +#include + +#include +#include +#include + +// libdiscover +#include "appstream/AppStreamUtils.h" +#include "config-paths.h" #include "Transaction/AddonList.h" AlpineApkResource::AlpineApkResource(const QtApk::Package &apkPkg, + AppStream::Component &component, + AbstractResource::Type typ, AbstractResourcesBackend *parent) : AbstractResource(parent) , m_state(AbstractResource::State::None) - , m_type(AbstractResource::Type::Technical) + , m_type(typ) , m_pkg(apkPkg) + , m_appsC(component) { } @@ -43,11 +58,17 @@ QString AlpineApkResource::availableVersion() const QStringList AlpineApkResource::categories() { + if (hasAppStreamData()) { + return m_appsC.categories(); + } return { m_category }; } QString AlpineApkResource::comment() { + if (hasAppStreamData()) { + return m_appsC.summary(); + } return m_pkg.description; } @@ -58,26 +79,83 @@ int AlpineApkResource::size() QUrl AlpineApkResource::homepage() { + if (hasAppStreamData()) { + return m_appsC.url(AppStream::Component::UrlKindHomepage); + } return QUrl::fromUserInput(m_pkg.url); } QUrl AlpineApkResource::helpURL() { + if (hasAppStreamData()) { + return m_appsC.url(AppStream::Component::UrlKindHelp); + } return QUrl(); } QUrl AlpineApkResource::bugURL() { + if (hasAppStreamData()) { + return m_appsC.url(AppStream::Component::UrlKindBugtracker); + } return QUrl(); } QUrl AlpineApkResource::donationURL() { + if (hasAppStreamData()) { + return m_appsC.url(AppStream::Component::UrlKindDonation); + } return QUrl(); } +///xdg-compatible icon name to represent the resource, url or QIcon QVariant AlpineApkResource::icon() const { + if (hasAppStreamData()) { + const QList icns = m_appsC.icons(); + if (icns.size() == 0) { + return QStringLiteral("package-x-generic"); + } + QIcon ico; + const AppStream::Icon &appIco = icns.first(); + + switch (appIco.kind()) { + case AppStream::Icon::KindStock: + // we can create icons of this type directly from theme + ico = QIcon::fromTheme(appIco.name()); + break; + case AppStream::Icon::KindLocal: + case AppStream::Icon::KindCached: { + // try from predefined standard Alpine path + const QString appstreamIconsPath = QLatin1String("/usr/share/app-info/icons/"); + const QString path = appstreamIconsPath + appIco.url().path(); + if (QFileInfo::exists(path)) { + ico.addFile(path, appIco.size()); + } else { + const QString altPath = appstreamIconsPath + + QStringLiteral("%1x%2/").arg(appIco.size().width()).arg(appIco.size().height()) + + appIco.url().path(); + if (QFileInfo::exists(altPath)) { + ico.addFile(altPath, appIco.size()); + } + } + } break; + default: break; + } + + // return icon only if we successfully loaded it + if (!ico.isNull()) { + return QVariant::fromValue(ico); + } + + // try to load from icon theme by package name, this is better + // than nothing and works surprisingly well for many packages + ico = QIcon::fromTheme(m_pkg.name); + if (!ico.isNull()) { + return QVariant::fromValue(ico); + } + } return QStringLiteral("package-x-generic"); } @@ -98,11 +176,17 @@ QJsonArray AlpineApkResource::licenses() QString AlpineApkResource::longDescription() { + if (hasAppStreamData()) { + return m_appsC.description(); + } return m_pkg.description; } QString AlpineApkResource::name() const { + if (hasAppStreamData()) { + return m_appsC.name(); + } return m_pkg.name; } @@ -128,13 +212,25 @@ AbstractResource::State AlpineApkResource::state() void AlpineApkResource::fetchChangelog() { - // QString log = longDescription(); - // Q_EMIT changelogFetched(log); + if (hasAppStreamData()) { + emit changelogFetched(AppStreamUtils::changelogToHtml(m_appsC)); + } } void AlpineApkResource::fetchScreenshots() { - // Q_EMIT screenshotsFetched(m_screenshotThumbnails, m_screenshots); + if (hasAppStreamData()) { + const QPair, QList > sc = AppStreamUtils::fetchScreenshots(m_appsC); + Q_EMIT screenshotsFetched(sc.first, sc.second); + } +} + +QString AlpineApkResource::appstreamId() const +{ + if (hasAppStreamData()) { + return m_appsC.id(); + } + return QString(); } void AlpineApkResource::setState(AbstractResource::State state) @@ -184,20 +280,41 @@ void AlpineApkResource::setAvailableVersion(const QString &av) m_availableVersion = av; } +bool AlpineApkResource::hasAppStreamData() const +{ + return !m_appsC.id().isEmpty(); +} + +bool AlpineApkResource::canExecute() const +{ + if (hasAppStreamData()) { + return (m_appsC.kind() == AppStream::Component::KindDesktopApp && + (m_state == AbstractResource::Installed || m_state == AbstractResource::Upgradeable)); + } + return false; +} void AlpineApkResource::invokeApplication() const { - // QDesktopServices d; - // d.openUrl(QUrl(QStringLiteral("https://projects.kde.org/projects/extragear/sysadmin/muon"))); + const QString desktopFile = QLatin1String("/usr/share/applications/") + appstreamId(); + if (QFile::exists(desktopFile)) { + QProcess::startDetached(QStringLiteral("kstart5"), {QStringLiteral("--service"), desktopFile}); + } } QUrl AlpineApkResource::url() const { + if (hasAppStreamData()) { + return QUrl(QStringLiteral("appstream://") + appstreamId()); + } return QUrl(QLatin1String("apk://") + packageName()); } QString AlpineApkResource::author() const { + if (hasAppStreamData()) { + return m_appsC.developerName(); + } return m_pkg.maintainer; } @@ -208,5 +325,12 @@ QString AlpineApkResource::sourceIcon() const QDate AlpineApkResource::releaseDate() const { + if (hasAppStreamData()) { + if (!m_appsC.releases().isEmpty()) { + auto release = m_appsC.releases().constFirst(); + return release.timestamp().date(); + } + } + // just build date is fine, too return m_pkg.buildTime.date(); } diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h index 7140786c5..e8948c467 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h @@ -23,6 +23,7 @@ #include #include +#include class AddonList; @@ -31,7 +32,10 @@ class AlpineApkResource : public AbstractResource Q_OBJECT public: - explicit AlpineApkResource(const QtApk::Package &apkPkg, AbstractResourcesBackend *parent); + explicit AlpineApkResource(const QtApk::Package &apkPkg, + AppStream::Component &component, + AbstractResource::Type typ, + AbstractResourcesBackend *parent); QList addonsInformation() override; QString section() override; @@ -52,10 +56,11 @@ public: QString name() const override; QString packageName() const override; AbstractResource::Type type() const override { return m_type; } - bool canExecute() const override { return true; } + bool canExecute() const override; void invokeApplication() const override; void fetchChangelog() override; void fetchScreenshots() override; + QString appstreamId() const override; QUrl url() const override; QString author() const override; QString sourceIcon() const override; @@ -69,6 +74,9 @@ public: void setAddonInstalled(const QString &addon, bool installed); void setAvailableVersion(const QString &av); +private: + bool hasAppStreamData() const; + public: AbstractResource::State m_state; const AbstractResource::Type m_type; @@ -78,6 +86,7 @@ public: QString m_originSoruce; QString m_sectionName; QList m_addons; + AppStream::Component m_appsC; }; #endif // ALPINEAPKRESOURCE_H -- GitLab From 5a006c4820734be25aa108c69b6b4acc93f2b5ef Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Mon, 28 Sep 2020 08:38:25 +0300 Subject: [PATCH 34/61] AlpineApkBackend: cmake: depend on Qt5::Concurrent --- libdiscover/backends/AlpineApkBackend/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt index 536525381..9e3bd82c2 100644 --- a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt +++ b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt @@ -34,6 +34,7 @@ target_link_libraries( PRIVATE Qt5::Core Qt5::Widgets + Qt5::Concurrent KF5::CoreAddons KF5::ConfigCore KF5::AuthCore -- GitLab From 4a0974c9d48be4c29f9e24b93d606cb2cdca3f1f Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Mon, 28 Sep 2020 09:51:12 +0300 Subject: [PATCH 35/61] AlpineApkBackend: load packages data in backgroud Load packages data and appstream metadata in background thread to unblock UI and increase discover startup time. --- .../AlpineApkBackend/AlpineApkBackend.cpp | 114 +++++++++++++----- .../AlpineApkBackend/AlpineApkBackend.h | 5 + 2 files changed, 91 insertions(+), 28 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp index ac91a6efa..e441c4b36 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -35,10 +35,14 @@ #include #include +#include #include +#include +#include #include #include #include +#include #include DISCOVER_BACKEND_PLUGIN(AlpineApkBackend) @@ -53,9 +57,6 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) const_cast(LOG_ALPINEAPK()).setEnabled(QtDebugMsg, false); #endif - // schedule checking for updates - QTimer::singleShot(1000, this, &AlpineApkBackend::checkForUpdates); - // connections with our updater QObject::connect(m_updater, &AlpineApkUpdater::updatesCountChanged, this, &AlpineApkBackend::updatesCountChanged); @@ -71,8 +72,24 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) m_updatesTimeoutTimer->setSingleShot(true); m_updatesTimeoutTimer->setInterval(5 * 60 * 1000); // 5 minutes + // load packages data in a separate thread; it takes a noticeable amount of time + // and this way UI is not blocked here + m_fetching = true; // we are busy! + QFuture loadResFuture = QtConcurrent::run(QThreadPool::globalInstance(), this, + &AlpineApkBackend::loadResources); + + QObject::connect(&m_voidFutureWatcher, &QFutureWatcher::finished, + this, &AlpineApkBackend::onLoadResourcesFinished); + m_voidFutureWatcher.setFuture(loadResFuture); + + SourcesModel::global()->addSourcesBackend(new AlpineApkSourcesBackend(this)); +} + +void AlpineApkBackend::loadResources() +{ qCDebug(LOG_ALPINEAPK) << "backend: loading AppStream metadata..."; - AppStream::Pool *appStreamPool = new AppStream::Pool(this); + + AppStream::Pool *appStreamPool = new AppStream::Pool(); appStreamPool->setFlags(AppStream::Pool::FlagReadCollection | AppStream::Pool::FlagReadMetainfo | AppStream::Pool::FlagReadDesktopFiles); @@ -102,6 +119,7 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) appStreamPool = nullptr; qCDebug(LOG_ALPINEAPK) << "backend: populating resources..."; + emit this->passiveMessage(i18n("Loading, please wait...")); if (m_apkdb.open(QtApk::QTAPK_OPENF_READONLY)) { m_availablePackages = m_apkdb.getAvailablePackages(); @@ -114,43 +132,77 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) // try to find appstream data for this package AppStream::Component appstreamComponent; - AbstractResource::Type resType = AbstractResource::Type::Technical; // default for (const auto& appsC : m_appStreamComponents) { // find result which package name is exactly the one we want if (appsC.packageNames().contains(pkg.name)) { - // found! - appstreamComponent = appsC; - // determine resource type here - switch (appsC.kind()) { - case AppStream::Component::KindDesktopApp: - case AppStream::Component::KindConsoleApp: - case AppStream::Component::KindWebApp: - resType = AbstractResource::Type::Application; - break; - case AppStream::Component::KindAddon: - resType = AbstractResource::Type::Addon; - break; - default: - resType = AbstractResource::Type::Technical; - break; + // workaround for kate (Kate Sessions is found first, but + // package name = "kate" too, bugged metadata?) + if (pkg.name == QStringLiteral("kate")) { + // qCDebug(LOG_ALPINEAPK) << appsC.packageNames() << appsC.id(); + // ^^ ("kate") "org.kde.plasma.katesessions" + if (appsC.id() != QStringLiteral("org.kde.kate")) { + continue; + } } + appstreamComponent = appsC; break; // exit for() loop } } + const QString key = pkg.name.toLower(); + m_resourcesAppstreamData.insert(key, appstreamComponent); + } + } + + qCDebug(LOG_ALPINEAPK) << " available" << m_availablePackages.size() + << "packages"; + qCDebug(LOG_ALPINEAPK) << " installed" << m_installedPackages.size() + << "packages"; +} + +static AbstractResource::Type toDiscoverResourceType(const AppStream::Component &component) +{ + AbstractResource::Type resType = AbstractResource::Type::Technical; // default + // determine resource type here + switch (component.kind()) { + case AppStream::Component::KindDesktopApp: + case AppStream::Component::KindConsoleApp: + case AppStream::Component::KindWebApp: + resType = AbstractResource::Type::Application; + break; + case AppStream::Component::KindAddon: + resType = AbstractResource::Type::Addon; + break; + default: + resType = AbstractResource::Type::Technical; + break; + } + return resType; +} + +void AlpineApkBackend::onLoadResourcesFinished() +{ + qCDebug(LOG_ALPINEAPK) << "backend: appstream data loaded and sorted; fill in resources"; + + if (m_availablePackages.size() > 0) { + for (const QtApk::Package &pkg: m_availablePackages) { + const QString key = pkg.name.toLower(); + + AppStream::Component &appstreamComponent = m_resourcesAppstreamData[key]; + const AbstractResource::Type resType = toDiscoverResourceType(appstreamComponent); + AlpineApkResource *res = new AlpineApkResource(pkg, appstreamComponent, resType, this); res->setCategoryName(QStringLiteral("alpine_packages")); res->setOriginSource(QStringLiteral("apk")); res->setSection(QStringLiteral("dummy")); - const QString key = pkg.name.toLower(); m_resources.insert(key, res); - connect(res, &AlpineApkResource::stateChanged, - this, &AlpineApkBackend::updatesCountChanged); + QObject::connect(res, &AlpineApkResource::stateChanged, + this, &AlpineApkBackend::updatesCountChanged); } - qCDebug(LOG_ALPINEAPK) << " available" << m_availablePackages.size() - << "packages"; } + + // update "installed/not installed" state if (m_installedPackages.size() > 0) { for (const QtApk::Package &pkg: m_installedPackages) { const QString key = pkg.name.toLower(); @@ -158,11 +210,17 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) m_resources.value(key)->setState(AbstractResource::Installed); } } - qCDebug(LOG_ALPINEAPK) << " installed" << m_installedPackages.size() - << "packages"; } - SourcesModel::global()->addSourcesBackend(new AlpineApkSourcesBackend(this)); + qCDebug(LOG_ALPINEAPK) << "backend: resources loaded."; + + m_fetching = false; + emit fetchingChanged(); + // ^^ this causes the UI to update "Featured" page and show + // to user that we actually have loaded packages data + + // schedule check for updates 1 sec after we've loaded all resources + QTimer::singleShot(1000, this, &AlpineApkBackend::checkForUpdates); } QVector AlpineApkBackend::category() const diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h index efcaf2ea8..2ec8b00b6 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h @@ -23,6 +23,7 @@ #include #include +#include #include @@ -65,12 +66,15 @@ public Q_SLOTS: private Q_SLOTS: void finishCheckForUpdates(); + void loadResources(); + void onLoadResourcesFinished(); public: QtApk::Database *apkdb() { return &m_apkdb; } private: QHash m_resources; + QHash m_resourcesAppstreamData; AlpineApkUpdater *m_updater; AlpineApkReviewsBackend *m_reviews; QtApk::Database m_apkdb; @@ -81,6 +85,7 @@ private: QTimer *m_updatesTimeoutTimer; QList m_appStreamComponents; // QVector m_collectedCategories; + QFutureWatcher m_voidFutureWatcher; }; #endif // AlpineApkBackend_H -- GitLab From 7767cf429ee83c548718dfd8077b3aff7cc1239f Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Mon, 28 Sep 2020 09:51:35 +0300 Subject: [PATCH 36/61] AlpineApkUpdater: disable too spammy logging --- libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp index 2f3d184fb..c03fd5c5c 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp @@ -204,7 +204,7 @@ void AlpineApkUpdater::proceed() int AlpineApkUpdater::updatesCount() { - qDebug(LOG_ALPINEAPK) << Q_FUNC_INFO << m_updatesCount; + // qDebug(LOG_ALPINEAPK) << Q_FUNC_INFO << m_updatesCount; return m_updatesCount; } -- GitLab From b5026f83108a66c5e9f9464bab19ee9a704242fa Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Wed, 13 Jan 2021 22:08:44 +0300 Subject: [PATCH 37/61] AlpineApkUpdater: try harder to get error string from helper --- .../backends/AlpineApkBackend/AlpineApkUpdater.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp index c03fd5c5c..677a784f8 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp @@ -299,12 +299,22 @@ void AlpineApkUpdater::handleKAuthHelperError( KAuth::ExecuteJob *reply, const QVariantMap &replyData) { - const QString message = replyData.value(QLatin1String("errorString"), + // error message should be received as part of JSON reply from helper + QString message = replyData.value(QLatin1String("errorString"), reply->errorString()).toString(); - qCDebug(LOG_ALPINEAPK) << "KAuth helper returned error:" << message << reply->error(); if (reply->error() == KAuth::ActionReply::Error::AuthorizationDeniedError) { + qCWarning(LOG_ALPINEAPK) << "updater: KAuth helper returned AuthorizationDeniedError"; Q_EMIT passiveMessage(i18n("Authorization denied")); } else { + // if received error message is empty, try other ways to get error text for user + // there are multiple ways to get error messages in kauth/kjob + if (message.isEmpty()) { + message = reply->errorString(); + if (message.isEmpty()) { + message = reply->errorText(); + } + } + qCDebug(LOG_ALPINEAPK) << "updater: KAuth helper returned error:" << message << reply->error(); Q_EMIT passiveMessage(i18n("Error") + QStringLiteral(":\n") + message); } } -- GitLab From 484cfb033d0598f898af106c4c3f42b39f6d67f0 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sun, 24 Jan 2021 08:16:20 +0300 Subject: [PATCH 38/61] AlpineApkBackend: rename root category name --- libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp index e441c4b36..8ddcc8e9f 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -231,7 +231,7 @@ QVector AlpineApkBackend::category() const // Display a single root category // we could add more, but Alpine apk does not have this concept static Category *s_rootCat = new Category( - i18nc("Root category name", "Alpine packages"), + i18nc("Root category name", "Alpine Linux packages"), QStringLiteral("package-x-generic"), // icon { s_apkFlt }, // orFilters - include packages that match filter { displayName() }, // pluginName -- GitLab From 69a593d8e7758c185e1a6fdcc5272893a9c6cf5c Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sun, 24 Jan 2021 08:28:04 +0300 Subject: [PATCH 39/61] KAuth helper: use single KAuth action for all operations Use single entry point for all package management operations. Using single KAuth action allows user to enter his password only once for all privileged operations during Discover session. --- .../AlpineApkBackend/AlpineApkAuthHelper.cpp | 91 ++++++++++--------- .../AlpineApkBackend/AlpineApkAuthHelper.h | 15 ++- .../org.kde.discover.alpineapkbackend.actions | 30 +----- 3 files changed, 59 insertions(+), 77 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp index e6e6cb7a1..97affc013 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp @@ -113,6 +113,7 @@ void AlpineApkAuthHelper::onTransactionError(const QString &msg) qCWarning(LOG_AUTHHELPER).nospace() << "ERROR occured in transaction \"" << m_currentTransaction->desc() << "\": " << msg; + // construct error message to use in helper reply const QString errMsg = m_currentTransaction->desc() + QLatin1String(" failed: ") + msg; m_actionReply.setErrorDescription(errMsg); m_actionReply.setData({ @@ -129,16 +130,48 @@ void AlpineApkAuthHelper::onTransactionFinished() m_loop->quit(); } -ActionReply AlpineApkAuthHelper::update(const QVariantMap &args) +// single entry point for all package management actions +ActionReply AlpineApkAuthHelper::pkgmgmt(const QVariantMap &args) { - // return error by default m_actionReply = ActionReply::HelperErrorReply(); - HelperSupport::progressStep(0); + // actual package management action to perform is passed in "pkgAction" argument + if (!args.contains(QLatin1String("pkgAction"))) { + m_actionReply.setError(ActionReply::InvalidActionError); + m_actionReply.setErrorDescription(QLatin1String("Please pass \'pkgAction\' argument.")); + HelperSupport::progressStep(100); + return m_actionReply; + } + + const QString pkgAction = args.value(QLatin1String("pkgAction")).toString(); + + if (pkgAction == QStringLiteral("update")) { + update(args); + } else if (pkgAction == QStringLiteral("add")) { + add(args); + } else if (pkgAction == QStringLiteral("del")) { + del(args); + } else if (pkgAction == QStringLiteral("upgrade")) { + upgrade(args); + } else if (pkgAction == QStringLiteral("repoconfig")) { + repoconfig(args); + } else { + // error: unknown pkgAction + m_actionReply.setError(ActionReply::NoSuchActionError); + m_actionReply.setErrorDescription(QLatin1String("Please pass a valid \'pkgAction\' argument. " + "Action \"%1\" is not recognized.").arg(pkgAction)); + } + + HelperSupport::progressStep(100); + return m_actionReply; +} + +void AlpineApkAuthHelper::update(const QVariantMap &args) +{ if (!openDatabase(args)) { m_actionReply.setErrorDescription(QStringLiteral("Failed to open database!")); - return m_actionReply; + return; } m_trans_ok = true; @@ -155,27 +188,19 @@ ActionReply AlpineApkAuthHelper::update(const QVariantMap &args) { QLatin1String("updatesCount"), updatesCount } }); } - - HelperSupport::progressStep(100); - return m_actionReply; } -ActionReply AlpineApkAuthHelper::add(const QVariantMap &args) +void AlpineApkAuthHelper::add(const QVariantMap &args) { - // return error by default - m_actionReply = ActionReply::HelperErrorReply(); - - HelperSupport::progressStep(0); - if (!openDatabase(args)) { m_actionReply.setErrorDescription(QStringLiteral("Failed to open database!")); - return m_actionReply; + return; } const QString pkgName = args.value(QLatin1String("pkgName"), QString()).toString(); if (pkgName.isEmpty()) { m_actionReply.setErrorDescription(QStringLiteral("Specify pkgName for adding!")); - return m_actionReply; + return; } m_trans_ok = true; @@ -188,27 +213,19 @@ ActionReply AlpineApkAuthHelper::add(const QVariantMap &args) if (m_trans_ok) { m_actionReply = ActionReply::SuccessReply(); } - - HelperSupport::progressStep(100); - return m_actionReply; } -ActionReply AlpineApkAuthHelper::del(const QVariantMap &args) +void AlpineApkAuthHelper::del(const QVariantMap &args) { - // return error by default - m_actionReply = ActionReply::HelperErrorReply(); - - HelperSupport::progressStep(0); - if (!openDatabase(args)) { m_actionReply.setErrorDescription(QStringLiteral("Failed to open database!")); - return m_actionReply; + return; } const QString pkgName = args.value(QLatin1String("pkgName"), QString()).toString(); if (pkgName.isEmpty()) { m_actionReply.setErrorDescription(QStringLiteral("Specify pkgName for removing!")); - return m_actionReply; + return; } const bool delRdepends = args.value(QLatin1String("delRdepends"), false).toBool(); @@ -228,20 +245,13 @@ ActionReply AlpineApkAuthHelper::del(const QVariantMap &args) if (m_trans_ok) { m_actionReply = ActionReply::SuccessReply(); } - - HelperSupport::progressStep(100); - return m_actionReply; } -ActionReply AlpineApkAuthHelper::upgrade(const QVariantMap &args) +void AlpineApkAuthHelper::upgrade(const QVariantMap &args) { - m_actionReply = ActionReply::HelperErrorReply(); - - HelperSupport::progressStep(0); - if (!openDatabase(args)) { m_actionReply.setErrorDescription(QStringLiteral("Failed to open database!")); - return m_actionReply; + return; } bool onlySimulate = args.value(QLatin1String("onlySimulate"), false).toBool(); @@ -272,16 +282,10 @@ ActionReply AlpineApkAuthHelper::upgrade(const QVariantMap &args) replyData.insert(QLatin1String("onlySimulate"), onlySimulate); m_actionReply.setData(replyData); } - - HelperSupport::progressStep(100); - return m_actionReply; } -ActionReply AlpineApkAuthHelper::repoconfig(const QVariantMap &args) +void AlpineApkAuthHelper::repoconfig(const QVariantMap &args) { - m_actionReply = ActionReply::HelperErrorReply(); - HelperSupport::progressStep(10); - if (args.contains(QLatin1String("repoList"))) { const QVariant v = args.value(QLatin1String("repoList")); const QVector repoVec = v.value>(); @@ -293,9 +297,6 @@ ActionReply AlpineApkAuthHelper::repoconfig(const QVariantMap &args) } else { m_actionReply.setErrorDescription(QStringLiteral("repoList parameter is missing in request!")); } - - HelperSupport::progressStep(100); - return m_actionReply; } KAUTH_HELPER_MAIN("org.kde.discover.alpineapkbackend", AlpineApkAuthHelper) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h index 7fd69a8f3..240e6ed35 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h @@ -35,17 +35,22 @@ public: ~AlpineApkAuthHelper() override; public Q_SLOTS: - ActionReply update(const QVariantMap &args); - ActionReply add(const QVariantMap &args); - ActionReply del(const QVariantMap &args); - ActionReply upgrade(const QVariantMap &args); - ActionReply repoconfig(const QVariantMap &args); + // single entry point for all package management operations + ActionReply pkgmgmt(const QVariantMap &args); protected: + // helpers bool openDatabase(const QVariantMap &args, bool readwrite = true); void closeDatabase(); void setupTransactionPostCreate(QtApk::Transaction *trans); + // individual pakckage management actions + void update(const QVariantMap &args); + void add(const QVariantMap &args); + void del(const QVariantMap &args); + void upgrade(const QVariantMap &args); + void repoconfig(const QVariantMap &args); + protected Q_SLOTS: void reportProgress(float percent); void onTransactionError(const QString &msg); diff --git a/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions index 5f7f76771..c9bb5f9f2 100644 --- a/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions +++ b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions @@ -1,29 +1,5 @@ -[org.kde.discover.alpineapkbackend.update] -Name=Update repository index -Description=Updates available packages list from repositories -Policy=auth_admin -Persistence=session - -[org.kde.discover.alpineapkbackend.upgrade] -Name=Upgrade all upgradable packages -Description=Upgrade installed packages to latest versions -Policy=auth_admin -Persistence=session - -[org.kde.discover.alpineapkbackend.add] -Name=Install/upgrade package -Description=Installs/upgrades one package -Policy=auth_admin -Persistence=session - -[org.kde.discover.alpineapkbackend.del] -Name=Remove package -Description=Uninstall one package -Policy=auth_admin -Persistence=session - -[org.kde.discover.alpineapkbackend.repoconfig] -Name=Configure repositories -Description=Configure repositories URLs +[org.kde.discover.alpineapkbackend.pkgmgmt] +Name=Package management +Description=Install or remove packages, upgrade system Policy=auth_admin Persistence=session -- GitLab From 7de19c2db5842cbe35c5f3ba2c6421c3e52c5ecb Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sun, 24 Jan 2021 08:32:59 +0300 Subject: [PATCH 40/61] AlpineApk: refactor code to use single entry for all KAuth stuff * Add AlpineApkAuthActionFactory to contain all KAuth action creation stuff in one place. * ActionFactory: Use new single entry point for all KAuth helper operations. * Switch all places in code that used KAuth actions to using ActionFactory:create*() methods. --- .../AlpineApkAuthActionFactory.cpp | 118 ++++++++++++++++++ .../AlpineApkAuthActionFactory.h | 41 ++++++ .../AlpineApkSourcesBackend.cpp | 28 +---- .../AlpineApkBackend/AlpineApkTransaction.cpp | 29 +---- .../AlpineApkBackend/AlpineApkUpdater.cpp | 45 +------ .../backends/AlpineApkBackend/CMakeLists.txt | 2 + 6 files changed, 178 insertions(+), 85 deletions(-) create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkAuthActionFactory.cpp create mode 100644 libdiscover/backends/AlpineApkBackend/AlpineApkAuthActionFactory.h diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthActionFactory.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthActionFactory.cpp new file mode 100644 index 000000000..972f8ec50 --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthActionFactory.cpp @@ -0,0 +1,118 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#include +#include + +#include "AlpineApkAuthActionFactory.h" +#include "alpineapk_backend_logging.h" + +namespace ActionFactory { + +static KAuth::Action createAlpineApkKAuthAction() +{ + KAuth::Action action(QStringLiteral("org.kde.discover.alpineapkbackend.pkgmgmt")); + action.setHelperId(QStringLiteral("org.kde.discover.alpineapkbackend")); + if (!action.isValid()) { + qCWarning(LOG_ALPINEAPK) << "Created KAuth action is not valid!"; + return action; + } + + // set action description + // setDetails deprecated since KF 5.68, use setDetailsV2() with DetailsMap. +#if KAUTH_VERSION < QT_VERSION_CHECK(5, 68, 0) + action.setDetails(i18n("Package management")); +#else + static const KAuth::Action::DetailsMap details{ + { KAuth::Action::AuthDetail::DetailMessage, i18n("Package management") } + }; + action.setDetailsV2(details); +#endif + + // change default timeout to 1 minute, bcause default DBus timeout + // of 25 seconds is not enough + action.setTimeout(1 * 60 * 1000); + + return action; +} + +KAuth::ExecuteJob *createUpdateAction(const QString &fakeRoot) +{ + KAuth::Action action = createAlpineApkKAuthAction(); + if (!action.isValid()) { + return nullptr; + } + // update-action specific details + action.setTimeout(2 * 60 * 1000); // 2 minutes + action.addArgument(QLatin1String("pkgAction"), QLatin1String("update")); + action.addArgument(QLatin1String("fakeRoot"), fakeRoot); + return action.execute(); +} + +KAuth::ExecuteJob *createUpgradeAction(bool onlySimulate) +{ + KAuth::Action action = createAlpineApkKAuthAction(); + if (!action.isValid()) { + return nullptr; + } + action.setTimeout(3 * 60 * 60 * 1000); // 3 hours, system upgrade can take really long + action.addArgument(QLatin1String("pkgAction"), QLatin1String("upgrade")); + action.addArgument(QLatin1String("onlySimulate"), onlySimulate); + return action.execute(); +} + +KAuth::ExecuteJob *createAddAction(const QString &pkgName) +{ + KAuth::Action action = createAlpineApkKAuthAction(); + if (!action.isValid()) { + return nullptr; + } + action.setTimeout(1 * 60 * 60 * 1000); // 1 hour, in case package is really big? + action.addArgument(QLatin1String("pkgAction"), QLatin1String("add")); + action.addArgument(QLatin1String("pkgName"), pkgName); + return action.execute(); +} + +KAuth::ExecuteJob *createDelAction(const QString &pkgName) +{ + KAuth::Action action = createAlpineApkKAuthAction(); + if (!action.isValid()) { + return nullptr; + } + action.setTimeout(1 * 60 * 60 * 1000); // although deletion is almost instant + action.addArgument(QLatin1String("pkgAction"), QLatin1String("del")); + action.addArgument(QLatin1String("pkgName"), pkgName); + return action.execute(); +} + +KAuth::ExecuteJob *createRepoconfigAction(const QVariant &repoUrls) +{ + KAuth::Action action = createAlpineApkKAuthAction(); + if (!action.isValid()) { + return nullptr; + } + // should be instant, writes few lines to /etc/apk/repositories + action.setTimeout(1 * 60 * 1000); // 1 minute + action.addArgument(QLatin1String("pkgAction"), QLatin1String("repoconfig")); + action.addArgument(QLatin1String("repoList"), repoUrls); + return action.execute(); +} + +} // namespace ActionFactory diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthActionFactory.h b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthActionFactory.h new file mode 100644 index 000000000..ff5667f85 --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthActionFactory.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef AlpineApkAuthActionFactory_H +#define AlpineApkAuthActionFactory_H + +#include +#include + +#include +#include +#include + +namespace ActionFactory { + +KAuth::ExecuteJob *createUpdateAction(const QString &fakeRoot); +KAuth::ExecuteJob *createUpgradeAction(bool onlySimulate = false); +KAuth::ExecuteJob *createAddAction(const QString &pkgName); +KAuth::ExecuteJob *createDelAction(const QString &pkgName); +KAuth::ExecuteJob *createRepoconfigAction(const QVariant &repoUrls); + +} // namespace ActionFactory + +#endif diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp index 28f08ef86..a126483a9 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp @@ -19,6 +19,7 @@ ***************************************************************************/ #include "AlpineApkSourcesBackend.h" +#include "AlpineApkAuthActionFactory.h" #include "alpineapk_backend_logging.h" // generated by ECM #include @@ -27,7 +28,6 @@ // KF5 #include -#include #include // libapk-qt @@ -100,28 +100,12 @@ void AlpineApkSourcesBackend::fillModelFromRepos() void AlpineApkSourcesBackend::saveSources() { - KAuth::Action repoConfigAction(QStringLiteral("org.kde.discover.alpineapkbackend.repoconfig")); - repoConfigAction.setHelperId(QStringLiteral("org.kde.discover.alpineapkbackend")); - if (!repoConfigAction.isValid()) { - qCWarning(LOG_ALPINEAPK) << "repoConfigAction is not valid!"; - return; - } - - repoConfigAction.setTimeout(1 * 60 * 1000); // 1 min -#if KAUTH_VERSION < QT_VERSION_CHECK(5, 68, 0) - upgradeAction.setDetails(i18n("Configure repositories URLs")); -#else - static const KAuth::Action::DetailsMap details{ - { KAuth::Action::AuthDetail::DetailMessage, i18n("Configure repositories URLs") } - }; - repoConfigAction.setDetailsV2(details); -#endif - // pass in new repositories list - repoConfigAction.addArgument(QLatin1String("repoList"), - QVariant::fromValue>(m_repos)); + const QVariant repoUrls = QVariant::fromValue>(m_repos); // run with elevated privileges - KAuth::ExecuteJob *reply = repoConfigAction.execute(); + KAuth::ExecuteJob *reply = ActionFactory::createRepoconfigAction(repoUrls); + if (!reply) return; + QObject::connect(reply, &KAuth::ExecuteJob::result, this, [this] (KJob *job) { KAuth::ExecuteJob *reply = static_cast(job); if (reply->error() != 0) { @@ -142,7 +126,7 @@ void AlpineApkSourcesBackend::saveSources() void AlpineApkSourcesBackend::onItemChanged(QStandardItem *item) { - // update internal storage vector and relaod model from it + // update internal storage vector and reload model from it // otherwise checks state are not updated in UI const Qt::CheckState cs = item->checkState(); const QModelIndex idx = m_sourcesModel->indexFromItem(item); diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp index ffd442927..26bf1bd0d 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp @@ -21,6 +21,7 @@ #include "AlpineApkTransaction.h" #include "AlpineApkBackend.h" #include "AlpineApkResource.h" +#include "AlpineApkAuthActionFactory.h" #include "alpineapk_backend_logging.h" // generated by ECM // Qt @@ -29,7 +30,6 @@ // KF5 #include -#include #include AlpineApkTransaction::AlpineApkTransaction(AlpineApkResource *res, Role role) @@ -61,42 +61,23 @@ void AlpineApkTransaction::cancel() void AlpineApkTransaction::startTransaction() { - KAuth::Action authAction; - QString actionDescription(i18n("Install package")); + KAuth::ExecuteJob *reply = nullptr; switch(role()) { case InstallRole: - authAction.setName(QStringLiteral("org.kde.discover.alpineapkbackend.add")); + reply = ActionFactory::createAddAction(m_resource->m_pkg.name); break; case RemoveRole: - authAction.setName(QStringLiteral("org.kde.discover.alpineapkbackend.del")); - actionDescription = i18n("Remove package"); + reply = ActionFactory::createDelAction(m_resource->m_pkg.name); break; case ChangeAddonsRole: qCWarning(LOG_ALPINEAPK) << "Addons are not supported by Alpine APK Backend!"; - return; break; } - authAction.setHelperId(QStringLiteral("org.kde.discover.alpineapkbackend")); - if (!authAction.isValid()) { - qCWarning(LOG_ALPINEAPK) << "kauth addAction is not valid!"; + if (!reply) { return; } - authAction.setTimeout(60 * 60 * 1000); // 60 min -#if KAUTH_VERSION < QT_VERSION_CHECK(5, 68, 0) - addAction.setDetails(actionDescription); -#else - const KAuth::Action::DetailsMap details{ - { KAuth::Action::AuthDetail::DetailMessage, actionDescription } - }; - authAction.setDetailsV2(details); -#endif - authAction.addArgument(QLatin1String("pkgName"), m_resource->m_pkg.name); - - // run action with elevated privileges - KAuth::ExecuteJob *reply = authAction.execute(); - // get result of this job QObject::connect(reply, &KAuth::ExecuteJob::result, this, [this](KJob *job) { KAuth::ExecuteJob *reply = static_cast(job); diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp index 677a784f8..0bbbce1c0 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp @@ -21,11 +21,11 @@ #include "AlpineApkUpdater.h" #include "AlpineApkResource.h" #include "AlpineApkBackend.h" +#include "AlpineApkAuthActionFactory.h" #include "alpineapk_backend_logging.h" #include "utils.h" #include -#include #include #include @@ -167,27 +167,10 @@ void AlpineApkUpdater::cancel() void AlpineApkUpdater::start() { qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; - KAuth::Action upgradeAction(QStringLiteral("org.kde.discover.alpineapkbackend.upgrade")); - upgradeAction.setHelperId(QStringLiteral("org.kde.discover.alpineapkbackend")); - - if (!upgradeAction.isValid()) { - qCWarning(LOG_ALPINEAPK) << "kauth upgradeAction is not valid!"; - return; - } - - upgradeAction.setTimeout(30 * 60 * 1000); // 30 min -#if KAUTH_VERSION < QT_VERSION_CHECK(5, 68, 0) - upgradeAction.setDetails(i18n("Upgrade currently installed packages")); -#else - static const KAuth::Action::DetailsMap details{ - { KAuth::Action::AuthDetail::DetailMessage, i18n("Upgrade currently installed packages") } - }; - upgradeAction.setDetailsV2(details); -#endif - // upgradeAction.addArgument(QLatin1String("onlySimulate"), true); // run upgrade with elevated privileges - KAuth::ExecuteJob *reply = upgradeAction.execute(); + KAuth::ExecuteJob *reply = ActionFactory::createUpgradeAction(); + if (!reply) return; QObject::connect(reply, &KAuth::ExecuteJob::result, this, &AlpineApkUpdater::handleKAuthUpgradeHelperReply); @@ -211,27 +194,11 @@ int AlpineApkUpdater::updatesCount() void AlpineApkUpdater::startCheckForUpdates() { QtApk::Database *db = m_backend->apkdb(); - KAuth::Action updateAction(QStringLiteral("org.kde.discover.alpineapkbackend.update")); - updateAction.setHelperId(QStringLiteral("org.kde.discover.alpineapkbackend")); - if (!updateAction.isValid()) { - qCWarning(LOG_ALPINEAPK) << "kauth updateAction is not valid!"; - return; - } - updateAction.setTimeout(60 * 1000); // 1 minute - // setDetails deprecated since KF 5.68, use setDetailsV2() with DetailsMap. -#if KAUTH_VERSION < QT_VERSION_CHECK(5, 68, 0) - updateAction.setDetails(i18n("Update repositories index")); -#else - static const KAuth::Action::DetailsMap details{ - { KAuth::Action::AuthDetail::DetailMessage, i18n("Update repositories index") } - }; - updateAction.setDetailsV2(details); -#endif - updateAction.addArgument(QLatin1String("fakeRoot"), db->fakeRoot()); // run updates check with elevated privileges to access // system package manager files - KAuth::ExecuteJob *reply = updateAction.execute(); + KAuth::ExecuteJob *reply = ActionFactory::createUpdateAction(db->fakeRoot()); + if (!reply) return; QObject::connect(reply, &KAuth::ExecuteJob::result, this, &AlpineApkUpdater::handleKAuthUpdateHelperReply); // qOverload is needed because of conflict with getter named percent() @@ -279,8 +246,8 @@ void AlpineApkUpdater::handleKAuthUpgradeHelperReply(KJob *job) if (reply->error() == 0) { QVariant pkgsV = replyData.value(QLatin1String("changes")); bool onlySimulate = replyData.value(QLatin1String("onlySimulate"), false).toBool(); - qCDebug(LOG_ALPINEAPK) << "KAuth helper upgrade reply received, onlySimulate:" << onlySimulate; if (onlySimulate) { + qCDebug(LOG_ALPINEAPK) << "KAuth helper upgrade reply received, simulation mode"; QVector pkgVector = pkgsV.value>(); qCDebug(LOG_ALPINEAPK) << " num changes:" << pkgVector.size(); for (const QtApk::Package &pkg : pkgVector) { diff --git a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt index 9e3bd82c2..7e54cfd9b 100644 --- a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt +++ b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt @@ -1,6 +1,8 @@ find_package(KF5Auth CONFIG REQUIRED) # Probably should be moved to top CMakeLists set(alpineapkbackend_SRCS + AlpineApkAuthActionFactory.h + AlpineApkAuthActionFactory.cpp AlpineApkBackend.cpp AlpineApkBackend.h AlpineApkResource.cpp -- GitLab From 96abd18d848ed67a6c16f4625b42c38327383a4a Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Fri, 29 Jan 2021 12:00:56 +0300 Subject: [PATCH 41/61] AlpineApkResource: add AppStream data setter --- libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp | 5 +++++ libdiscover/backends/AlpineApkBackend/AlpineApkResource.h | 1 + 2 files changed, 6 insertions(+) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp index a6f4bc0fa..8f493a490 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp @@ -285,6 +285,11 @@ bool AlpineApkResource::hasAppStreamData() const return !m_appsC.id().isEmpty(); } +void AlpineApkResource::setAppStreamData(const AppStream::Component &component) +{ + m_appsC = component; +} + bool AlpineApkResource::canExecute() const { if (hasAppStreamData()) { diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h index e8948c467..5304a8773 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h @@ -73,6 +73,7 @@ public: void setAddons(const AddonList &addons); void setAddonInstalled(const QString &addon, bool installed); void setAvailableVersion(const QString &av); + void setAppStreamData(const AppStream::Component &component); private: bool hasAppStreamData() const; -- GitLab From c87972c9249fda6a97636c702d500622b4bc7a5a Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sat, 17 Jul 2021 21:10:57 +0300 Subject: [PATCH 42/61] CMake: AlpineApkBackend: depend on KIOCore, KJobWidgets Needed for AppStreamDataDownloader --- libdiscover/backends/AlpineApkBackend/CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt index 7e54cfd9b..0d657c6ab 100644 --- a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt +++ b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt @@ -1,4 +1,5 @@ find_package(KF5Auth CONFIG REQUIRED) # Probably should be moved to top CMakeLists +find_package(KF5JobWidgets CONFIG REQUIRED) set(alpineapkbackend_SRCS AlpineApkAuthActionFactory.h @@ -37,9 +38,11 @@ target_link_libraries( Qt5::Core Qt5::Widgets Qt5::Concurrent - KF5::CoreAddons - KF5::ConfigCore KF5::AuthCore + KF5::ConfigCore + KF5::CoreAddons + KF5::JobWidgets + KF5::KIOCore Discover::Common ApkQt::ApkQt AppStreamQt -- GitLab From 39f58cb92b20a4f4334c0a0d67865d4ff8fd15f8 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Fri, 29 Jan 2021 12:11:51 +0300 Subject: [PATCH 43/61] Add AppstreamDataDownloader class * Use KIO::get - Simpler and more readable code than the one using QNetworkAccessManager. * use temp files for downloading * review comments from apol * use KUiServerJobTracker to show some indication --- .../AppstreamDataDownloader.cpp | 237 ++++++++++++++++++ .../AppstreamDataDownloader.h | 156 ++++++++++++ .../backends/AlpineApkBackend/CMakeLists.txt | 2 + 3 files changed, 395 insertions(+) create mode 100644 libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.cpp create mode 100644 libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.h diff --git a/libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.cpp b/libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.cpp new file mode 100644 index 000000000..e791b2e02 --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.cpp @@ -0,0 +1,237 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KF5 +#include +#include + +#include "AppstreamDataDownloader.h" +#include "alpineapk_backend_logging.h" + +namespace DiscoverVersion { +// contains static QLatin1String version("5.20.5"); definition +// autogenerated from top CMakeLists.txt +#include "../../../DiscoverVersion.h" +} + +QString AppstreamDataDownloader::appStreamCacheDir() +{ + const QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/external_appstream_data"); + // ^^ "~/.cache/discover/external_appstream_data" + QDir(cachePath).mkpath(QStringLiteral(".")); + return cachePath; +} + +AppstreamDataDownloader::AppstreamDataDownloader(QObject *parent) + : QObject(parent) +{ + m_jobTracker = new KUiServerJobTracker(this); +} + +void AppstreamDataDownloader::setCacheExpirePeriodSecs(qint64 secs) +{ + m_cacheExpireSeconds = secs; +} + +void AppstreamDataDownloader::loadUrlsJson(const QString &jsonPath) +{ + const QString jsonBaseName = QFileInfo(jsonPath).baseName(); + QFile jsonFile(jsonPath); + if (!jsonFile.open(QIODevice::ReadOnly)) { + qCWarning(LOG_ALPINEAPK) << "Failed to open JSON:" << jsonPath << "for reading!"; + Q_EMIT downloadFinished(); + return; + } + const QByteArray jsonBa = jsonFile.readAll(); + jsonFile.close(); + + QJsonParseError jsonError; + const QJsonDocument jDoc = QJsonDocument::fromJson(jsonBa, &jsonError); + if (jDoc.isNull()) { + qCWarning(LOG_ALPINEAPK) << "Failed to parse JSON:" << jsonPath << "!"; + qCWarning(LOG_ALPINEAPK) << jsonError.errorString(); + Q_EMIT downloadFinished(); + return; + } + // JSON structure: + // { + // "urls": [ + // "https://...", "https://...", "https://..." + // ] + // } + const QJsonObject rootObj = jDoc.object(); + const QJsonArray urls = rootObj.value(QLatin1String("urls")).toArray(); + for (const QJsonValue &urlValue : urls) { + const QString url = urlValue.toString(); + m_urls.append(url); + // prefixes are used to avoid name clashes with potential similar + // URL paths from other JSON files. Json file basename is used + // as prefix + m_urlPrefixes.insert(url, jsonBaseName); + } +} + +QString AppstreamDataDownloader::calcLocalFileSavePath(const QUrl &urlToDownload) +{ + // we are adding a prefix here to local file name to avoid possible + // file name clashes with files from other JSONs + const QString urlPrefix = m_urlPrefixes.value(urlToDownload.toString(), QString()); + const QFileInfo urlInfo(urlToDownload.path()); + const QString localCacheFile = AppstreamDataDownloader::appStreamCacheDir() + QDir::separator() + urlPrefix + QLatin1Char('_') + urlInfo.fileName(); + // aka "~/.cache/discover/external_appstream_data/urlPrefix_fileName.xml.gz" + return localCacheFile; +} + +void AppstreamDataDownloader::start() +{ + m_urls.clear(); + // load json files with appdata URLs configuration + const QString path = QStandardPaths::locate( + QStandardPaths::GenericDataLocation, + QLatin1String("libdiscover/external-appstream-urls"), + QStandardPaths::LocateDirectory); + if (path.isEmpty()) { + qCWarning(LOG_ALPINEAPK) << "external-appstream-urls directory does not exist."; + return; + } + + QDir jsonsDir(path); + // search for all JSON files in that directory and load each one + QFileInfoList fileList = jsonsDir.entryInfoList({QStringLiteral("*.json")}, QDir::Files); + for (const QFileInfo &fi : fileList) { + qCDebug(LOG_ALPINEAPK) << " reading URLs JSON: " << fi.absoluteFilePath(); + loadUrlsJson(fi.absoluteFilePath()); + } + + qCDebug(LOG_ALPINEAPK) << "appstream_downloader: urls:" << m_urls; + + // check if download is needed at all, maybe all files are already up to date? + + appStreamCacheDir(); // can create a cache dir if not exists + + const QDateTime dtNow = QDateTime::currentDateTime(); + m_urlsToDownload.clear(); + for (const QString &url : m_urls) { + const QUrl urlToDownload(url, QUrl::TolerantMode); + const QString localCacheFile = calcLocalFileSavePath(urlToDownload); + const QFileInfo localFi(localCacheFile); + if (localFi.exists()) { + int modifiedSecsAgo = localFi.lastModified().secsTo(dtNow); + if (modifiedSecsAgo >= m_cacheExpireSeconds) { + qCDebug(LOG_ALPINEAPK) << " appstream metadata file: " << localFi.fileName() << " was last modified " << modifiedSecsAgo + << " seconds ago, need to download"; + m_urlsToDownload.append(url); + } + } else { + // locally downloaded file does not even exist, we need to download it + m_urlsToDownload.append(url); + qCDebug(LOG_ALPINEAPK) << " appstream metadata file: " << localFi.fileName() + << " does not exist, queued for downloading"; + } + } + + if (!m_urlsToDownload.isEmpty()) { + // some files are outdated; download is needed + qCDebug(LOG_ALPINEAPK) << "appstream_downloader: We will need to download " + << m_urlsToDownload.size() << " file(s)"; + + const QString discoverVersion(QStringLiteral("plasma-discover %1").arg(DiscoverVersion::version)); + + m_jobs.clear(); + for (const QString &sUrl : qAsConst(m_urlsToDownload)) { + const QUrl url(sUrl, QUrl::TolerantMode); + KIO::TransferJob *job = KIO::get(url, KIO::LoadType::Reload, KIO::JobFlag::HideProgressInfo); + job->addMetaData(QLatin1String("UserAgent"), discoverVersion); + + m_jobTracker->registerJob(job); + + QObject::connect(job, &KJob::result, this, &AppstreamDataDownloader::onJobResult); + QObject::connect(job, &KIO::TransferJob::data, this, &AppstreamDataDownloader::onJobData); + + m_jobs.push_back(job); + } + } else { + // no need to download anything + qCDebug(LOG_ALPINEAPK) << "appstream_downloader: All appstream data files " + "are up to date, not downloading anything"; + Q_EMIT downloadFinished(); + return; + } +} + +void AppstreamDataDownloader::onJobData(KIO::Job *job, const QByteArray &data) +{ + KIO::TransferJob *tjob = qobject_cast(job); + if (data.size() < 1) { + return; + } + // while downloading, save data to temporary file + const QString filePath = calcLocalFileSavePath(tjob->url()) + QLatin1String(".tmp"); + QFile fout(filePath); + if (!fout.open(QIODevice::WriteOnly | QIODevice::Append)) { + qCWarning(LOG_ALPINEAPK) << "appstream_downloader: failed to write: " << filePath; + return; + } + fout.write(data); + fout.close(); +} + +void AppstreamDataDownloader::onJobResult(KJob *job) +{ + KIO::TransferJob *tjob = qobject_cast(job); + m_jobs.removeOne(tjob); + m_jobTracker->unregisterJob(tjob); + + const QString localCacheFile = calcLocalFileSavePath(tjob->url()); + const QString localCacheFileTmp = localCacheFile + QLatin1String(".tmp"); + + if (tjob->error()) { + qCWarning(LOG_ALPINEAPK) << "appstream_downloader: failed to download: " << tjob->url(); + qCWarning(LOG_ALPINEAPK) << tjob->errorString(); + // error cleanup - remove temp file + QFile::remove(localCacheFileTmp); + } else { + // success - rename tmp file to real + QFile::remove(localCacheFile); // just in case, or QFile::rename() will fail + QFile::rename(localCacheFileTmp, localCacheFile); + m_cacheWasUpdated = true; + qCDebug(LOG_ALPINEAPK) << "appstream_downloader: saved: " << localCacheFile; + } + + tjob->deleteLater(); + + qCDebug(LOG_ALPINEAPK).nospace() << "appstream_downloader: " << localCacheFile << " request finished (" << m_jobs.size() << " left)"; + + if (m_jobs.isEmpty()) { + qCDebug(LOG_ALPINEAPK) << "appstream_downloader: all downloads have finished!"; + Q_EMIT downloadFinished(); + } +} diff --git a/libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.h b/libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.h new file mode 100644 index 000000000..1b3cc6e28 --- /dev/null +++ b/libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.h @@ -0,0 +1,156 @@ +/*************************************************************************** + * Copyright © 2020 Alexey Min * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef AlpineAppstreamDataDownloader_H +#define AlpineAppstreamDataDownloader_H + +#include +#include +#include +#include +#include +#include + +class KJob; +class KUiServerJobTracker; +namespace KIO +{ +class Job; +class TransferJob; +} + +/** + * @class AppstreamDataDownloader + * + * @details The job of this class is to download appstream data + * gzipped XMLs from some web server. + * + * Some distros (for example, Alpine Linux) do not provide + * appstream data as installable package, instead they host it + * on the internet (at https://appstream.alpinelinux.org) and + * software center app has to download them on its own. + * + * Those files are not very large (from few kilobytes to couple + * of megabytes) but we still need them. + * + * URLs to download archives from are stored in JSON files in + * /usr/share/libdiscover/external-appstream-urls/ directory + * (QStandardPaths::GenericDataLocation/libdiscover/external-appstream-urls + * from C++/Qt code, ${DATA_INSTALL_DIR}/libdiscover/external-appstream-urls + * from cmake). + * + * JSON file format: + * --------------------- + * { + * "urls": [ + * "https://url1", + * "https://url2", + * ... + * ] + * } + * --------------------- + * These JSON files, if needed, should be provided by the linux + * distribution. + * + * This class can load any amount of those JSON files, + * fetch URLs from them and download all files pointed by + * those URLs to discover's cache directory: + * "~/.cache/discover/external_appstream_data" (aka + * QStandardPaths::CacheLocation). Use + * getAppStreamCacheDir() to get this path. + * If files are already present in cache and not outdated, + * they are not downloaded again. Default cache expiration + * time is 7 days and can be tweaked using + * setCacheExpirePeriodSecs(). + */ +class AppstreamDataDownloader: public QObject +{ + Q_OBJECT +public: + explicit AppstreamDataDownloader(QObject *parent = nullptr); + + /** + * Use return value of this function to add extra metadata + * directories to AppStream loader, in case of AppStreamQt: + * AppStream::Pool::addMetadataLocation(). + * This method creates a cache dir if it does not exist. + * @return directory where downloaded files are stored. + */ + static QString appStreamCacheDir(); + + /** + * Call this after receiving downloadFinished() signal to + * test if there actually was something new downloaded. + * @return true, if new files were actually downloaded, or + * false if files already present in cache are up to date. + */ + bool cacheWasUpdated() const { return m_cacheWasUpdated; } + + /** + * @return cache expire timeout in seconds + */ + qint64 cacheExpirePeriodSecs() const + { + return m_cacheExpireSeconds; + } + + /** + * Set cache expiration timeout. + * @param secs - new cache expiration timeout, in seconds. + */ + void setCacheExpirePeriodSecs(qint64 secs); + +public Q_SLOTS: + /** + * Start the asynchronous download job. + * downloadFinished() signal will be emitted when everything is done. + * start() may finish immediately if all cached files are + * up to date and no downloads are needed. + */ + void start(); + +Q_SIGNALS: + /** + * This signal is emitted when download job is finished. + * To check if there were actual downloads performed, call + * cacheWasUpdated(). + */ + void downloadFinished(); + +private: + QString calcLocalFileSavePath(const QUrl &urlTodownload); + void loadUrlsJson(const QString &path); + +private Q_SLOTS: + void onJobData(KIO::Job *job, const QByteArray &data); + void onJobResult(KJob *job); + +protected: + qint64 m_cacheExpireSeconds = 7 * 24 * 3600; + QStringList m_urls; + QStringList m_urlsToDownload; + QHash m_urlPrefixes; + bool m_cacheWasUpdated = false; + + QList m_jobs; + KUiServerJobTracker *m_jobTracker; +}; + +#endif diff --git a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt index 0d657c6ab..08c679a01 100644 --- a/libdiscover/backends/AlpineApkBackend/CMakeLists.txt +++ b/libdiscover/backends/AlpineApkBackend/CMakeLists.txt @@ -16,6 +16,8 @@ set(alpineapkbackend_SRCS AlpineApkUpdater.h AlpineApkTransaction.cpp AlpineApkTransaction.h + AppstreamDataDownloader.h + AppstreamDataDownloader.cpp ) ecm_qt_declare_logging_category( -- GitLab From d8a3d8dd7fea654421f9120e13e242d03e73240a Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Fri, 29 Jan 2021 12:17:48 +0300 Subject: [PATCH 44/61] AlpineApkBackend: use AppstreamDataDownloader Download AppStream data from external URLs after loading resources initially. Reload AppStream metadata after download has finished. Refactor resource loading process into several subroutines for easier understanding and code sharing. --- .../AlpineApkBackend/AlpineApkBackend.cpp | 166 +++++++++++++----- .../AlpineApkBackend/AlpineApkBackend.h | 10 +- 2 files changed, 131 insertions(+), 45 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp index 8ddcc8e9f..3641142eb 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -24,6 +24,7 @@ #include "AlpineApkTransaction.h" #include "AlpineApkSourcesBackend.h" #include "AlpineApkUpdater.h" +#include "AppstreamDataDownloader.h" #include "alpineapk_backend_logging.h" // generated by ECM #include "resources/SourcesModel.h" @@ -85,16 +86,19 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) SourcesModel::global()->addSourcesBackend(new AlpineApkSourcesBackend(this)); } -void AlpineApkBackend::loadResources() +// this fills in m_appStreamComponents +void AlpineApkBackend::loadAppStreamComponents() { - qCDebug(LOG_ALPINEAPK) << "backend: loading AppStream metadata..."; - AppStream::Pool *appStreamPool = new AppStream::Pool(); appStreamPool->setFlags(AppStream::Pool::FlagReadCollection | AppStream::Pool::FlagReadMetainfo | AppStream::Pool::FlagReadDesktopFiles); appStreamPool->setCacheFlags(AppStream::Pool::CacheFlagUseUser | AppStream::Pool::CacheFlagUseSystem); + + // hey hey! cool stuff + appStreamPool->addMetadataLocation(AppstreamDataDownloader::appStreamCacheDir()); + if (!appStreamPool->load()) { qCWarning(LOG_ALPINEAPK) << "backend: Failed to load appstream data:" << appStreamPool->lastError(); @@ -103,36 +107,32 @@ void AlpineApkBackend::loadResources() qCDebug(LOG_ALPINEAPK) << "backend: loaded AppStream metadata OK:" << m_appStreamComponents.size() << "components."; // collect all categories present in appstream metadata -// QSet collectedCategories; -// for (const AppStream::Component &component : m_appStreamComponents) { -// const QStringList cats = component.categories(); -// for (const QString &cat : cats) { -// collectedCategories.insert(cat); -// } -// } -// for (const QString &cat : collectedCategories) { -// qCDebug(LOG_ALPINEAPK) << " collected category: " << cat; -// m_collectedCategories << cat; -// } + // QSet collectedCategories; + // for (const AppStream::Component &component : m_appStreamComponents) { + // const QStringList cats = component.categories(); + // for (const QString &cat : cats) { + // collectedCategories.insert(cat); + // } + // } + // for (const QString &cat : collectedCategories) { + // qCDebug(LOG_ALPINEAPK) << " collected category: " << cat; + // m_collectedCategories << cat; + // } } delete appStreamPool; - appStreamPool = nullptr; - - qCDebug(LOG_ALPINEAPK) << "backend: populating resources..."; - emit this->passiveMessage(i18n("Loading, please wait...")); - - if (m_apkdb.open(QtApk::QTAPK_OPENF_READONLY)) { - m_availablePackages = m_apkdb.getAvailablePackages(); - m_installedPackages = m_apkdb.getInstalledPackages(); - m_apkdb.close(); - } +} +// this uses m_appStreamComponents and m_availablePackages +// to fill in m_resourcesAppstreamData +void AlpineApkBackend::parseAppStreamMetadata() +{ if (m_availablePackages.size() > 0) { - for (const QtApk::Package &pkg: m_availablePackages) { + + for (const QtApk::Package &pkg: qAsConst(m_availablePackages)) { // try to find appstream data for this package AppStream::Component appstreamComponent; - for (const auto& appsC : m_appStreamComponents) { + for (const auto& appsC : qAsConst(m_appStreamComponents)) { // find result which package name is exactly the one we want if (appsC.packageNames().contains(pkg.name)) { // workaround for kate (Kate Sessions is found first, but @@ -153,11 +153,6 @@ void AlpineApkBackend::loadResources() m_resourcesAppstreamData.insert(key, appstreamComponent); } } - - qCDebug(LOG_ALPINEAPK) << " available" << m_availablePackages.size() - << "packages"; - qCDebug(LOG_ALPINEAPK) << " installed" << m_installedPackages.size() - << "packages"; } static AbstractResource::Type toDiscoverResourceType(const AppStream::Component &component) @@ -180,28 +175,81 @@ static AbstractResource::Type toDiscoverResourceType(const AppStream::Component return resType; } -void AlpineApkBackend::onLoadResourcesFinished() +void AlpineApkBackend::fillResourcesAndApplyAppStreamData() { - qCDebug(LOG_ALPINEAPK) << "backend: appstream data loaded and sorted; fill in resources"; - + // now the tricky part - we need to reapply appstream component metadata to each resource if (m_availablePackages.size() > 0) { for (const QtApk::Package &pkg: m_availablePackages) { const QString key = pkg.name.toLower(); - AppStream::Component &appstreamComponent = m_resourcesAppstreamData[key]; - const AbstractResource::Type resType = toDiscoverResourceType(appstreamComponent); + AppStream::Component &appsComponent = m_resourcesAppstreamData[key]; + const AbstractResource::Type resType = toDiscoverResourceType(appsComponent); + + AlpineApkResource *res = m_resources.value(key, nullptr); + if (res == nullptr) { + // during first run of this function during initial load + // m_resources hash is empty, so we need to insert new items + res = new AlpineApkResource(pkg, appsComponent, resType, this); + res->setCategoryName(QStringLiteral("alpine_packages")); + res->setOriginSource(QStringLiteral("apk")); + res->setSection(QStringLiteral("dummy")); + m_resources.insert(key, res); + QObject::connect(res, &AlpineApkResource::stateChanged, + this, &AlpineApkBackend::updatesCountChanged); + } else { + // this is not an initial run, just update existing resource + res->setAppStreamData(appsComponent); + } + } + } +} - AlpineApkResource *res = new AlpineApkResource(pkg, appstreamComponent, resType, this); - res->setCategoryName(QStringLiteral("alpine_packages")); - res->setOriginSource(QStringLiteral("apk")); - res->setSection(QStringLiteral("dummy")); +void AlpineApkBackend::reloadAppStreamMetadata() +{ + // mark us as "Loading..." + m_fetching = true; + emit fetchingChanged(); - m_resources.insert(key, res); - QObject::connect(res, &AlpineApkResource::stateChanged, - this, &AlpineApkBackend::updatesCountChanged); - } + loadAppStreamComponents(); + parseAppStreamMetadata(); + fillResourcesAndApplyAppStreamData(); + + // mark us as "done loading" + m_fetching = false; + emit fetchingChanged(); +} + +// this function is executed in the background thread +void AlpineApkBackend::loadResources() +{ + Q_EMIT this->passiveMessage(i18n("Loading, please wait...")); + + qCDebug(LOG_ALPINEAPK) << "backend: loading AppStream metadata..."; + + loadAppStreamComponents(); + + qCDebug(LOG_ALPINEAPK) << "backend: populating resources..."; + + if (m_apkdb.open(QtApk::QTAPK_OPENF_READONLY)) { + m_availablePackages = m_apkdb.getAvailablePackages(); + m_installedPackages = m_apkdb.getInstalledPackages(); + m_apkdb.close(); } + parseAppStreamMetadata(); + + qCDebug(LOG_ALPINEAPK) << " available" << m_availablePackages.size() + << "packages"; + qCDebug(LOG_ALPINEAPK) << " installed" << m_installedPackages.size() + << "packages"; +} + +void AlpineApkBackend::onLoadResourcesFinished() +{ + qCDebug(LOG_ALPINEAPK) << "backend: appstream data loaded and sorted; fill in resources"; + + fillResourcesAndApplyAppStreamData(); + // update "installed/not installed" state if (m_installedPackages.size() > 0) { for (const QtApk::Package &pkg: m_installedPackages) { @@ -221,6 +269,36 @@ void AlpineApkBackend::onLoadResourcesFinished() // schedule check for updates 1 sec after we've loaded all resources QTimer::singleShot(1000, this, &AlpineApkBackend::checkForUpdates); + + // AppStream appdata downloader can download updated metadata files + // in a background thread. When potential download is finished, + // appstream data will be reloaded. + m_appstreamDownloader = new AppstreamDataDownloader(nullptr); + QObject::connect(m_appstreamDownloader, &AppstreamDataDownloader::downloadFinished, + this, &AlpineApkBackend::onAppstreamDataDownloaded, Qt::QueuedConnection); + m_appstreamDownloader->start(); +} + +void AlpineApkBackend::onAppstreamDataDownloaded() +{ + if (m_appstreamDownloader) { + if (m_appstreamDownloader->cacheWasUpdated()) { + // it means we need to reload previously loaded appstream metadata + // m_fetching is true if loadResources() is still executing + // in a background thread + if (!m_fetching) { + qCDebug(LOG_ALPINEAPK) << "AppStream metadata was updated; re-applying it to all resources"; + reloadAppStreamMetadata(); + } else { + qCWarning(LOG_ALPINEAPK) << "AppStream metadata was updated, but cannot apply it: still fetching"; + // it should not really happen, but if it happens, + // then downloaded metadata will be used on the next + // discover launch anyway. + } + } + delete m_appstreamDownloader; + m_appstreamDownloader = nullptr; + } } QVector AlpineApkBackend::category() const diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h index 2ec8b00b6..07b6b7be8 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.h @@ -22,8 +22,9 @@ #define AlpineApkBackend_H #include -#include + #include +#include #include @@ -32,6 +33,7 @@ class AlpineApkReviewsBackend; class AlpineApkUpdater; class AlpineApkResource; +class AppstreamDataDownloader; class KJob; class QTimer; @@ -66,8 +68,13 @@ public Q_SLOTS: private Q_SLOTS: void finishCheckForUpdates(); + void loadAppStreamComponents(); + void parseAppStreamMetadata(); + void reloadAppStreamMetadata(); + void fillResourcesAndApplyAppStreamData(); void loadResources(); void onLoadResourcesFinished(); + void onAppstreamDataDownloaded(); public: QtApk::Database *apkdb() { return &m_apkdb; } @@ -86,6 +93,7 @@ private: QList m_appStreamComponents; // QVector m_collectedCategories; QFutureWatcher m_voidFutureWatcher; + AppstreamDataDownloader *m_appstreamDownloader; }; #endif // AlpineApkBackend_H -- GitLab From 2bbdf1dfdd2af36287afb228d5acf6947ef8c3e2 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Mon, 1 Feb 2021 14:24:25 +0300 Subject: [PATCH 45/61] AlpineApkUpdater: fix progress reporting for system upgrade Connect to progress updates from helper and implement AlpineApkUpdater::progress() getter function. --- .../AlpineApkBackend/AlpineApkUpdater.cpp | 23 +++++++++++++++---- .../AlpineApkBackend/AlpineApkUpdater.h | 3 ++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp index 0bbbce1c0..30399369b 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp @@ -91,8 +91,7 @@ bool AlpineApkUpdater::hasUpdates() const qreal AlpineApkUpdater::progress() const { - qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; - return 0.0; + return m_upgradeProgress; } void AlpineApkUpdater::removeResources(const QList &apps) @@ -171,10 +170,15 @@ void AlpineApkUpdater::start() // run upgrade with elevated privileges KAuth::ExecuteJob *reply = ActionFactory::createUpgradeAction(); if (!reply) return; + QObject::connect(reply, &KAuth::ExecuteJob::result, this, &AlpineApkUpdater::handleKAuthUpgradeHelperReply); + // qOverload is needed because of conflict with getter named percent() + QObject::connect(reply, QOverload::of(&KAuth::ExecuteJob::percent), + this, &AlpineApkUpdater::handleKAuthUpgradeHelperProgress); m_progressing = true; + m_upgradeProgress = 0.0; Q_EMIT progressingChanged(m_progressing); reply->start(); @@ -239,6 +243,17 @@ void AlpineApkUpdater::handleKAuthUpdateHelperProgress(KJob *job, unsigned long Q_EMIT progressChanged(static_cast(percent)); } +void AlpineApkUpdater::handleKAuthUpgradeHelperProgress(KJob *job, unsigned long percent) +{ + Q_UNUSED(job) + qCDebug(LOG_ALPINEAPK) << " upgrade progress: " << percent; + qreal newProgress = static_cast(percent); + if (newProgress != m_upgradeProgress) { + m_upgradeProgress = newProgress; + Q_EMIT progressChanged(m_upgradeProgress); + } +} + void AlpineApkUpdater::handleKAuthUpgradeHelperReply(KJob *job) { KAuth::ExecuteJob *reply = static_cast(job); @@ -258,8 +273,8 @@ void AlpineApkUpdater::handleKAuthUpgradeHelperReply(KJob *job) handleKAuthHelperError(reply, replyData); } - // we are not in the state "Fetching updates" now, update UI - Q_EMIT checkForUpdatesFinished(); + m_progressing = false; + Q_EMIT progressingChanged(m_progressing); } void AlpineApkUpdater::handleKAuthHelperError( diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h index 0ee2fcb47..6ca3ce073 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.h @@ -159,6 +159,7 @@ public Q_SLOTS: void handleKAuthUpdateHelperProgress(KJob *job, unsigned long percent); // upgrade void handleKAuthUpgradeHelperReply(KJob *job); + void handleKAuthUpgradeHelperProgress(KJob *job, unsigned long percent); //void transactionRemoved(Transaction* t); //void cleanup(); @@ -186,7 +187,7 @@ private: // QSet m_upgradeable; // QSet m_pendingResources; bool m_progressing = false; -// qreal m_progress; + qreal m_upgradeProgress = 0.0; // QDateTime m_lastUpdate; // QTimer m_timer; // bool m_canCancel = false; -- GitLab From deb2782619a7359a03c606409070a4f571f07514 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Mon, 1 Feb 2021 14:26:29 +0300 Subject: [PATCH 46/61] AlpineApkUpdater: remove useless debug prints and comments --- .../backends/AlpineApkBackend/AlpineApkUpdater.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp index 30399369b..14df959cd 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp @@ -40,8 +40,6 @@ AlpineApkUpdater::AlpineApkUpdater(AbstractResourcesBackend *parent) void AlpineApkUpdater::prepare() { - qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; - QtApk::Database *db = m_backend->apkdb(); if (db->isOpen()) { @@ -85,7 +83,6 @@ void AlpineApkUpdater::prepare() bool AlpineApkUpdater::hasUpdates() const { - // qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO << m_updatesCount; return (m_updatesCount > 0); } @@ -96,22 +93,18 @@ qreal AlpineApkUpdater::progress() const void AlpineApkUpdater::removeResources(const QList &apps) { - // qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; const QSet checkSet = kToSet(apps); m_markedToUpdate -= checkSet; } void AlpineApkUpdater::addResources(const QList &apps) { - //Q_UNUSED(apps) - //qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; const QSet checkSet = kToSet(apps); m_markedToUpdate += checkSet; } QList AlpineApkUpdater::toUpdate() const { - // qCDebug(LOG_ALPINEAPK) << Q_FUNC_INFO; return m_allUpdateable.values(); } @@ -191,7 +184,6 @@ void AlpineApkUpdater::proceed() int AlpineApkUpdater::updatesCount() { - // qDebug(LOG_ALPINEAPK) << Q_FUNC_INFO << m_updatesCount; return m_updatesCount; } -- GitLab From 68ccfb5f1f9eaa67a9164e0dbcee8920de7369a0 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Wed, 24 Feb 2021 14:51:27 +0300 Subject: [PATCH 47/61] AlpineApkSourcesBackend: port away from QAction follow-up to 4bf3f59adb82e2ef38df98073ee39e6d20e56921 fixed after b5e913d2231a3d899b902a015bfa6be04524d8a3 --- .../AlpineApkBackend/AlpineApkSourcesBackend.cpp | 13 ++++++------- .../AlpineApkBackend/AlpineApkSourcesBackend.h | 9 ++++++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp index a126483a9..c10ebdcb5 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp @@ -18,6 +18,8 @@ * along with this program. If not, see . * ***************************************************************************/ +#include "resources/DiscoverAction.h" + #include "AlpineApkSourcesBackend.h" #include "AlpineApkAuthActionFactory.h" #include "alpineapk_backend_logging.h" // generated by ECM @@ -36,16 +38,13 @@ AlpineApkSourcesBackend::AlpineApkSourcesBackend(AbstractResourcesBackend *parent) : AbstractSourcesBackend(parent) , m_sourcesModel(new QStandardItemModel(this)) - , m_refreshAction(new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), - QStringLiteral("Reload"), this)) - , m_saveAction(new QAction(QIcon::fromTheme(QStringLiteral("document-save")), - QStringLiteral("Save"), this)) - // ^^ unfortunately QML side ignores icons for custom actions + , m_refreshAction(new DiscoverAction(QStringLiteral("view-refresh"), QStringLiteral("Reload"), this)) + , m_saveAction(new DiscoverAction(QStringLiteral("document-save"), QStringLiteral("Save"), this)) { loadSources(); - QObject::connect(m_refreshAction, &QAction::triggered, + QObject::connect(m_refreshAction, &DiscoverAction::triggered, this, &AlpineApkSourcesBackend::loadSources); - QObject::connect(m_saveAction, &QAction::triggered, + QObject::connect(m_saveAction, &DiscoverAction::triggered, this, &AlpineApkSourcesBackend::saveSources); // track enabling/disabling repo source QObject::connect(m_sourcesModel, &QStandardItemModel::itemChanged, diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.h b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.h index eacda22dc..c655d3d16 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.h @@ -21,11 +21,14 @@ #ifndef ALPINEAPKSOURCESBACKEND_H #define ALPINEAPKSOURCESBACKEND_H -#include +#include "resources/AbstractSourcesBackend.h" + #include #include +class DiscoverAction; + class AlpineApkSourcesBackend : public AbstractSourcesBackend { public: @@ -49,8 +52,8 @@ private: void onItemChanged(QStandardItem* item); QStandardItemModel *m_sourcesModel = nullptr; - QAction *m_refreshAction = nullptr; - QAction *m_saveAction = nullptr; + DiscoverAction *m_refreshAction = nullptr; + DiscoverAction *m_saveAction = nullptr; QVector m_repos; }; -- GitLab From e8d189eb9f88edbc809aa92101b4f3824e7e467d Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Thu, 25 Feb 2021 07:39:50 +0300 Subject: [PATCH 48/61] AlpineApkBackend: Use shorter backend name: it's shown in Install button See https://invent.kde.org/plasma/discover/-/commit/ ce89d530de3414f25bb4b48a1933a52bf37be922 --- libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp index 3641142eb..88b37cf0e 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -485,7 +485,7 @@ void AlpineApkBackend::finishCheckForUpdates() QString AlpineApkBackend::displayName() const { - return i18nc("Backend plugin display name", "Alpine APK backend"); + return i18nc("Backend plugin display name", "Alpine APK"); } bool AlpineApkBackend::hasApplications() const -- GitLab From 851df77c083a4d129439a849fb553f1297748133 Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Thu, 25 Feb 2021 07:40:41 +0300 Subject: [PATCH 49/61] AlpineApkResource: use Alpine Linux logo as backend icon --- libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp index 8f493a490..459347061 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp @@ -325,7 +325,7 @@ QString AlpineApkResource::author() const QString AlpineApkResource::sourceIcon() const { - return QStringLiteral("player-time"); + return QStringLiteral("alpine-linux-logo-icon"); } QDate AlpineApkResource::releaseDate() const -- GitLab From 1d00eac0bc5c1f13b797cd85518d356625570ae3 Mon Sep 17 00:00:00 2001 From: Alexey Minnekhanov Date: Sat, 15 May 2021 05:52:54 +0300 Subject: [PATCH 50/61] KAuth helper: don't ask for password Allow everyone to install/remove/upgrade packages without asking for password. Polkit-kde auth dialog is not mobile friendly. Fixes https://gitlab.com/postmarketOS/pmaports/-/issues/1036 --- .../org.kde.discover.alpineapkbackend.actions | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions index c9bb5f9f2..f9db78dbb 100644 --- a/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions +++ b/libdiscover/backends/AlpineApkBackend/org.kde.discover.alpineapkbackend.actions @@ -1,5 +1,5 @@ [org.kde.discover.alpineapkbackend.pkgmgmt] Name=Package management -Description=Install or remove packages, upgrade system -Policy=auth_admin +Description=Install/remove/upgrade packages +Policy=yes Persistence=session -- GitLab From 0247a5d113569286a7796fd799e6a253e283330b Mon Sep 17 00:00:00 2001 From: Alexey Minnekhanov Date: Sun, 20 Jun 2021 22:58:29 +0300 Subject: [PATCH 51/61] HACK: AlpineApkResource: Workaround broken Alpine metadata thumbnails from Alpine metadata are unusable --- .../AlpineApkBackend/AlpineApkResource.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp index 459347061..d7ac49012 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp @@ -220,7 +220,21 @@ void AlpineApkResource::fetchChangelog() void AlpineApkResource::fetchScreenshots() { if (hasAppStreamData()) { - const QPair, QList > sc = AppStreamUtils::fetchScreenshots(m_appsC); + /*const*/ QPair, QList> sc = AppStreamUtils::fetchScreenshots(m_appsC); + if (sc.first.size() > 0) { + const QUrl &url = sc.first.first(); + if (url.isRelative()) { + // This is a hack to fix broken Alpine Linux appstream + // metadata: somehow generated thumbnails URLs are relative + // and always fail to load (not present in metadata at all) + // We work around this by using full url, which is veyr not + // good - they're fetched by http://... + // It would be great if thumbnails were hosted locally. + if (sc.second.size() > 0) { + sc.first = sc.second; + } + } + } Q_EMIT screenshotsFetched(sc.first, sc.second); } } -- GitLab From 907dc0559d362c9948a31a27acfd4f35328a9c7a Mon Sep 17 00:00:00 2001 From: Alexey Min Date: Sat, 17 Jul 2021 14:10:08 +0300 Subject: [PATCH 52/61] AlpineApk{Updater,Transaction}: Fix deprecation warn with KF5>=5.80 Use percentChanged() signal instead of ambiguous overloaded percent() signal. --- .../AlpineApkBackend/AlpineApkTransaction.cpp | 5 +++++ .../backends/AlpineApkBackend/AlpineApkUpdater.cpp | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp index 26bf1bd0d..0f346e843 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp @@ -29,6 +29,7 @@ #include // KF5 +#include #include #include @@ -95,7 +96,11 @@ void AlpineApkTransaction::startTransaction() }); // get progress reports for this job +#if KCOREADDONS_VERSION < QT_VERSION_CHECK(5,80,0) QObject::connect(reply, QOverload::of(&KAuth::ExecuteJob::percent), this, +#else + QObject::connect(reply, &KAuth::ExecuteJob::percentChanged, this, +#endif [this](KJob *job, unsigned long percent) { Q_UNUSED(job) if (percent >= 40 && role() == InstallRole) { diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp index 14df959cd..4b09c7d05 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp @@ -25,6 +25,7 @@ #include "alpineapk_backend_logging.h" #include "utils.h" +#include #include #include @@ -166,9 +167,14 @@ void AlpineApkUpdater::start() QObject::connect(reply, &KAuth::ExecuteJob::result, this, &AlpineApkUpdater::handleKAuthUpgradeHelperReply); +#if KCOREADDONS_VERSION < QT_VERSION_CHECK(5,80,0) // qOverload is needed because of conflict with getter named percent() QObject::connect(reply, QOverload::of(&KAuth::ExecuteJob::percent), this, &AlpineApkUpdater::handleKAuthUpgradeHelperProgress); +#else + QObject::connect(reply, &KAuth::ExecuteJob::percentChanged, + this, &AlpineApkUpdater::handleKAuthUpgradeHelperProgress); +#endif m_progressing = true; m_upgradeProgress = 0.0; @@ -197,9 +203,14 @@ void AlpineApkUpdater::startCheckForUpdates() if (!reply) return; QObject::connect(reply, &KAuth::ExecuteJob::result, this, &AlpineApkUpdater::handleKAuthUpdateHelperReply); +#if KCOREADDONS_VERSION < QT_VERSION_CHECK(5,80,0) // qOverload is needed because of conflict with getter named percent() QObject::connect(reply, QOverload::of(&KAuth::ExecuteJob::percent), this, &AlpineApkUpdater::handleKAuthUpdateHelperProgress); +#else + QObject::connect(reply, &KAuth::ExecuteJob::percentChanged, + this, &AlpineApkUpdater::handleKAuthUpdateHelperProgress); +#endif m_progressing = true; Q_EMIT progressingChanged(m_progressing); -- GitLab From 9200c2c8c7eedfedd02fee5ad550a7a23a70cec3 Mon Sep 17 00:00:00 2001 From: Alexey Minnekhanov Date: Fri, 21 Jan 2022 07:33:33 +0300 Subject: [PATCH 53/61] AppstreamDataDownloader: support new Alpine metadata file names This is needed to avoid name clash caused by new file names. Also remove old-format metadata files, to avoid conflicts between metadata in new files and metadata in possibly existing old files. --- .../AppstreamDataDownloader.cpp | 91 ++++++++++++++----- .../AppstreamDataDownloader.h | 6 +- 2 files changed, 72 insertions(+), 25 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.cpp b/libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.cpp index e791b2e02..27c2ad679 100644 --- a/libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.cpp +++ b/libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.cpp @@ -103,13 +103,48 @@ QString AppstreamDataDownloader::calcLocalFileSavePath(const QUrl &urlToDownload { // we are adding a prefix here to local file name to avoid possible // file name clashes with files from other JSONs + // urlToDownload looks like: + // "https://appstream.alpinelinux.org/data/edge/main/Components-main-aarch64.xml.gz" + // "https://appstream.alpinelinux.org/data/edge/community/Components-community-aarch64.xml.gz" + // future update will change them to this form: + // "https://appstream.alpinelinux.org/data/edge/main/Components-aarch64.xml.gz" + // "https://appstream.alpinelinux.org/data/edge/community/Components-aarch64.xml.gz" + // so, file names will clash. We also need to have full URL to affect local file name + // to avoid name clashes. const QString urlPrefix = m_urlPrefixes.value(urlToDownload.toString(), QString()); - const QFileInfo urlInfo(urlToDownload.path()); - const QString localCacheFile = AppstreamDataDownloader::appStreamCacheDir() + QDir::separator() + urlPrefix + QLatin1Char('_') + urlInfo.fileName(); - // aka "~/.cache/discover/external_appstream_data/urlPrefix_fileName.xml.gz" + const QString urlPathHash = urlToDownload.path().replace(QLatin1Char('/'), QLatin1Char('_')); + const QString localCacheFile = AppstreamDataDownloader::appStreamCacheDir() + QDir::separator() + urlPrefix + QLatin1Char('_') + urlPathHash; + + // new "~/.cache/discover/external_appstream_data/alpine-appstream-data__data_edge_main_Components-aarch64.xml.gz" + return localCacheFile; } +QString AppstreamDataDownloader::calcLocalFileSavePathOld(const QUrl &urlToDownload) +{ + // Calculate what file name was there for old format. + // We keep a list of "old" file names so we can delete them. + // "~/.cache/discover/external_appstream_data/alpine-appstream-data_Components-main-aarch64.xml.gz" + // ^ ^/| ^ ^ + // | appstream cache dir -------------------|/|--- urlPrefix -----|_|-- file name + const QString urlPrefix = m_urlPrefixes.value(urlToDownload.toString(), QString()); + const QString urlFileName = QFileInfo(urlToDownload.path()).fileName(); + const QString oldFormatFileName = AppstreamDataDownloader::appStreamCacheDir() + QDir::separator() + urlPrefix + QLatin1Char('_') + urlFileName; + return oldFormatFileName; +} + +void AppstreamDataDownloader::cleanupOldCachedFiles() +{ + if (m_oldFormatFileNames.isEmpty()) { + return; + } + qCDebug(LOG_ALPINEAPK) << "appstream_downloader: removing old files:"; + for (const QString &oldFn : m_oldFormatFileNames) { + bool ok = QFile::remove(oldFn); + qCDebug(LOG_ALPINEAPK) << " " << oldFn << (ok ? "OK" : "Fail"); + } +} + void AppstreamDataDownloader::start() { m_urls.clear(); @@ -138,7 +173,10 @@ void AppstreamDataDownloader::start() appStreamCacheDir(); // can create a cache dir if not exists const QDateTime dtNow = QDateTime::currentDateTime(); + m_urlsToDownload.clear(); + m_oldFormatFileNames.clear(); + for (const QString &url : m_urls) { const QUrl urlToDownload(url, QUrl::TolerantMode); const QString localCacheFile = calcLocalFileSavePath(urlToDownload); @@ -156,35 +194,39 @@ void AppstreamDataDownloader::start() qCDebug(LOG_ALPINEAPK) << " appstream metadata file: " << localFi.fileName() << " does not exist, queued for downloading"; } - } - if (!m_urlsToDownload.isEmpty()) { - // some files are outdated; download is needed - qCDebug(LOG_ALPINEAPK) << "appstream_downloader: We will need to download " - << m_urlsToDownload.size() << " file(s)"; - - const QString discoverVersion(QStringLiteral("plasma-discover %1").arg(DiscoverVersion::version)); - - m_jobs.clear(); - for (const QString &sUrl : qAsConst(m_urlsToDownload)) { - const QUrl url(sUrl, QUrl::TolerantMode); - KIO::TransferJob *job = KIO::get(url, KIO::LoadType::Reload, KIO::JobFlag::HideProgressInfo); - job->addMetaData(QLatin1String("UserAgent"), discoverVersion); - - m_jobTracker->registerJob(job); - - QObject::connect(job, &KJob::result, this, &AppstreamDataDownloader::onJobResult); - QObject::connect(job, &KIO::TransferJob::data, this, &AppstreamDataDownloader::onJobData); + // create a set of possible cached files with old name format + const QString localCacheFileOld = calcLocalFileSavePathOld(urlToDownload); + m_oldFormatFileNames.insert(localCacheFileOld); + } - m_jobs.push_back(job); - } - } else { + if (m_urlsToDownload.isEmpty()) { // no need to download anything qCDebug(LOG_ALPINEAPK) << "appstream_downloader: All appstream data files " "are up to date, not downloading anything"; + cleanupOldCachedFiles(); Q_EMIT downloadFinished(); return; } + + // If we're here, some files are outdated; download is needed + qCDebug(LOG_ALPINEAPK) << "appstream_downloader: We will need to download " << m_urlsToDownload.size() << " file(s)"; + + const QString discoverVersion(QStringLiteral("plasma-discover %1").arg(DiscoverVersion::version)); + + m_jobs.clear(); + for (const QString &sUrl : qAsConst(m_urlsToDownload)) { + const QUrl url(sUrl, QUrl::TolerantMode); + KIO::TransferJob *job = KIO::get(url, KIO::LoadType::Reload, KIO::JobFlag::HideProgressInfo); + job->addMetaData(QLatin1String("UserAgent"), discoverVersion); + + m_jobTracker->registerJob(job); + + QObject::connect(job, &KJob::result, this, &AppstreamDataDownloader::onJobResult); + QObject::connect(job, &KIO::TransferJob::data, this, &AppstreamDataDownloader::onJobData); + + m_jobs.push_back(job); + } } void AppstreamDataDownloader::onJobData(KIO::Job *job, const QByteArray &data) @@ -232,6 +274,7 @@ void AppstreamDataDownloader::onJobResult(KJob *job) if (m_jobs.isEmpty()) { qCDebug(LOG_ALPINEAPK) << "appstream_downloader: all downloads have finished!"; + cleanupOldCachedFiles(); Q_EMIT downloadFinished(); } } diff --git a/libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.h b/libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.h index 1b3cc6e28..390f4f6e2 100644 --- a/libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.h +++ b/libdiscover/backends/AlpineApkBackend/AppstreamDataDownloader.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -135,8 +136,10 @@ Q_SIGNALS: void downloadFinished(); private: - QString calcLocalFileSavePath(const QUrl &urlTodownload); + QString calcLocalFileSavePath(const QUrl &urlToDownload); + QString calcLocalFileSavePathOld(const QUrl &urlToDownload); void loadUrlsJson(const QString &path); + void cleanupOldCachedFiles(); private Q_SLOTS: void onJobData(KIO::Job *job, const QByteArray &data); @@ -146,6 +149,7 @@ protected: qint64 m_cacheExpireSeconds = 7 * 24 * 3600; QStringList m_urls; QStringList m_urlsToDownload; + QSet m_oldFormatFileNames; QHash m_urlPrefixes; bool m_cacheWasUpdated = false; -- GitLab From 8afe048aadf9c95c7b5b66d2d3523113a824c516 Mon Sep 17 00:00:00 2001 From: Alexey Minnekhanov Date: Fri, 21 Jan 2022 07:43:23 +0300 Subject: [PATCH 54/61] Revert "HACK: AlpineApkResource: Workaround broken Alpine metadata" This reverts commit e0a968f536bcc993cd6cf81c00a99f98fd1474b0. I have a hope that this is no longer needed with new metadata. --- .../AlpineApkBackend/AlpineApkResource.cpp | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp index d7ac49012..459347061 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp @@ -220,21 +220,7 @@ void AlpineApkResource::fetchChangelog() void AlpineApkResource::fetchScreenshots() { if (hasAppStreamData()) { - /*const*/ QPair, QList> sc = AppStreamUtils::fetchScreenshots(m_appsC); - if (sc.first.size() > 0) { - const QUrl &url = sc.first.first(); - if (url.isRelative()) { - // This is a hack to fix broken Alpine Linux appstream - // metadata: somehow generated thumbnails URLs are relative - // and always fail to load (not present in metadata at all) - // We work around this by using full url, which is veyr not - // good - they're fetched by http://... - // It would be great if thumbnails were hosted locally. - if (sc.second.size() > 0) { - sc.first = sc.second; - } - } - } + const QPair, QList > sc = AppStreamUtils::fetchScreenshots(m_appsC); Q_EMIT screenshotsFetched(sc.first, sc.second); } } -- GitLab From f74a71991de831772f11c7b9fb1030d2ebba461b Mon Sep 17 00:00:00 2001 From: Alexey Minnekhanov Date: Tue, 8 Feb 2022 21:41:05 +0300 Subject: [PATCH 55/61] Follow upstream changes to quit64 as size follow-up to a1cf31faf1a8aeebf2786715590cf3bb8a5cd472 --- libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp | 4 ++-- libdiscover/backends/AlpineApkBackend/AlpineApkResource.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp index 459347061..2c981ca90 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp @@ -72,9 +72,9 @@ QString AlpineApkResource::comment() return m_pkg.description; } -int AlpineApkResource::size() +quint64 AlpineApkResource::size() { - return static_cast(m_pkg.size); + return m_pkg.size; } QUrl AlpineApkResource::homepage() diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h index 5304a8773..fec02a994 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.h @@ -44,7 +44,7 @@ public: QString availableVersion() const override; QString installedVersion() const override; QJsonArray licenses() override; - int size() override; + quint64 size() override; QUrl homepage() override; QUrl helpURL() override; QUrl bugURL() override; -- GitLab From 45a9f9a6b22eb0838ebdd5653f9a599c16d54fdc Mon Sep 17 00:00:00 2001 From: Alexey Minnekhanov Date: Sat, 16 Apr 2022 08:42:09 +0300 Subject: [PATCH 56/61] AlpineApkBackend: compatibility with AppStreamQt 0.15 or later use newer API and enum values --- .../backends/AlpineApkBackend/AlpineApkBackend.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp index 88b37cf0e..f53947324 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -90,14 +91,20 @@ AlpineApkBackend::AlpineApkBackend(QObject *parent) void AlpineApkBackend::loadAppStreamComponents() { AppStream::Pool *appStreamPool = new AppStream::Pool(); +#if ASQ_CHECK_VERSION(0, 15, 0) + // use newer API and flags available only since 0.15.0 + appStreamPool->setFlags(AppStream::Pool::FlagLoadOsCollection | AppStream::Pool::FlagLoadOsMetainfo | AppStream::Pool::FlagLoadOsDesktopFiles); + + // AS_FORMAT_STYLE_COLLECTION - Parse AppStream metadata collections (shipped by software distributors) + appStreamPool->addExtraDataLocation(AppstreamDataDownloader::appStreamCacheDir(), AppStream::Metadata::FormatStyleCollection); +#else appStreamPool->setFlags(AppStream::Pool::FlagReadCollection | AppStream::Pool::FlagReadMetainfo | AppStream::Pool::FlagReadDesktopFiles); appStreamPool->setCacheFlags(AppStream::Pool::CacheFlagUseUser | AppStream::Pool::CacheFlagUseSystem); - - // hey hey! cool stuff appStreamPool->addMetadataLocation(AppstreamDataDownloader::appStreamCacheDir()); +#endif if (!appStreamPool->load()) { qCWarning(LOG_ALPINEAPK) << "backend: Failed to load appstream data:" -- GitLab From edcf23a379cf8cd64f417647b85b64917688bfa4 Mon Sep 17 00:00:00 2001 From: Alexey Minnekhanov Date: Sun, 5 Jun 2022 22:04:17 +0300 Subject: [PATCH 57/61] Switch to new-style KAuth headers (KF 5.92 compatibility) KAuthAction -> KAuth/Action, etc --- .../backends/AlpineApkBackend/AlpineApkAuthActionFactory.h | 6 +++--- .../backends/AlpineApkBackend/AlpineApkAuthHelper.cpp | 2 +- libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h | 3 ++- .../backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp | 2 +- .../backends/AlpineApkBackend/AlpineApkTransaction.cpp | 4 ++-- libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp | 4 ++-- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthActionFactory.h b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthActionFactory.h index ff5667f85..b43ca7e24 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthActionFactory.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthActionFactory.h @@ -24,9 +24,9 @@ #include #include -#include -#include -#include +#include +#include +#include namespace ActionFactory { diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp index 97affc013..14d559ba5 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include "AlpineApkAuthHelper.h" diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h index 240e6ed35..55f81d16b 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkAuthHelper.h @@ -21,7 +21,8 @@ #include #include #include -#include + +#include #include diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp index c10ebdcb5..073430894 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkSourcesBackend.cpp @@ -29,7 +29,7 @@ #include // KF5 -#include +#include #include // libapk-qt diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp index 0f346e843..de82546ca 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkTransaction.cpp @@ -29,9 +29,9 @@ #include // KF5 -#include -#include +#include #include +#include AlpineApkTransaction::AlpineApkTransaction(AlpineApkResource *res, Role role) : AlpineApkTransaction(res, {}, role) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp index 4b09c7d05..b36ffb987 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkUpdater.cpp @@ -25,9 +25,9 @@ #include "alpineapk_backend_logging.h" #include "utils.h" -#include -#include +#include #include +#include #include -- GitLab From 14ebd0b5c36995d7cd3a8c037149ec57732a5f91 Mon Sep 17 00:00:00 2001 From: Alexey Minnekhanov Date: Sun, 18 Sep 2022 17:21:43 +0300 Subject: [PATCH 58/61] AlpineApkReviewsBackend: compat with upstream changes Adapt to changes in ee3d6e9c912d901174a44fe96136583086cd6932: add overrides for new abstract methods, move userName() to protected --- .../AlpineApkReviewsBackend.h | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkReviewsBackend.h b/libdiscover/backends/AlpineApkBackend/AlpineApkReviewsBackend.h index 435f845ba..523dd4cb9 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkReviewsBackend.h +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkReviewsBackend.h @@ -32,7 +32,6 @@ class AlpineApkReviewsBackend : public AbstractReviewsBackend public: explicit AlpineApkReviewsBackend(AlpineApkBackend *parent = nullptr); - QString userName() const override { return QStringLiteral("dummy"); } void login() override {} void logout() override {} void registerAndLogin() override {} @@ -42,11 +41,26 @@ public: void deleteReview(Review *) override {} void fetchReviews(AbstractResource *app, int page = 1) override; bool isFetching() const override { return false; } - bool isReviewable() const override { return false; } - void submitReview(AbstractResource *, const QString &, const QString &, const QString &) override {} + bool isReviewable() const override + { + return false; + } void flagReview(Review *, const QString&, const QString&) override {} void submitUsefulness(Review *, bool) override {} bool isResourceSupported(AbstractResource *) const override { return false; } + bool supportsNameChange() const override + { + return false; + } + +protected: + void sendReview(AbstractResource *, const QString &, const QString &, const QString &, const QString &) override + { + } + QString userName() const override + { + return QStringLiteral("dummy"); + } }; #endif // ALPINEAPKREVIEWSBACKEND_H -- GitLab From ecb521888111a62c483aa6c8ceb9ed0cce85394b Mon Sep 17 00:00:00 2001 From: Alexey Minnekhanov Date: Sun, 18 Sep 2022 17:29:35 +0300 Subject: [PATCH 59/61] AlpineApkBackend: compat with upstream Fixup after 78277138749a9cd4b644f2814822a1b392d259c8 "category decorations are not a thing anymore" --- .../AlpineApkBackend/AlpineApkBackend.cpp | 49 +++++++++---------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp index f53947324..5bcc38556 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -315,36 +315,33 @@ QVector AlpineApkBackend::category() const // Display a single root category // we could add more, but Alpine apk does not have this concept - static Category *s_rootCat = new Category( - i18nc("Root category name", "Alpine Linux packages"), - QStringLiteral("package-x-generic"), // icon - { s_apkFlt }, // orFilters - include packages that match filter - { displayName() }, // pluginName - {}, // subCategories - none - QUrl(), // decoration (what is it?) - false // isAddons + static Category *s_rootCat = new Category(i18nc("Root category name", "Alpine Linux packages"), + QStringLiteral("package-x-generic"), // icon + {s_apkFlt}, // orFilters - include packages that match filter + {displayName()}, // pluginName + {}, // subCategories - none + false // isAddons ); return { s_rootCat }; -// static QVector s_cats; -// if (s_cats.isEmpty()) { -// // fill only once -// s_cats << s_rootCat; -// for (const QString &scat : m_collectedCategories) { -// Category *cat = new Category( -// scat, // name -// QStringLiteral("package-x-generic"), // icon -// {}, // orFilters -// { displayName() }, // pluginName -// {}, // subcategories -// QUrl(), // decoration -// false // isAddons -// ); -// s_cats << cat; -// } -// } -// return s_cats; + // static QVector s_cats; + // if (s_cats.isEmpty()) { + // // fill only once + // s_cats << s_rootCat; + // for (const QString &scat : m_collectedCategories) { + // Category *cat = new Category( + // scat, // name + // QStringLiteral("package-x-generic"), // icon + // {}, // orFilters + // { displayName() }, // pluginName + // {}, // subcategories + // false // isAddons + // ); + // s_cats << cat; + // } + // } + // return s_cats; // ^^ causes deep hang in discover in recalculating QML bindings } -- GitLab From 64cad620679750aab6548ea2722718b8671bda55 Mon Sep 17 00:00:00 2001 From: Alexey Minnekhanov Date: Fri, 25 Nov 2022 07:27:01 +0300 Subject: [PATCH 60/61] Compat with upstream compat with: c703adef3872d355c46e92795eedd1f910d1d9af ("Categories: Make them truly recursive") --- .../backends/AlpineApkBackend/AlpineApkBackend.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp index 5bcc38556..9cc20aaa7 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkBackend.cpp @@ -310,16 +310,15 @@ void AlpineApkBackend::onAppstreamDataDownloaded() QVector AlpineApkBackend::category() const { - static QPair s_apkFlt( - FilterType::CategoryFilter, QLatin1String("alpine_packages")); + static CategoryFilter s_apkFlt{CategoryFilter::FilterType::CategoryNameFilter, QLatin1String("alpine_packages")}; // Display a single root category // we could add more, but Alpine apk does not have this concept - static Category *s_rootCat = new Category(i18nc("Root category name", "Alpine Linux packages"), - QStringLiteral("package-x-generic"), // icon - {s_apkFlt}, // orFilters - include packages that match filter + static Category *s_rootCat = new Category(i18nc("Root category name", "Alpine Linux packages"), // name + QStringLiteral("package-x-generic"), // icon name + s_apkFlt, // const CategoryFilter& filters {displayName()}, // pluginName - {}, // subCategories - none + {}, // QVector subCategories - none false // isAddons ); -- GitLab From 7f781785c443c4fc73bd50dd7bd4cb895f5442c2 Mon Sep 17 00:00:00 2001 From: Alexey Minnekhanov Date: Fri, 3 Mar 2023 19:41:55 +0300 Subject: [PATCH 61/61] AlpineApkResource: compat with upstream changes --- libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp index 2c981ca90..67c4cdcd5 100644 --- a/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp +++ b/libdiscover/backends/AlpineApkBackend/AlpineApkResource.cpp @@ -220,8 +220,8 @@ void AlpineApkResource::fetchChangelog() void AlpineApkResource::fetchScreenshots() { if (hasAppStreamData()) { - const QPair, QList > sc = AppStreamUtils::fetchScreenshots(m_appsC); - Q_EMIT screenshotsFetched(sc.first, sc.second); + const Screenshots screenshots = AppStreamUtils::fetchScreenshots(m_appsC); + Q_EMIT screenshotsFetched(screenshots); } } -- GitLab