Skip to content
Merged
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
31 changes: 31 additions & 0 deletions shell/assets/styles/base/_mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,34 @@
// we need to use !important because it needs to superseed other classes that might impact outlines
outline: 2px solid var(--primary-keyboard-focus);
}

// -------------------------------------------------------------------------------------------------
// Extension dialog styles

@mixin extension-dialog {
padding: 8px 16px 16px 16px;

h4 {
font-weight: bold;
}

.dialog-panel {
display: flex;
flex-direction: column;
min-height: 96px;

.dialog-info {
flex: 1;
}
}

.dialog-buttons {
display: flex;
justify-content: flex-end;
margin-top: 24px;

> *:not(:last-child) {
margin-right: 8px;
}
}
}
3 changes: 3 additions & 0 deletions shell/assets/translations/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5578,6 +5578,9 @@ plugins:
prompt: "Are you sure that you want to install this extension?"
version: Version
warnNotCertified: Please ensure that you are aware of the risks of installing Extensions from untrusted authors
alreadyInstalledTitle: This extension is already installed from another source
alreadyInstalledPrompt: To install it from this source, you need to uninstall the existing version first and reload the page (required). Would you like to continue?
uninstallExisting: Uninstall existing version
upgrade:
label: Upgrade
title: Upgrade extension {name}
Expand Down
2 changes: 2 additions & 0 deletions shell/components/__tests__/PromptModal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import DeveloperLoadExtensionDialog from '@shell/dialog/DeveloperLoadExtensionDi
import AddExtensionReposDialog from '@shell/dialog/AddExtensionReposDialog.vue';
import InstallExtensionDialog from '@shell/dialog/InstallExtensionDialog.vue';
import UninstallExtensionDialog from '@shell/dialog/UninstallExtensionDialog.vue';
import UninstallExistingExtensionDialog from '@shell/dialog/UninstallExistingExtensionDialog.vue';
import KnownHostsEditDialog from '@shell/dialog/KnownHostsEditDialog.vue';
import ImportDialog from '@shell/dialog/ImportDialog.vue';
import SearchDialog from '@shell/dialog/SearchDialog.vue';
Expand Down Expand Up @@ -110,6 +111,7 @@ describe('component: PromptModal', () => {
['AddExtensionReposDialog', AddExtensionReposDialog],
['InstallExtensionDialog', InstallExtensionDialog],
['UninstallExtensionDialog', UninstallExtensionDialog],
['UninstallExistingExtensionDialog', UninstallExistingExtensionDialog],
['KnownHostsEditDialog', KnownHostsEditDialog],
['ImportDialog', ImportDialog],
['SearchDialog', SearchDialog],
Expand Down
33 changes: 6 additions & 27 deletions shell/dialog/InstallExtensionDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export default {

const plugin = this.plugin;

this.updateStatus(plugin.name, this.action);
this.updateStatus(plugin.id, this.action);

// Find the version that the user wants to install
const version = plugin.versions?.find((v) => v.version === this.version);
Expand Down Expand Up @@ -370,31 +370,21 @@ export default {
</template>

<style lang="scss" scoped>
.plugin-install-dialog {
padding: 10px;
@import '@shell/assets/styles/base/_mixins.scss';

h4 {
font-weight: bold;
}
.plugin-install-dialog {
@include extension-dialog;

.dialog-panel {
display: flex;
flex-direction: column;
min-height: 100px;

p {
margin-bottom: 5px;
}

.dialog-info {
flex: 1;
margin-bottom: 4px;
}

.toggle-advanced {
display: flex;
align-items: center;
cursor: pointer;
margin: 10px 0;
margin: 8px 0;

&:hover {
text-decoration: none;
Expand All @@ -403,19 +393,8 @@ export default {
}

.version-selector {
margin: 0 10px 10px 10px;
width: auto;
}
}

.dialog-buttons {
display: flex;
justify-content: flex-end;
margin-top: 10px;

> *:not(:last-child) {
margin-right: 10px;
}
}
}
</style>
141 changes: 141 additions & 0 deletions shell/dialog/UninstallExistingExtensionDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<script>
import AsyncButton from '@shell/components/AsyncButton';
import { CATALOG } from '@shell/config/types';
import { UI_PLUGIN_NAMESPACE } from '@shell/config/uiplugins';

/**
* Dialog shown when user tries to install an extension that is already installed from a different source.
* Prompts the user to uninstall the existing version first before installing from the new source.
*/
export default {
emits: ['close'],

components: { AsyncButton },

props: {
/**
* The installed plugin that needs to be uninstalled
*/
installedPlugin: {
type: Object,
default: () => {},
required: true
},
/**
* Callback to update install status on extensions main screen
*/
updateStatus: {
type: Function,
default: () => {},
required: true
},
/**
* Callback when modal is closed
*/
closed: {
type: Function,
default: () => {},
required: true
}
},

data() {
return { busy: false };
},

methods: {
closeDialog(result) {
this.closed(result);
this.$emit('close');
},
async uninstall() {
this.busy = true;

const plugin = this.installedPlugin;

this.updateStatus(plugin.id, 'uninstall');

// Delete the CR if this is a developer plugin (there is no Helm App, so need to remove the CRD ourselves)
if (plugin.uiplugin?.isDeveloper) {
// Delete the custom resource
await plugin.uiplugin.remove();
}

// Find the app for this plugin using direct lookup (more efficient than findAll)
let pluginApp = null;

try {
const appId = `${ UI_PLUGIN_NAMESPACE }/${ plugin.name }`;

pluginApp = await this.$store.dispatch('management/find', {
type: CATALOG.APP,
id: appId
});
} catch (e) {
// If the app cannot be found (e.g. already removed), proceed without error
pluginApp = null;
}

if (pluginApp) {
try {
await pluginApp.remove();
} catch (e) {
this.$store.dispatch('growl/error', {
title: this.t('plugins.error.generic'),
message: e.message ? e.message : e,
timeout: 10000
}, { root: true });

this.busy = false;

return;
}

await this.$store.dispatch('management/findAll', { type: CATALOG.OPERATION });
}

// Close the dialog
this.closeDialog({ uninstalled: true, plugin });
}
}
};
</script>

<template>
<div class="plugin-install-dialog">
<h4 class="mt-10">
{{ t('plugins.install.alreadyInstalledTitle') }}
</h4>
<div class="mt-10 dialog-panel">
<div class="dialog-info">
<p>
{{ t('plugins.install.alreadyInstalledPrompt') }}
</p>
</div>
<div class="dialog-buttons">
<button
:disabled="busy"
class="btn role-secondary"
data-testid="uninstall-existing-ext-modal-cancel-btn"
@click="closeDialog(false)"
>
{{ t('generic.cancel') }}
</button>
<AsyncButton
mode="uninstall"
:action-label="t('plugins.install.uninstallExisting')"
data-testid="uninstall-existing-ext-modal-uninstall-btn"
@click="uninstall()"
/>
</div>
</div>
</div>
</template>

<style lang="scss" scoped>
@import '@shell/assets/styles/base/_mixins.scss';

.plugin-install-dialog {
@include extension-dialog;
}
</style>
30 changes: 4 additions & 26 deletions shell/dialog/UninstallExtensionDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default {

const plugin = this.plugin;

this.updateStatus(plugin.name, 'uninstall');
this.updateStatus(plugin.id, 'uninstall');
Comment thread
aalves08 marked this conversation as resolved.

// Delete the CR if this is a developer plugin (there is no Helm App, so need to remove the CRD ourselves)
if (plugin.uiplugin?.isDeveloper) {
Expand Down Expand Up @@ -132,31 +132,9 @@ export default {
</template>

<style lang="scss" scoped>
.plugin-install-dialog {
padding: 10px;

h4 {
font-weight: bold;
}
@import '@shell/assets/styles/base/_mixins.scss';

.dialog-panel {
display: flex;
flex-direction: column;
min-height: 100px;

.dialog-info {
flex: 1;
}
}

.dialog-buttons {
display: flex;
justify-content: flex-end;
margin-top: 10px;

> *:not(:last-child) {
margin-right: 10px;
}
}
.plugin-install-dialog {
@include extension-dialog;
}
</style>
Loading
Loading