Skip to content

Extensions: Added uninstall and update process #27768

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/framework/extensions/extensionstypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ struct Manifest {
};

Uri uri;
io::path_t path;
Type type = Type::Undefined;
String title;
String description;
Expand All @@ -226,6 +227,7 @@ struct Manifest {
String version;
int apiversion = DEFAULT_API_VERSION;
bool legacyPlugin = false;
bool isRemovable = false;

std::vector<Action> actions;

Expand Down
2 changes: 2 additions & 0 deletions src/framework/extensions/iextensioninstaller.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "modularity/imoduleinterface.h"
#include "extensionstypes.h"

#include "global/types/ret.h"
#include "global/io/path.h"
Expand All @@ -15,5 +16,6 @@ class IExtensionInstaller : MODULE_EXPORT_INTERFACE

virtual Ret isFileSupported(const io::path_t path) const = 0;
virtual Ret installExtension(const io::path_t path) = 0;
virtual Ret uninstallExtension(const Uri& uri) = 0;
};
}
55 changes: 53 additions & 2 deletions src/framework/extensions/internal/extensioninstaller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "global/io/fileinfo.h"
#include "global/serialization/zipreader.h"
#include "global/translation.h"
#include "global/uuid.h"

#include "../extensionstypes.h"
Expand Down Expand Up @@ -36,11 +37,40 @@ Ret ExtensionInstaller::installExtension(const io::path_t srcPath)
ExtensionsLoader loader;
Manifest m = loader.parseManifest(data);

bool hasSame = provider()->manifest(m.uri).isValid();
if (hasSame) {
Manifest existingManifest = provider()->manifest(m.uri);
bool alreadyInstalled = existingManifest.isValid();
if (alreadyInstalled && existingManifest.version == m.version) {
LOGI() << "already installed: " << m.uri;

interactive()->info(trc("extensions", "The extension is already installed."), std::string(),
{ interactive()->buttonData(IInteractive::Button::Ok) });

return make_ok();
}

if (alreadyInstalled && !existingManifest.isRemovable) {
interactive()->error(trc("extensions", "This extension cannot be updated."), std::string(),
{ interactive()->buttonData(IInteractive::Button::Ok) });

return make_ok();
}

if (alreadyInstalled) {
std::string text = qtrc("extensions", "Another version of the extension “%1” is already installed (version %2). "
"Do you want to replace it with version %3?")
.arg(existingManifest.title, existingManifest.version, m.version).toStdString();

IInteractive::Result result = interactive()->question(trc("extensions", "Update Extension"), text, {
interactive()->buttonData(IInteractive::Button::Cancel),
interactive()->buttonData(IInteractive::Button::Ok)
});

if (result.button() == int(IInteractive::Button::Ok)) {
this->uninstallExtension(existingManifest.uri);
} else {
return make_ok();
}
}
}

// unpack
Expand All @@ -66,3 +96,24 @@ Ret ExtensionInstaller::installExtension(const io::path_t srcPath)

return ret;
}

Ret ExtensionInstaller::uninstallExtension(const Uri& uri)
{
Manifest manifest = provider()->manifest(uri);
if (!manifest.isValid()) {
return make_ret(Ret::Code::UnknownError);
} else if (!manifest.isRemovable) {
return make_ret(Ret::Code::NotSupported);
}

io::path_t path = manifest.path;

Ret ret = fileSystem()->remove(io::dirpath(path));
if (!ret) {
LOGE() << "Failed to delete the folder: " << path;
return ret;
}

provider()->reloadExtensions();
return make_ok();
}
5 changes: 5 additions & 0 deletions src/framework/extensions/internal/extensioninstaller.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@
#include "modularity/ioc.h"
#include "../iextensionsconfiguration.h"
#include "../iextensionsprovider.h"
#include "global/iinteractive.h"
#include "io/ifilesystem.h"

namespace muse::extensions {
class ExtensionInstaller : public IExtensionInstaller
{
muse::GlobalInject<IExtensionsConfiguration> configuration;
muse::GlobalInject<IExtensionsProvider> provider;
muse::GlobalInject<muse::IInteractive> interactive;
muse::GlobalInject<io::IFileSystem> fileSystem;

public:
ExtensionInstaller() = default;

Ret isFileSupported(const io::path_t path) const override;
Ret installExtension(const io::path_t path) override;
Ret uninstallExtension(const Uri& uri) override;
};
}
4 changes: 3 additions & 1 deletion src/framework/extensions/internal/extensionsloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ ManifestList ExtensionsLoader::loadManifestList(const io::path_t& defPath, const
retList.push_back(m);
}

for (const Manifest& m : externalManifests) {
for (Manifest& m : externalManifests) {
if (!m.isValid()) {
continue;
}
Expand All @@ -67,6 +67,7 @@ ManifestList ExtensionsLoader::loadManifestList(const io::path_t& defPath, const
continue;
}

m.isRemovable = true;
retList.push_back(m);
}

Expand All @@ -80,6 +81,7 @@ ManifestList ExtensionsLoader::manifestList(const io::path_t& rootPath) const
for (const io::path_t& path : paths) {
LOGD() << "parsing manifest: " << path;
Manifest manifest = parseManifest(path);
manifest.path = path;
resolvePaths(manifest, io::FileInfo(path).dirPath());
manifests.push_back(manifest);
}
Expand Down
1 change: 1 addition & 0 deletions src/framework/extensions/internal/extensionsprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include "global/containers.h"
#include "global/async/async.h"
#include "global/io/path.h"

#include "extensionsloader.h"
#include "legacy/extpluginsloader.h"
Expand Down
2 changes: 2 additions & 0 deletions src/framework/extensions/internal/extensionsprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@
#include "../iextensionsprovider.h"
#include "../iextensionsexecpointsregister.h"
#include "global/iinteractive.h"
#include "io/ifilesystem.h"

namespace muse::extensions {
class ExtensionsProvider : public IExtensionsProvider, public Injectable, public async::Asyncable
{
Inject<IExtensionsConfiguration> configuration = { this };
Inject<IExtensionsExecPointsRegister> execPointsRegister = { this };
Inject<IInteractive> interactive = { this };
Inject<io::IFileSystem> fileSystem = { this };

public:
ExtensionsProvider(const modularity::ContextPtr& iocCtx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,11 @@ ManifestList ExtPluginsLoader::loadManifestList(const io::path_t& defPath, const
retList.push_back(m);
}

for (const Manifest& m : externalManifests) {
for (Manifest& m : externalManifests) {
if (!m.isValid()) {
continue;
}
m.isRemovable = true;
retList.push_back(m);
}

Expand All @@ -95,6 +96,7 @@ ManifestList ExtPluginsLoader::manifestList(const io::path_t& rootPath) const
if (!manifest.isValid()) {
continue;
}
manifest.path = path;
resolvePaths(manifest, io::FileInfo(path).dirPath());
manifests.push_back(manifest);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ Item {
background: flickable

isEnabled: Boolean(selectedPlugin) ? selectedPlugin.enabled : false
isRemovable: Boolean(selectedPlugin) ? selectedPlugin.isRemovable : false

additionalInfoModel: [
{"title": qsTrc("extensions", "Version:"), "value": Boolean(selectedPlugin) ? selectedPlugin.version : "" },
Expand All @@ -198,6 +199,12 @@ Item {
extensionsModel.selectExecPoint(selectedPlugin.uri, index)
}

onRemoveRequest: function() {
extensionsModel.removeExtension(selectedPlugin.uri)
prv.resetSelectedPlugin()
panel.close()
}

onEditShortcutRequested: {
Qt.callLater(extensionsModel.editShortcut, selectedPlugin.uri)
panel.close()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ InfoPanel {

property var execPointsModel: null
property int currentExecPointIndex: 0
property bool isRemovable: false

signal editShortcutRequested()
signal execPointSelected(int index)
signal removeRequest()

buttonsPanel: RowLayout {
id: buttons
Expand Down Expand Up @@ -84,37 +86,62 @@ InfoPanel {
}
}



FlatButton {
id: mainButton
RowLayout {
Layout.alignment: Qt.AlignRight
spacing: 22

FlatButton {
id: removeButton

visible: root.isRemovable
navigation.name: text + "Button"
navigation.panel: root.contentNavigation
navigation.column: 2
accessible.ignored: true
navigation.onActiveChanged: {
if (!navigation.active) {
accessible.ignored = false
}
}

navigation.name: text + "Button"
navigation.panel: root.contentNavigation
navigation.column: 3
accessible.ignored: true
navigation.onActiveChanged: {
if (!navigation.active) {
accessible.ignored = false
text: qsTrc("workspace", "Remove")

onClicked: {
root.removeRequest()
}
}

text: !root.isEnabled ? qsTrc("extensions", "Enable") : qsTrc("extensions", "Disable")

Component.onCompleted: {
root.mainButton = mainButton
}

onClicked: {
//! NOTE temporary
// The function with the choice of the call point is not ready yet.
// Therefore, here is the previous solution with the button,
// but in fact the choice is made from the list
// 0 - disabled
// 1 - enabled (manual call)
// (here we switch to the opposite state)
root.execPointSelected(root.isEnabled ? 0 : 1)
FlatButton {
id: mainButton

navigation.name: text + "Button"
navigation.panel: root.contentNavigation
navigation.column: 3
accessible.ignored: true
navigation.onActiveChanged: {
if (!navigation.active) {
accessible.ignored = false
}
}

text: !root.isEnabled ? qsTrc("extensions", "Enable") : qsTrc("extensions", "Disable")

Component.onCompleted: {
root.mainButton = mainButton
}

onClicked: {
//! NOTE temporary
// The function with the choice of the call point is not ready yet.
// Therefore, here is the previous solution with the button,
// but in fact the choice is made from the list
// 0 - disabled
// 1 - enabled (manual call)
// (here we switch to the opposite state)
root.execPointSelected(root.isEnabled ? 0 : 1)
}
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/framework/extensions/view/extensionslistmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ ExtensionsListModel::ExtensionsListModel(QObject* parent)
{ rCategory, "category" },
{ rVersion, "version" },
{ rShortcuts, "shortcuts" },
{ rIsRemovable, "isRemovable" }
};
}

Expand Down Expand Up @@ -120,6 +121,9 @@ QVariant ExtensionsListModel::data(const QModelIndex& index, int role) const
//: No keyboard shortcut is assigned to this plugin.
return muse::qtrc("extensions", "Not defined");
}
case rIsRemovable: {
return plugin.isRemovable;
}
}

return QVariant();
Expand Down Expand Up @@ -219,6 +223,11 @@ void ExtensionsListModel::reloadPlugins()
provider()->reloadExtensions();
}

void ExtensionsListModel::removeExtension(const QString& uri)
{
installer()->uninstallExtension(Uri(uri.toStdString()));
}

QVariantList ExtensionsListModel::categories() const
{
QVariantList result;
Expand Down
6 changes: 5 additions & 1 deletion src/framework/extensions/view/extensionslistmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include "modularity/ioc.h"
#include "iinteractive.h"
#include "extensions/iextensioninstaller.h"
#include "extensions/iextensionsconfiguration.h"
#include "extensions/iextensionsprovider.h"
#include "shortcuts/ishortcutsregister.h"
Expand All @@ -40,6 +41,7 @@ class ExtensionsListModel : public QAbstractListModel, public Injectable, public

Inject<IInteractive> interactive = { this };
Inject<IExtensionsProvider> provider = { this };
Inject<IExtensionInstaller> installer = { this };
Inject<IExtensionsConfiguration> configuration = { this };
Inject<shortcuts::IShortcutsRegister> shortcutsRegister = { this };

Expand All @@ -58,6 +60,7 @@ class ExtensionsListModel : public QAbstractListModel, public Injectable, public

Q_INVOKABLE void editShortcut(const QString& uri);
Q_INVOKABLE void reloadPlugins();
Q_INVOKABLE void removeExtension(const QString& uri);

Q_INVOKABLE QVariantList categories() const;

Expand All @@ -73,7 +76,8 @@ class ExtensionsListModel : public QAbstractListModel, public Injectable, public
rEnabled,
rCategory,
rVersion,
rShortcuts
rShortcuts,
rIsRemovable
};

struct ExecPoints {
Expand Down
Loading