Commit 0376a972 authored by Aleix Pol Gonzalez's avatar Aleix Pol Gonzalez 🐧

Support snap permissions in Discover

Subscribers: ngraham, plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D12103
parent 1c46f55a
......@@ -242,6 +242,20 @@ DiscoverPage {
Layout.bottomMargin: Kirigami.Units.largeSpacing
}
Repeater {
model: application.objects
delegate: Loader {
property QtObject resource: appInfo.application
source: modelData
}
}
Item {
height: addonsButton.height
width: 5
}
// Details/metadata
Rectangle {
color: Kirigami.Theme.linkColor
Layout.fillWidth: true
......
......@@ -28,7 +28,7 @@ elseif(BUILD_FlatpakBackend)
message(WARNING "BUILD_FlatpakBackend enabled but Flatpak=${FLATPAK_FOUND} or AppStreamQt=${AppStreamQt_FOUND} not found")
endif()
option(BUILD_SnapBackend "Build Snap support. Still a proof of concept" "OFF")
option(BUILD_SnapBackend "Build Snap support." "ON")
if(BUILD_SnapBackend)
find_package(Snapd)
set_package_properties(Snapd PROPERTIES
......
add_subdirectory(libsnapclient)
add_library(snap-backend MODULE SnapResource.cpp SnapBackend.cpp SnapReviewsBackend.cpp SnapTransaction.cpp)
add_library(snap-backend MODULE SnapResource.cpp SnapBackend.cpp SnapReviewsBackend.cpp SnapTransaction.cpp snapui.qrc)
target_link_libraries(snap-backend Qt5::Core KF5::CoreAddons KF5::ConfigCore Discover::Common Snapd::Core)
install(TARGETS snap-backend DESTINATION ${PLUGIN_INSTALL_DIR}/discover)
......
......@@ -24,11 +24,56 @@
#include <QProcess>
#include <QBuffer>
#include <QImageReader>
#include <QQmlComponent>
#include <QStandardItemModel>
#include <KLocalizedString>
#include <utils.h>
SnapResource::SnapResource(QSharedPointer<QSnapdSnap> snap, AbstractResource::State state, SnapBackend* parent)
: AbstractResource(parent)
QDebug operator<<(QDebug debug, const QSnapdPlug& plug)
{
QDebugStateSaver saver(debug);
debug.nospace() << "QSnapdPlug(";
debug.nospace() << "name:" << plug.name() << ',';
debug.nospace() << "snap:" << plug.snap() << ',';
debug.nospace() << "label:" << plug.label() << ',';
debug.nospace() << "interface:" << plug.interface() << ',';
debug.nospace() << "connectionCount:" << plug.connectionCount();
debug.nospace() << ')';
return debug;
}
QDebug operator<<(QDebug debug, const QSnapdSlot& slot)
{
QDebugStateSaver saver(debug);
debug.nospace() << "QSnapdSlot(";
debug.nospace() << "name:" << slot.name() << ',';
debug.nospace() << "label:" << slot.label() << ',';
debug.nospace() << "snap:" << slot.snap() << ',';
debug.nospace() << "interface:" << slot.interface() << ',';
debug.nospace() << "connectionCount:" << slot.connectionCount();
debug.nospace() << ')';
return debug;
}
QDebug operator<<(QDebug debug, const QSnapdPlug* plug)
{
QDebugStateSaver saver(debug);
debug.nospace() << "*" << *plug;
return debug;
}
QDebug operator<<(QDebug debug, const QSnapdSlot* slot)
{
QDebugStateSaver saver(debug);
debug.nospace() << "*" << *slot;
return debug;
}
SnapResource::SnapResource(QSharedPointer<QSnapdSnap> snap, AbstractResource::State state, SnapBackend* backend)
: AbstractResource(backend)
, m_state(state)
, m_snap(snap)
, m_objects({ QStringLiteral("qrc:/snapui/PermissionsButton.qml") })
{
setObjectName(snap->name());
}
......@@ -192,3 +237,91 @@ QDate SnapResource::releaseDate() const
{
return {};
}
class PlugsModel : public QStandardItemModel
{
public:
enum Roles {
PlugNameRole = Qt::UserRole + 1,
SlotSnapRole,
SlotNameRole
};
PlugsModel(QSnapdSnap* snap, SnapBackend* backend, QObject* parent)
: QStandardItemModel(parent)
, m_snap(snap)
, m_backend(backend)
{
setItemRoleNames(roleNames().unite(
{ {Qt::CheckStateRole, "checked"} }
));
auto req = backend->client()->getInterfaces();
req->runSync();
QHash<QString, QVector<QSnapdSlot*>> slotsForInterface;
for (int i = 0; i<req->slotCount(); ++i) {
const auto slot = req->slot(i);
slot->setParent(this);
slotsForInterface[slot->interface()].append(slot);
}
for (int i = 0; i<req->plugCount(); ++i) {
const QScopedPointer<QSnapdPlug> plug(req->plug(i));
if (plug->snap() == m_snap->name()) {
for (auto slot: slotsForInterface[plug->interface()]) {
auto item = new QStandardItem;
if (plug->label().isEmpty())
item->setText(plug->name());
else
item->setText(i18n("%1 - %2", plug->name(), plug->label()));
item->setCheckable(true);
item->setCheckState(plug->connectionCount()>0 ? Qt::Checked : Qt::Unchecked);
item->setData(plug->name(), PlugNameRole);
item->setData(slot->snap(), SlotSnapRole);
item->setData(slot->name(), SlotNameRole);
appendRow(item);
}
}
}
}
private:
bool setData(const QModelIndex & index, const QVariant & value, int role) override {
if (role != Qt::CheckStateRole)
return QStandardItemModel::setData(index, value, role);
auto item = itemFromIndex(index);
Q_ASSERT(item);
const QString plugName = item->data(PlugNameRole).toString();
const QString slotSnap = item->data(SlotSnapRole).toString();
const QString slotName = item->data(SlotNameRole).toString();
QSnapdRequest* req;
if (item->checkState() == Qt::Checked) {
req = m_backend->client()->connectInterface(m_snap->name(), plugName, slotSnap, slotName);
} else {
req = m_backend->client()->disconnectInterface(m_snap->name(), plugName, slotSnap, slotName);
}
req->runSync();
if (req->error()) {
qWarning() << "snapd error" << req->errorString();
}
return req->error() == QSnapdRequest::NoError;
}
QSnapdSnap* const m_snap;
SnapBackend* const m_backend;
};
QAbstractItemModel* SnapResource::plugs(QObject* p)
{
if (!isInstalled())
return new QStandardItemModel(p);
return new PlugsModel(m_snap.data(), qobject_cast<SnapBackend*>(parent()), p);
}
......@@ -27,10 +27,12 @@
#include <QSharedPointer>
class SnapBackend;
class QAbstractItemModel;
class SnapResource : public AbstractResource
{
Q_OBJECT
Q_PROPERTY(QStringList objects MEMBER m_objects CONSTANT)
public:
explicit SnapResource(QSharedPointer<QSnapdSnap> snap, AbstractResource::State state, SnapBackend* parent);
~SnapResource() override = default;
......@@ -62,12 +64,15 @@ public:
QDate releaseDate() const override;
Q_SCRIPTABLE QAbstractItemModel* plugs(QObject* parents);
public:
void gotIcon();
AbstractResource::State m_state;
QSharedPointer<QSnapdSnap> m_snap;
mutable QVariant m_icon;
const QStringList m_objects;
};
#endif // SNAPRESOURCE_H
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource>
<file>snapui/PermissionsButton.qml</file>
</qresource>
</RCC>
/*
* Copyright (C) 2018 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library/Lesser General Public License
* version 2, or (at your option) any later version, as published by the
* Free Software Foundation
*
* 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 Library/Lesser General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import QtQuick 2.1
import QtQuick.Controls 2.1
import org.kde.kirigami 2.1 as Kirigami
Button
{
id: root
text: i18n("Configure permissions...")
visible: resource.isInstalled
onClicked: overlay.open()
Popup {
id: overlay
parent: applicationWindow().overlay
bottomPadding: Kirigami.Units.largeSpacing
topPadding: Kirigami.Units.largeSpacing
x: (parent.width - width)/2
y: (parent.height - height)/2
width: parent.width * 1/3
height: Math.min(view.contentHeight + bottomPadding + topPadding, parent.height * 4/5)
ListView {
id: view
anchors.fill: parent
header: Kirigami.Heading {
text: i18n ("Permissions for %1", resource.name)
}
model: resource.plugs(root)
delegate: CheckDelegate {
id: delegate
width: parent.width
text: model.display
checked: model.checked
onClicked: {
model.checked = delegate.checked
}
}
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment