mirror of
https://github.com/portainer/portainer.git
synced 2025-08-08 07:15:23 +02:00
refactor(ui/modals): replace bootbox with react solution [EE-4541] (#8010)
This commit is contained in:
parent
392c7f74b8
commit
e66dea44e3
111 changed files with 1330 additions and 1562 deletions
|
@ -1,10 +1,13 @@
|
|||
import { ModalType } from '@@/modals';
|
||||
import { confirm } from '@@/modals/confirm';
|
||||
import { buildConfirmButton } from '@@/modals/utils';
|
||||
|
||||
class KubernetesAppGitFormController {
|
||||
/* @ngInject */
|
||||
constructor($async, $state, StackService, ModalService, Notifications) {
|
||||
constructor($async, $state, StackService, Notifications) {
|
||||
this.$async = $async;
|
||||
this.$state = $state;
|
||||
this.StackService = StackService;
|
||||
this.ModalService = ModalService;
|
||||
this.Notifications = Notifications;
|
||||
|
||||
this.state = {
|
||||
|
@ -39,19 +42,16 @@ class KubernetesAppGitFormController {
|
|||
async pullAndRedeployApplication() {
|
||||
return this.$async(async () => {
|
||||
try {
|
||||
const confirmed = await this.ModalService.confirmAsync({
|
||||
const confirmed = await confirm({
|
||||
title: 'Are you sure?',
|
||||
message: 'Any changes to this application will be overridden by the definition in git and may cause a service interruption. Do you wish to continue?',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Update',
|
||||
className: 'btn-warning',
|
||||
},
|
||||
},
|
||||
confirmButton: buildConfirmButton('Update', 'warning'),
|
||||
modalType: ModalType.Warn,
|
||||
});
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.redeployInProgress = true;
|
||||
await this.StackService.updateKubeGit(this.stack.Id, this.stack.EndpointId, this.namespace, this.formValues);
|
||||
this.Notifications.success('Success', 'Pulled and redeployed stack successfully');
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import uuidv4 from 'uuid/v4';
|
||||
import { RepositoryMechanismTypes } from 'Kubernetes/models/deploy';
|
||||
import { confirm } from '@@/modals/confirm';
|
||||
import { buildConfirmButton } from '@@/modals/utils';
|
||||
import { ModalType } from '@@/modals';
|
||||
class KubernetesRedeployAppGitFormController {
|
||||
/* @ngInject */
|
||||
constructor($async, $state, StackService, ModalService, Notifications, WebhookHelper) {
|
||||
constructor($async, $state, StackService, Notifications, WebhookHelper) {
|
||||
this.$async = $async;
|
||||
this.$state = $state;
|
||||
this.StackService = StackService;
|
||||
this.ModalService = ModalService;
|
||||
this.Notifications = Notifications;
|
||||
this.WebhookHelper = WebhookHelper;
|
||||
|
||||
|
@ -80,19 +82,16 @@ class KubernetesRedeployAppGitFormController {
|
|||
async pullAndRedeployApplication() {
|
||||
return this.$async(async () => {
|
||||
try {
|
||||
const confirmed = await this.ModalService.confirmAsync({
|
||||
const confirmed = await confirm({
|
||||
title: 'Are you sure?',
|
||||
message: 'Any changes to this application will be overridden by the definition in git and may cause a service interruption. Do you wish to continue?',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Update',
|
||||
className: 'btn-warning',
|
||||
},
|
||||
},
|
||||
confirmButton: buildConfirmButton('Update', 'warning'),
|
||||
modalType: ModalType.Warn,
|
||||
});
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.redeployInProgress = true;
|
||||
await this.StackService.updateKubeGit(this.stack.Id, this.stack.EndpointId, this.namespace, this.formValues);
|
||||
this.Notifications.success('Success', 'Pulled and redeployed stack successfully');
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import uuidv4 from 'uuid/v4';
|
||||
import { RepositoryMechanismTypes } from 'Kubernetes/models/deploy';
|
||||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
import { confirmStackUpdate } from '@/react/docker/stacks/common/confirm-stack-update';
|
||||
|
||||
class StackRedeployGitFormController {
|
||||
/* @ngInject */
|
||||
constructor($async, $state, $compile, $scope, StackService, ModalService, Notifications, WebhookHelper, FormHelper) {
|
||||
constructor($async, $state, $compile, $scope, StackService, Notifications, WebhookHelper, FormHelper) {
|
||||
this.$async = $async;
|
||||
this.$state = $state;
|
||||
this.$compile = $compile;
|
||||
this.$scope = $scope;
|
||||
this.StackService = StackService;
|
||||
this.ModalService = ModalService;
|
||||
this.Notifications = Notifications;
|
||||
this.WebhookHelper = WebhookHelper;
|
||||
this.FormHelper = FormHelper;
|
||||
|
@ -105,33 +106,32 @@ class StackRedeployGitFormController {
|
|||
async submit() {
|
||||
const isSwarmStack = this.stack.Type === 1;
|
||||
const that = this;
|
||||
this.ModalService.confirmStackUpdate(
|
||||
confirmStackUpdate(
|
||||
'Any changes to this stack or application made locally in Portainer will be overridden, which may cause service interruption. Do you wish to continue?',
|
||||
isSwarmStack,
|
||||
'btn-warning',
|
||||
async function (result) {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
that.state.redeployInProgress = true;
|
||||
await that.StackService.updateGit(
|
||||
that.stack.Id,
|
||||
that.stack.EndpointId,
|
||||
that.FormHelper.removeInvalidEnvVars(that.formValues.Env),
|
||||
that.formValues.Option.Prune,
|
||||
that.formValues,
|
||||
!!result[0]
|
||||
);
|
||||
that.Notifications.success('Success', 'Pulled and redeployed stack successfully');
|
||||
that.$state.reload();
|
||||
} catch (err) {
|
||||
that.Notifications.error('Failure', err, 'Failed redeploying stack');
|
||||
} finally {
|
||||
that.state.redeployInProgress = false;
|
||||
}
|
||||
isSwarmStack
|
||||
).then(async function (result) {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
);
|
||||
try {
|
||||
that.state.redeployInProgress = true;
|
||||
await that.StackService.updateGit(
|
||||
that.stack.Id,
|
||||
that.stack.EndpointId,
|
||||
that.FormHelper.removeInvalidEnvVars(that.formValues.Env),
|
||||
that.formValues.Option.Prune,
|
||||
that.formValues,
|
||||
result.pullImage
|
||||
);
|
||||
|
||||
that.Notifications.success('Success', 'Pulled and redeployed stack successfully');
|
||||
that.$state.reload();
|
||||
} catch (err) {
|
||||
that.Notifications.error('Failure', err, 'Failed redeploying stack');
|
||||
} finally {
|
||||
that.state.redeployInProgress = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async saveGitSettings() {
|
||||
|
|
|
@ -2,13 +2,11 @@ import angular from 'angular';
|
|||
|
||||
import { apiServicesModule } from './api';
|
||||
import { Notifications } from './notifications';
|
||||
import { ModalServiceAngular } from './modal.service';
|
||||
import { HttpRequestHelperAngular } from './http-request.helper';
|
||||
import { EndpointProvider } from './endpointProvider';
|
||||
|
||||
export default angular
|
||||
.module('portainer.app.services', [apiServicesModule])
|
||||
.factory('Notifications', Notifications)
|
||||
.factory('ModalService', ModalServiceAngular)
|
||||
.factory('EndpointProvider', EndpointProvider)
|
||||
.factory('HttpRequestHelper', HttpRequestHelperAngular).name;
|
||||
|
|
|
@ -1,266 +0,0 @@
|
|||
import sanitize from 'sanitize-html';
|
||||
import bootbox from 'bootbox';
|
||||
|
||||
import {
|
||||
applyBoxCSS,
|
||||
ButtonsOptions,
|
||||
confirmButtons,
|
||||
buildTitle,
|
||||
ModalTypeIcon,
|
||||
} from './utils';
|
||||
|
||||
type ConfirmCallback = (confirmed: boolean) => void;
|
||||
|
||||
interface ConfirmAsyncOptions {
|
||||
title: string;
|
||||
message: string;
|
||||
buttons: ButtonsOptions;
|
||||
}
|
||||
|
||||
interface ConfirmOptions extends ConfirmAsyncOptions {
|
||||
callback: ConfirmCallback;
|
||||
}
|
||||
|
||||
export function confirmWebEditorDiscard() {
|
||||
const options = {
|
||||
title: buildTitle('Are you sure?'),
|
||||
message:
|
||||
'You currently have unsaved changes in the editor. Are you sure you want to leave?',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Yes',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
};
|
||||
return new Promise((resolve) => {
|
||||
confirm({
|
||||
...options,
|
||||
callback: (confirmed) => resolve(confirmed),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmAsync(options: ConfirmAsyncOptions) {
|
||||
return new Promise((resolve) => {
|
||||
confirm({
|
||||
...options,
|
||||
title: buildTitle(options.title),
|
||||
callback: (confirmed) => resolve(confirmed),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmDestructiveAsync(options: ConfirmAsyncOptions) {
|
||||
return new Promise((resolve) => {
|
||||
confirm({
|
||||
...options,
|
||||
title: buildTitle(options.title, ModalTypeIcon.Destructive),
|
||||
callback: (confirmed) => resolve(confirmed),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function confirm(options: ConfirmOptions) {
|
||||
const box = bootbox.confirm({
|
||||
title: options.title,
|
||||
message: options.message,
|
||||
buttons: confirmButtons(options.buttons),
|
||||
callback: options.callback,
|
||||
});
|
||||
|
||||
applyBoxCSS(box);
|
||||
}
|
||||
|
||||
export function confirmWarn(options: ConfirmOptions) {
|
||||
confirm({ ...options, title: buildTitle(options.title, ModalTypeIcon.Warn) });
|
||||
}
|
||||
|
||||
export function confirmDestructive(options: ConfirmOptions) {
|
||||
confirm({
|
||||
...options,
|
||||
title: buildTitle(options.title, ModalTypeIcon.Destructive),
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmImageForceRemoval(callback: ConfirmCallback) {
|
||||
confirm({
|
||||
title: buildTitle('Are you sure?', ModalTypeIcon.Destructive),
|
||||
message:
|
||||
'Forcing the removal of the image will remove the image even if it has multiple tags or if it is used by stopped containers.',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Remove the image',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
callback,
|
||||
});
|
||||
}
|
||||
|
||||
export function cancelRegistryRepositoryAction(callback: ConfirmCallback) {
|
||||
confirm({
|
||||
title: buildTitle('Are you sure?', ModalTypeIcon.Destructive),
|
||||
message:
|
||||
'WARNING: interrupting this operation before it has finished will result in the loss of all tags. Are you sure you want to do this?',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Stop',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
callback,
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmDeletion(message: string, callback: ConfirmCallback) {
|
||||
const messageSanitized = sanitize(message);
|
||||
confirm({
|
||||
title: buildTitle('Are you sure?', ModalTypeIcon.Destructive),
|
||||
message: messageSanitized,
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Remove',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
callback,
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmWithTitle(
|
||||
title: string,
|
||||
message: string,
|
||||
callback: ConfirmCallback
|
||||
) {
|
||||
const messageSanitized = sanitize(message);
|
||||
confirm({
|
||||
title: buildTitle(title, ModalTypeIcon.Destructive),
|
||||
message: messageSanitized,
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Remove',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
callback,
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmDetachment(message: string, callback: ConfirmCallback) {
|
||||
const messageSanitized = sanitize(message);
|
||||
confirm({
|
||||
title: buildTitle('Are you sure?'),
|
||||
message: messageSanitized,
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Detach',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
},
|
||||
callback,
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmDisassociate(callback: ConfirmCallback) {
|
||||
const message =
|
||||
'<p>Disassociating this Edge environment will mark it as non associated and will clear the registered Edge ID.</p>' +
|
||||
'<p>Any agent started with the Edge key associated to this environment will be able to re-associate with this environment.</p>' +
|
||||
'<p>You can re-use the Edge ID and Edge key that you used to deploy the existing Edge agent to associate a new Edge device to this environment.</p>';
|
||||
confirm({
|
||||
title: buildTitle('About disassociating'),
|
||||
message: sanitize(message),
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Disassociate',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
},
|
||||
callback,
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmUpdate(message: string, callback: ConfirmCallback) {
|
||||
const messageSanitized = sanitize(message);
|
||||
|
||||
confirm({
|
||||
title: buildTitle('Are you sure?'),
|
||||
message: messageSanitized,
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Update',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
},
|
||||
callback,
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmRedeploy(message: string, callback: ConfirmCallback) {
|
||||
const messageSanitized = sanitize(message);
|
||||
|
||||
confirm({
|
||||
title: '',
|
||||
message: messageSanitized,
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Redeploy the applications',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
cancel: {
|
||||
label: "I'll do it later",
|
||||
},
|
||||
},
|
||||
callback,
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmDeletionAsync(message: string) {
|
||||
return new Promise((resolve) => {
|
||||
confirmDeletion(message, (confirmed) => resolve(confirmed));
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmImageExport(callback: ConfirmCallback) {
|
||||
confirm({
|
||||
title: buildTitle('Caution'),
|
||||
message:
|
||||
'The export may take several minutes, do not navigate away whilst the export is in progress.',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Continue',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
},
|
||||
callback,
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmChangePassword() {
|
||||
return confirmAsync({
|
||||
title: buildTitle('Are you sure?'),
|
||||
message:
|
||||
'You will be logged out after the password change. Do you want to change your password?',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Change',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmForceChangePassword() {
|
||||
const box = bootbox.dialog({
|
||||
message:
|
||||
'Please update your password to a stronger password to continue using Portainer',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'OK',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
applyBoxCSS(box);
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
import sanitize from 'sanitize-html';
|
||||
import bootbox from 'bootbox';
|
||||
|
||||
import {
|
||||
cancelRegistryRepositoryAction,
|
||||
confirmAsync,
|
||||
confirmWarn,
|
||||
confirmDestructive,
|
||||
confirmDestructiveAsync,
|
||||
confirmDisassociate,
|
||||
confirmDeletion,
|
||||
confirmDetachment,
|
||||
confirmDeletionAsync,
|
||||
confirmChangePassword,
|
||||
confirmImageExport,
|
||||
confirmImageForceRemoval,
|
||||
confirmRedeploy,
|
||||
confirmUpdate,
|
||||
confirmWebEditorDiscard,
|
||||
confirm,
|
||||
confirmForceChangePassword,
|
||||
confirmWithTitle,
|
||||
} from './confirm';
|
||||
import {
|
||||
confirmContainerDeletion,
|
||||
confirmContainerRecreation,
|
||||
confirmServiceForceUpdate,
|
||||
confirmStackUpdate,
|
||||
selectRegistry,
|
||||
} from './prompt';
|
||||
|
||||
export function enlargeImage(imageUrl: string) {
|
||||
const imageSanitized = sanitize(imageUrl);
|
||||
|
||||
bootbox.dialog({
|
||||
message: `<img src="${imageSanitized}" style="width:100%" />`,
|
||||
className: 'image-zoom-modal',
|
||||
onEscape: true,
|
||||
});
|
||||
}
|
||||
|
||||
/* @ngInject */
|
||||
export function ModalServiceAngular() {
|
||||
return {
|
||||
enlargeImage,
|
||||
confirmWebEditorDiscard,
|
||||
confirmAsync,
|
||||
confirmWarn,
|
||||
confirmDestructive,
|
||||
confirmDestructiveAsync,
|
||||
confirm,
|
||||
confirmImageForceRemoval,
|
||||
cancelRegistryRepositoryAction,
|
||||
confirmDeletion,
|
||||
confirmDetachment,
|
||||
confirmDisassociate,
|
||||
confirmUpdate,
|
||||
confirmRedeploy,
|
||||
confirmDeletionAsync,
|
||||
confirmContainerRecreation,
|
||||
confirmChangePassword,
|
||||
confirmImageExport,
|
||||
confirmServiceForceUpdate,
|
||||
confirmStackUpdate,
|
||||
selectRegistry,
|
||||
confirmContainerDeletion,
|
||||
confirmForceChangePassword,
|
||||
confirmWithTitle,
|
||||
};
|
||||
}
|
|
@ -1,235 +0,0 @@
|
|||
import sanitize from 'sanitize-html';
|
||||
import bootbox from 'bootbox';
|
||||
|
||||
import {
|
||||
applyBoxCSS,
|
||||
ButtonsOptions,
|
||||
confirmButtons,
|
||||
buildTitle,
|
||||
ModalTypeIcon,
|
||||
} from './utils';
|
||||
|
||||
type PromptCallback = ((value: string) => void) | ((value: string[]) => void);
|
||||
|
||||
interface InputOption {
|
||||
text: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface PromptOptions {
|
||||
title: string;
|
||||
message?: string;
|
||||
inputType?:
|
||||
| 'text'
|
||||
| 'textarea'
|
||||
| 'email'
|
||||
| 'select'
|
||||
| 'checkbox'
|
||||
| 'date'
|
||||
| 'time'
|
||||
| 'number'
|
||||
| 'password'
|
||||
| 'radio'
|
||||
| 'range';
|
||||
inputOptions: InputOption[];
|
||||
buttons: ButtonsOptions;
|
||||
value?: string;
|
||||
callback: PromptCallback;
|
||||
}
|
||||
|
||||
export async function promptAsync(options: Omit<PromptOptions, 'callback'>) {
|
||||
return new Promise((resolve) => {
|
||||
prompt({
|
||||
...options,
|
||||
callback: (result: string | string[]) => resolve(result),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// the ts-ignore is required because the bootbox typings are not up to date
|
||||
// remove the ts-ignore when the typings are updated in
|
||||
export function prompt(options: PromptOptions) {
|
||||
const box = bootbox.prompt({
|
||||
title: options.title,
|
||||
message: options.message || '',
|
||||
inputType: options.inputType,
|
||||
inputOptions: options.inputOptions,
|
||||
buttons: options.buttons ? confirmButtons(options.buttons) : undefined,
|
||||
// casting is done because ts definition expects string=>any, but library code can emit different values, based on inputType
|
||||
callback: options.callback as (value: string) => void,
|
||||
value: options.value,
|
||||
});
|
||||
|
||||
applyBoxCSS(box);
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
export function confirmContainerDeletion(
|
||||
title: string,
|
||||
callback: PromptCallback
|
||||
) {
|
||||
prompt({
|
||||
title: buildTitle(title, ModalTypeIcon.Destructive),
|
||||
inputType: 'checkbox',
|
||||
inputOptions: [
|
||||
{
|
||||
text: 'Automatically remove non-persistent volumes<i></i>',
|
||||
value: '1',
|
||||
},
|
||||
],
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Remove',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
callback,
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmUpdateAppIngress(
|
||||
title: string,
|
||||
message: string,
|
||||
inputText: string,
|
||||
callback: PromptCallback
|
||||
) {
|
||||
prompt({
|
||||
title: buildTitle(title),
|
||||
inputType: 'checkbox',
|
||||
message,
|
||||
inputOptions: [
|
||||
{
|
||||
text: `${inputText}<i></i>`,
|
||||
value: '1',
|
||||
},
|
||||
],
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Update',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
},
|
||||
callback,
|
||||
});
|
||||
}
|
||||
|
||||
export function selectRegistry(options: PromptOptions) {
|
||||
prompt(options);
|
||||
}
|
||||
|
||||
export function confirmContainerRecreation(
|
||||
cannotPullImage: boolean | null,
|
||||
callback: PromptCallback
|
||||
) {
|
||||
const box = prompt({
|
||||
title: buildTitle('Are you sure?', ModalTypeIcon.Destructive),
|
||||
|
||||
inputType: 'checkbox',
|
||||
inputOptions: [
|
||||
{
|
||||
text: 'Re-pull image<i></i>',
|
||||
value: '1',
|
||||
},
|
||||
],
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Recreate',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
callback,
|
||||
});
|
||||
|
||||
const message = `You're about to recreate this container and any non-persisted data will be lost. This container will be removed and another one will be created using the same configuration.`;
|
||||
box.find('.bootbox-body').prepend(`<p>${message}</p>`);
|
||||
const label = box.find('.form-check-label');
|
||||
label.css('padding-left', '5px');
|
||||
label.css('padding-right', '25px');
|
||||
|
||||
if (cannotPullImage) {
|
||||
label.css('cursor', 'not-allowed');
|
||||
label.find('i').css('cursor', 'not-allowed');
|
||||
const checkbox = box.find('.bootbox-input-checkbox');
|
||||
checkbox.prop('disabled', true);
|
||||
const formCheck = box.find('.form-check');
|
||||
formCheck.prop('style', 'height: 45px;');
|
||||
const cannotPullImageMessage = `<pr-icon icon="'alert-triangle'" mode="'warning'"/>
|
||||
<div class="inline-text text-warning">
|
||||
<span>Cannot re-pull as the image is inaccessible - either it no longer exists or the tag or name is no longer correct.
|
||||
</span>
|
||||
</div>`;
|
||||
formCheck.append(`${cannotPullImageMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function confirmServiceForceUpdate(
|
||||
message: string,
|
||||
callback: PromptCallback
|
||||
) {
|
||||
const sanitizedMessage = sanitize(message);
|
||||
|
||||
const box = prompt({
|
||||
title: buildTitle('Are you sure?'),
|
||||
inputType: 'checkbox',
|
||||
inputOptions: [
|
||||
{
|
||||
text: 'Re-pull image<i></i>',
|
||||
value: '1',
|
||||
},
|
||||
],
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Update',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
},
|
||||
callback,
|
||||
});
|
||||
|
||||
customizeCheckboxPrompt(box, sanitizedMessage);
|
||||
}
|
||||
|
||||
export function confirmStackUpdate(
|
||||
message: string,
|
||||
defaultToggle: boolean,
|
||||
confirmButtonClass: string | undefined,
|
||||
callback: PromptCallback
|
||||
) {
|
||||
const sanitizedMessage = sanitize(message);
|
||||
|
||||
const box = prompt({
|
||||
title: buildTitle('Are you sure?'),
|
||||
inputType: 'checkbox',
|
||||
inputOptions: [
|
||||
{
|
||||
text: 'Re-pull image and redeploy<i></i>',
|
||||
value: '1',
|
||||
},
|
||||
],
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Update',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
},
|
||||
callback,
|
||||
});
|
||||
|
||||
customizeCheckboxPrompt(box, sanitizedMessage, defaultToggle);
|
||||
}
|
||||
|
||||
function customizeCheckboxPrompt(
|
||||
box: JQuery<HTMLElement>,
|
||||
message: string,
|
||||
toggleCheckbox = false,
|
||||
showCheck = false
|
||||
) {
|
||||
box.find('.bootbox-body').prepend(`<p>${message}</p>`);
|
||||
const checkbox = box.find('.bootbox-input-checkbox');
|
||||
checkbox.prop('checked', toggleCheckbox);
|
||||
|
||||
if (showCheck) {
|
||||
checkbox.addClass('visible');
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
import sanitize from 'sanitize-html';
|
||||
|
||||
interface Button {
|
||||
label: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export interface ButtonsOptions {
|
||||
confirm: Button;
|
||||
cancel?: Button;
|
||||
}
|
||||
|
||||
export enum ModalTypeIcon {
|
||||
Warn = 'warning',
|
||||
Destructive = 'error',
|
||||
}
|
||||
|
||||
export function confirmButtons(options: ButtonsOptions) {
|
||||
return {
|
||||
confirm: {
|
||||
label: sanitize(options.confirm.label),
|
||||
className:
|
||||
options.confirm.className && sanitize(options.confirm.className),
|
||||
},
|
||||
cancel: {
|
||||
label:
|
||||
options.cancel && options.cancel.label
|
||||
? sanitize(options.cancel.label)
|
||||
: 'Cancel',
|
||||
className: 'btn-default',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function buildTitle(
|
||||
title: string,
|
||||
modalType: ModalTypeIcon = ModalTypeIcon.Warn
|
||||
) {
|
||||
return `
|
||||
<div class="background-${modalType}">
|
||||
<h5 class="modal-title">${sanitize(title)}</h5>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
export function applyBoxCSS(box: JQuery<HTMLElement>) {
|
||||
box.css({
|
||||
'vertical-align': 'middle',
|
||||
});
|
||||
}
|
|
@ -15,6 +15,7 @@ toastr.options = {
|
|||
closeButton: true,
|
||||
progressBar: true,
|
||||
tapToDismiss: false,
|
||||
escapeHtml: true,
|
||||
// custom button, using the lucide icon x.svg inside
|
||||
closeHtml: `<button type="button"><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
@ -1,40 +1,21 @@
|
|||
import _ from 'lodash';
|
||||
import { selectRegistry } from '@/react/docker/images/ItemView/RegistrySelectPrompt';
|
||||
|
||||
angular.module('portainer.app').factory('RegistryModalService', ModalServiceFactory);
|
||||
angular.module('portainer.app').factory('RegistryModalService', RegistryModalService);
|
||||
|
||||
function ModalServiceFactory($q, ModalService, RegistryService) {
|
||||
function RegistryModalService(RegistryService) {
|
||||
const service = {};
|
||||
|
||||
function registries2Options(registries) {
|
||||
return registries.map((r) => ({
|
||||
text: r.Name,
|
||||
value: String(r.Id),
|
||||
}));
|
||||
}
|
||||
|
||||
service.registryModal = async function (repository, registries) {
|
||||
const deferred = $q.defer();
|
||||
|
||||
const options = registries2Options(registries);
|
||||
const registryModel = RegistryService.retrievePorRegistryModelFromRepositoryWithRegistries(repository, registries);
|
||||
const defaultValue = String(_.get(registryModel, 'Registry.Id', '0'));
|
||||
const defaultValue = _.get(registryModel, 'Registry.Id', 0);
|
||||
|
||||
ModalService.selectRegistry({
|
||||
title: 'Which registry do you want to use?',
|
||||
inputType: 'select',
|
||||
inputOptions: options,
|
||||
value: defaultValue,
|
||||
callback: (registryId) => {
|
||||
if (registryId) {
|
||||
const registryModel = RegistryService.retrievePorRegistryModelFromRepositoryWithRegistries(repository, registries, registryId);
|
||||
deferred.resolve(registryModel);
|
||||
} else {
|
||||
deferred.resolve(null);
|
||||
}
|
||||
},
|
||||
});
|
||||
const registryId = await selectRegistry(registries, defaultValue);
|
||||
if (!registryId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
return RegistryService.retrievePorRegistryModelFromRepositoryWithRegistries(repository, registries, registryId);
|
||||
};
|
||||
|
||||
return service;
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
import { confirmChangePassword, confirmDelete } from '@@/modals/confirm';
|
||||
import { openDialog } from '@@/modals/Dialog';
|
||||
import { buildConfirmButton } from '@@/modals/utils';
|
||||
|
||||
angular.module('portainer.app').controller('AccountController', [
|
||||
'$scope',
|
||||
'$state',
|
||||
|
@ -7,8 +11,7 @@ angular.module('portainer.app').controller('AccountController', [
|
|||
'SettingsService',
|
||||
'StateManager',
|
||||
'ThemeManager',
|
||||
'ModalService',
|
||||
function ($scope, $state, Authentication, UserService, Notifications, SettingsService, StateManager, ThemeManager, ModalService) {
|
||||
function ($scope, $state, Authentication, UserService, Notifications, SettingsService, StateManager, ThemeManager) {
|
||||
$scope.formValues = {
|
||||
currentPassword: '',
|
||||
newPassword: '',
|
||||
|
@ -17,7 +20,7 @@ angular.module('portainer.app').controller('AccountController', [
|
|||
};
|
||||
|
||||
$scope.updatePassword = async function () {
|
||||
const confirmed = await ModalService.confirmChangePassword();
|
||||
const confirmed = await confirmChangePassword();
|
||||
if (confirmed) {
|
||||
try {
|
||||
await UserService.updateUserPassword($scope.userID, $scope.formValues.currentPassword, $scope.formValues.newPassword);
|
||||
|
@ -56,8 +59,9 @@ angular.module('portainer.app').controller('AccountController', [
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($scope.forceChangePassword) {
|
||||
ModalService.confirmForceChangePassword();
|
||||
confirmForceChangePassword();
|
||||
}
|
||||
return !$scope.forceChangePassword;
|
||||
};
|
||||
|
@ -69,7 +73,7 @@ angular.module('portainer.app').controller('AccountController', [
|
|||
$scope.removeAction = (selectedTokens) => {
|
||||
const msg = 'Do you want to remove the selected access token(s)? Any script or application using these tokens will no longer be able to invoke the Portainer API.';
|
||||
|
||||
ModalService.confirmDeletion(msg, function (confirmed) {
|
||||
confirmDelete(msg).then((confirmed) => {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
@ -160,3 +164,10 @@ angular.module('portainer.app').controller('AccountController', [
|
|||
initView();
|
||||
},
|
||||
]);
|
||||
|
||||
function confirmForceChangePassword() {
|
||||
return openDialog({
|
||||
message: 'Please update your password to a stronger password to continue using Portainer',
|
||||
buttons: [buildConfirmButton('OK')],
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,30 +4,17 @@ import { TEMPLATE_NAME_VALIDATION_REGEX } from '@/constants';
|
|||
import { getTemplateVariables, intersectVariables } from '@/react/portainer/custom-templates/components/utils';
|
||||
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
|
||||
import { editor, upload, git } from '@@/BoxSelector/common-options/build-methods';
|
||||
import { confirmWebEditorDiscard } from '@@/modals/confirm';
|
||||
|
||||
class CreateCustomTemplateViewController {
|
||||
/* @ngInject */
|
||||
constructor(
|
||||
$async,
|
||||
$state,
|
||||
$scope,
|
||||
$window,
|
||||
Authentication,
|
||||
ModalService,
|
||||
CustomTemplateService,
|
||||
FormValidator,
|
||||
Notifications,
|
||||
ResourceControlService,
|
||||
StackService,
|
||||
StateManager
|
||||
) {
|
||||
constructor($async, $state, $scope, $window, Authentication, CustomTemplateService, FormValidator, Notifications, ResourceControlService, StackService, StateManager) {
|
||||
Object.assign(this, {
|
||||
$async,
|
||||
$state,
|
||||
$window,
|
||||
$scope,
|
||||
Authentication,
|
||||
ModalService,
|
||||
CustomTemplateService,
|
||||
FormValidator,
|
||||
Notifications,
|
||||
|
@ -253,7 +240,7 @@ class CreateCustomTemplateViewController {
|
|||
|
||||
async uiCanExit() {
|
||||
if (this.state.Method === 'editor' && this.formValues.FileContent && this.state.isEditorDirty) {
|
||||
return this.ModalService.confirmWebEditorDiscard();
|
||||
return confirmWebEditorDiscard();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { AccessControlFormData } from 'Portainer/components/accessControlForm/po
|
|||
import { TEMPLATE_NAME_VALIDATION_REGEX } from '@/constants';
|
||||
import { renderTemplate } from '@/react/portainer/custom-templates/components/utils';
|
||||
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
|
||||
import { confirmDelete } from '@@/modals/confirm';
|
||||
|
||||
class CustomTemplatesViewController {
|
||||
/* @ngInject */
|
||||
|
@ -14,7 +15,6 @@ class CustomTemplatesViewController {
|
|||
Authentication,
|
||||
CustomTemplateService,
|
||||
FormValidator,
|
||||
ModalService,
|
||||
NetworkService,
|
||||
Notifications,
|
||||
ResourceControlService,
|
||||
|
@ -28,7 +28,6 @@ class CustomTemplatesViewController {
|
|||
this.Authentication = Authentication;
|
||||
this.CustomTemplateService = CustomTemplateService;
|
||||
this.FormValidator = FormValidator;
|
||||
this.ModalService = ModalService;
|
||||
this.NetworkService = NetworkService;
|
||||
this.Notifications = Notifications;
|
||||
this.ResourceControlService = ResourceControlService;
|
||||
|
@ -244,7 +243,7 @@ class CustomTemplatesViewController {
|
|||
return this.$async(this.confirmDeleteAsync, templateId);
|
||||
}
|
||||
async confirmDeleteAsync(templateId) {
|
||||
const confirmed = await this.ModalService.confirmDeletionAsync('Are you sure that you want to delete this template?');
|
||||
const confirmed = await confirmDelete('Are you sure that you want to delete this template?');
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -4,11 +4,12 @@ import { ResourceControlViewModel } from '@/react/portainer/access-control/model
|
|||
import { AccessControlFormData } from 'Portainer/components/accessControlForm/porAccessControlFormModel';
|
||||
import { getTemplateVariables, intersectVariables } from '@/react/portainer/custom-templates/components/utils';
|
||||
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
|
||||
import { confirmWebEditorDiscard } from '@@/modals/confirm';
|
||||
|
||||
class EditCustomTemplateViewController {
|
||||
/* @ngInject */
|
||||
constructor($async, $state, $window, ModalService, Authentication, CustomTemplateService, FormValidator, Notifications, ResourceControlService) {
|
||||
Object.assign(this, { $async, $state, $window, ModalService, Authentication, CustomTemplateService, FormValidator, Notifications, ResourceControlService });
|
||||
constructor($async, $state, $window, Authentication, CustomTemplateService, FormValidator, Notifications, ResourceControlService) {
|
||||
Object.assign(this, { $async, $state, $window, Authentication, CustomTemplateService, FormValidator, Notifications, ResourceControlService });
|
||||
|
||||
this.isTemplateVariablesEnabled = isBE;
|
||||
|
||||
|
@ -146,7 +147,7 @@ class EditCustomTemplateViewController {
|
|||
|
||||
async uiCanExit() {
|
||||
if (this.formValues.FileContent !== this.oldFileContent && this.state.isEditorDirty) {
|
||||
return this.ModalService.confirmWebEditorDiscard();
|
||||
return confirmWebEditorDiscard();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@ import { createProfile } from 'Portainer/hostmanagement/fdo/fdo.service';
|
|||
|
||||
angular.module('portainer.app').controller('AddProfileController', AddProfileController);
|
||||
|
||||
export default function AddProfileController($scope, $async, $state, $window, ModalService, Authentication, Notifications) {
|
||||
/* @ngInject */
|
||||
export default function AddProfileController($scope, $async, $state, $window, Notifications) {
|
||||
$scope.buildMethods = [editor];
|
||||
|
||||
$scope.formValues = {
|
||||
|
|
|
@ -4,7 +4,8 @@ import { getProfile, updateProfile } from 'Portainer/hostmanagement/fdo/fdo.serv
|
|||
|
||||
angular.module('portainer.app').controller('EditProfileController', EditProfileController);
|
||||
|
||||
export default function EditProfileController($scope, $async, $state, $window, ModalService, Authentication, Notifications) {
|
||||
/* @ngInject */
|
||||
export default function EditProfileController($scope, $async, $state, $window, Notifications) {
|
||||
$scope.buildMethods = [editor];
|
||||
|
||||
$scope.formValues = {
|
||||
|
|
|
@ -5,11 +5,13 @@ import { PortainerEndpointTypes } from '@/portainer/models/endpoint/models';
|
|||
import { EndpointSecurityFormData } from '@/portainer/components/endpointSecurity/porEndpointSecurityModel';
|
||||
import EndpointHelper from '@/portainer/helpers/endpointHelper';
|
||||
import { getAMTInfo } from 'Portainer/hostmanagement/open-amt/open-amt.service';
|
||||
import { confirmDestructiveAsync } from '@/portainer/services/modal.service/confirm';
|
||||
import { confirmDestructive } from '@@/modals/confirm';
|
||||
import { isEdgeEnvironment } from '@/react/portainer/environments/utils';
|
||||
|
||||
import { commandsTabs } from '@/react/edge/components/EdgeScriptForm/scripts';
|
||||
import { GpusListAngular } from '@/react/portainer/environments/wizard/EnvironmentsCreationView/shared/Hardware/GpusList';
|
||||
import { confirmDisassociate } from '@/react/portainer/environments/ItemView/ConfirmDisassociateModel';
|
||||
import { buildConfirmButton } from '@@/modals/utils';
|
||||
|
||||
angular.module('portainer.app').component('gpusList', GpusListAngular).controller('EndpointController', EndpointController);
|
||||
|
||||
|
@ -26,8 +28,7 @@ function EndpointController(
|
|||
|
||||
Notifications,
|
||||
Authentication,
|
||||
SettingsService,
|
||||
ModalService
|
||||
SettingsService
|
||||
) {
|
||||
$scope.onChangeCheckInInterval = onChangeCheckInInterval;
|
||||
$scope.setFieldValue = setFieldValue;
|
||||
|
@ -114,7 +115,7 @@ function EndpointController(
|
|||
};
|
||||
|
||||
$scope.onDisassociateEndpoint = async function () {
|
||||
ModalService.confirmDisassociate((confirmed) => {
|
||||
confirmDisassociate().then((confirmed) => {
|
||||
if (confirmed) {
|
||||
disassociateEndpoint();
|
||||
}
|
||||
|
@ -192,19 +193,10 @@ function EndpointController(
|
|||
var TLSSkipClientVerify = TLS && (TLSMode === 'tls_ca' || TLSMode === 'tls_only');
|
||||
|
||||
if (isEdgeEnvironment(endpoint.Type) && _.difference($scope.initialTagIds, endpoint.TagIds).length > 0) {
|
||||
let confirmed = await confirmDestructiveAsync({
|
||||
let confirmed = await confirmDestructive({
|
||||
title: 'Confirm action',
|
||||
message: 'Removing tags from this environment will remove the corresponding edge stacks when dynamic grouping is being used',
|
||||
buttons: {
|
||||
cancel: {
|
||||
label: 'Cancel',
|
||||
className: 'btn-default',
|
||||
},
|
||||
confirm: {
|
||||
label: 'Confirm',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
},
|
||||
confirmButton: buildConfirmButton(),
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { map } from 'lodash';
|
||||
import EndpointHelper from '@/portainer/helpers/endpointHelper';
|
||||
import { getEnvironments } from '@/react/portainer/environments/environment.service';
|
||||
import { confirmDelete } from '@@/modals/confirm';
|
||||
|
||||
export class EndpointsController {
|
||||
/* @ngInject */
|
||||
constructor($state, $async, EndpointService, GroupService, ModalService, Notifications, EndpointProvider, StateManager) {
|
||||
constructor($state, $async, EndpointService, GroupService, Notifications, EndpointProvider, StateManager) {
|
||||
Object.assign(this, {
|
||||
$state,
|
||||
$async,
|
||||
EndpointService,
|
||||
GroupService,
|
||||
ModalService,
|
||||
Notifications,
|
||||
EndpointProvider,
|
||||
StateManager,
|
||||
|
@ -30,7 +30,7 @@ export class EndpointsController {
|
|||
}
|
||||
|
||||
removeAction(endpoints) {
|
||||
this.ModalService.confirmDeletion('This action will remove all configurations associated to your environment(s). Continue?', (confirmed) => {
|
||||
confirmDelete('This action will remove all configurations associated to your environment(s). Continue?').then((confirmed) => {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import _ from 'lodash-es';
|
||||
import { confirmDelete } from '@@/modals/confirm';
|
||||
import { RegistryTypes } from 'Portainer/models/registryTypes';
|
||||
|
||||
angular.module('portainer.app').controller('RegistriesController', [
|
||||
|
@ -6,9 +7,8 @@ angular.module('portainer.app').controller('RegistriesController', [
|
|||
'$scope',
|
||||
'$state',
|
||||
'RegistryService',
|
||||
'ModalService',
|
||||
'Notifications',
|
||||
function ($q, $scope, $state, RegistryService, ModalService, Notifications) {
|
||||
function ($q, $scope, $state, RegistryService, Notifications) {
|
||||
$scope.state = {
|
||||
actionInProgress: false,
|
||||
};
|
||||
|
@ -24,7 +24,7 @@ angular.module('portainer.app').controller('RegistriesController', [
|
|||
const registriesMsg = selectedItems.length > 1 ? 'registries' : 'registry';
|
||||
const msg = `T${regAttrMsg} ${registriesMsg} might be used by applications inside one or more environments. Removing the ${registriesMsg} could lead to a service interruption for the applications using t${regAttrMsg} ${registriesMsg}. Do you want to remove the selected ${registriesMsg}?`;
|
||||
|
||||
ModalService.confirmDeletion(msg, function onConfirm(confirmed) {
|
||||
confirmDelete(msg).then((confirmed) => {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -7,12 +7,10 @@ angular.module('portainer.app').controller('SettingsController', [
|
|||
'$scope',
|
||||
'Notifications',
|
||||
'SettingsService',
|
||||
'ModalService',
|
||||
'StateManager',
|
||||
'BackupService',
|
||||
'FileSaver',
|
||||
'Blob',
|
||||
function ($scope, Notifications, SettingsService, ModalService, StateManager, BackupService, FileSaver) {
|
||||
function ($scope, Notifications, SettingsService, StateManager, BackupService, FileSaver) {
|
||||
$scope.customBannerFeatureId = FeatureId.CUSTOM_LOGIN_BANNER;
|
||||
$scope.s3BackupFeatureId = FeatureId.S3_BACKUP_SETTING;
|
||||
$scope.enforceDeploymentOptions = FeatureId.ENFORCE_DEPLOYMENT_OPTIONS;
|
||||
|
|
|
@ -8,6 +8,7 @@ import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
|||
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
|
||||
import { renderTemplate } from '@/react/portainer/custom-templates/components/utils';
|
||||
import { editor, upload, git, customTemplate } from '@@/BoxSelector/common-options/build-methods';
|
||||
import { confirmWebEditorDiscard } from '@@/modals/confirm';
|
||||
|
||||
angular
|
||||
.module('portainer.app')
|
||||
|
@ -18,7 +19,6 @@ angular
|
|||
$state,
|
||||
$async,
|
||||
$window,
|
||||
ModalService,
|
||||
StackService,
|
||||
Authentication,
|
||||
Notifications,
|
||||
|
@ -363,7 +363,7 @@ angular
|
|||
|
||||
this.uiCanExit = async function () {
|
||||
if ($scope.state.Method === 'editor' && $scope.formValues.StackFileContent && $scope.state.isEditorDirty) {
|
||||
return ModalService.confirmWebEditorDiscard();
|
||||
return confirmWebEditorDiscard();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -4,10 +4,13 @@ import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
|||
import { getEnvironments } from '@/react/portainer/environments/environment.service';
|
||||
import { StackStatus, StackType } from '@/react/docker/stacks/types';
|
||||
import { extractContainerNames } from '@/portainer/helpers/stackHelper';
|
||||
import { confirmStackUpdate } from '@/react/docker/stacks/common/confirm-stack-update';
|
||||
import { confirm, confirmDelete, confirmWebEditorDiscard } from '@@/modals/confirm';
|
||||
import { ModalType } from '@@/modals';
|
||||
import { buildConfirmButton } from '@@/modals/utils';
|
||||
|
||||
angular.module('portainer.app').controller('StackController', [
|
||||
'$async',
|
||||
'$compile',
|
||||
'$q',
|
||||
'$scope',
|
||||
'$state',
|
||||
|
@ -23,7 +26,6 @@ angular.module('portainer.app').controller('StackController', [
|
|||
'Notifications',
|
||||
'FormHelper',
|
||||
'GroupService',
|
||||
'ModalService',
|
||||
'StackHelper',
|
||||
'ResourceControlService',
|
||||
'Authentication',
|
||||
|
@ -31,7 +33,6 @@ angular.module('portainer.app').controller('StackController', [
|
|||
'endpoint',
|
||||
function (
|
||||
$async,
|
||||
$compile,
|
||||
$q,
|
||||
$scope,
|
||||
$state,
|
||||
|
@ -47,7 +48,6 @@ angular.module('portainer.app').controller('StackController', [
|
|||
Notifications,
|
||||
FormHelper,
|
||||
GroupService,
|
||||
ModalService,
|
||||
StackHelper,
|
||||
ResourceControlService,
|
||||
Authentication,
|
||||
|
@ -129,29 +129,24 @@ angular.module('portainer.app').controller('StackController', [
|
|||
};
|
||||
|
||||
$scope.migrateStack = function (name, endpointId) {
|
||||
return $q(function (resolve) {
|
||||
ModalService.confirmWarn({
|
||||
return $q(async function (resolve) {
|
||||
const confirmed = await confirm({
|
||||
title: 'Are you sure?',
|
||||
modalType: ModalType.Warn,
|
||||
message:
|
||||
'This action will deploy a new instance of this stack on the target environment, please note that this does NOT relocate the content of any persistent volumes that may be attached to this stack.',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Migrate',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
callback: function onConfirm(confirmed) {
|
||||
if (!confirmed) {
|
||||
return resolve();
|
||||
}
|
||||
return resolve(migrateStack(name, endpointId));
|
||||
},
|
||||
confirmButton: buildConfirmButton('Migrate', 'danger'),
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return resolve();
|
||||
}
|
||||
return resolve(migrateStack(name, endpointId));
|
||||
});
|
||||
};
|
||||
|
||||
$scope.removeStack = function () {
|
||||
ModalService.confirmDeletion('Do you want to remove the stack? Associated services will be removed as well.', function onConfirm(confirmed) {
|
||||
confirmDelete('Do you want to remove the stack? Associated services will be removed as well').then((confirmed) => {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
@ -160,10 +155,11 @@ angular.module('portainer.app').controller('StackController', [
|
|||
};
|
||||
|
||||
$scope.detachStackFromGit = function () {
|
||||
ModalService.confirmDetachment('Do you want to detach the stack from Git?', function onConfirm(confirmed) {
|
||||
confirmDetachment().then(function onConfirm(confirmed) {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.deployStack();
|
||||
});
|
||||
};
|
||||
|
@ -240,7 +236,7 @@ angular.module('portainer.app').controller('StackController', [
|
|||
$scope.deployStack = function () {
|
||||
const stack = $scope.stack;
|
||||
const isSwarmStack = stack.Type === 1;
|
||||
ModalService.confirmStackUpdate('Do you want to force an update of the stack?', isSwarmStack, null, function (result) {
|
||||
confirmStackUpdate('Do you want to force an update of the stack?', isSwarmStack).then(function (result) {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
@ -257,7 +253,7 @@ angular.module('portainer.app').controller('StackController', [
|
|||
}
|
||||
|
||||
$scope.state.actionInProgress = true;
|
||||
StackService.updateStack(stack, stackFile, env, prune, !!result[0])
|
||||
StackService.updateStack(stack, stackFile, env, prune, result.pullImage)
|
||||
.then(function success() {
|
||||
Notifications.success('Success', 'Stack successfully deployed');
|
||||
$scope.state.isEditorDirty = false;
|
||||
|
@ -285,10 +281,11 @@ angular.module('portainer.app').controller('StackController', [
|
|||
return $async(stopStackAsync);
|
||||
}
|
||||
async function stopStackAsync() {
|
||||
const confirmed = await ModalService.confirmAsync({
|
||||
const confirmed = await confirm({
|
||||
title: 'Are you sure?',
|
||||
modalType: ModalType.Warn,
|
||||
message: 'Are you sure you want to stop this stack?',
|
||||
buttons: { confirm: { label: 'Stop', className: 'btn-danger' } },
|
||||
confirmButton: buildConfirmButton('Stop', 'danger'),
|
||||
});
|
||||
if (!confirmed) {
|
||||
return;
|
||||
|
@ -447,7 +444,7 @@ angular.module('portainer.app').controller('StackController', [
|
|||
|
||||
this.uiCanExit = async function () {
|
||||
if ($scope.stackFileContent && $scope.state.isEditorDirty) {
|
||||
return ModalService.confirmWebEditorDiscard();
|
||||
return confirmWebEditorDiscard();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -494,3 +491,12 @@ angular.module('portainer.app').controller('StackController', [
|
|||
initView();
|
||||
},
|
||||
]);
|
||||
|
||||
function confirmDetachment() {
|
||||
return confirm({
|
||||
modalType: ModalType.Warn,
|
||||
title: 'Are you sure?',
|
||||
message: 'Do you want to detach the stack from Git?',
|
||||
confirmButton: buildConfirmButton('Detach', 'danger'),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { confirmDelete } from '@@/modals/confirm';
|
||||
|
||||
angular.module('portainer.app').controller('StacksController', StacksController);
|
||||
|
||||
/* @ngInject */
|
||||
function StacksController($scope, $state, Notifications, StackService, ModalService, Authentication, endpoint) {
|
||||
function StacksController($scope, $state, Notifications, StackService, Authentication, endpoint) {
|
||||
$scope.removeAction = function (selectedItems) {
|
||||
ModalService.confirmDeletion('Do you want to remove the selected stack(s)? Associated services will be removed as well.', function onConfirm(confirmed) {
|
||||
confirmDelete('Do you want to remove the selected stack(s)? Associated services will be removed as well.').then((confirmed) => {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import { ModalType } from '@@/modals';
|
||||
import { buildConfirmButton } from '@@/modals/utils';
|
||||
import { confirm, confirmChangePassword, confirmDelete } from '@@/modals/confirm';
|
||||
|
||||
angular.module('portainer.app').controller('UserController', [
|
||||
'$q',
|
||||
'$scope',
|
||||
'$state',
|
||||
'$transition$',
|
||||
'UserService',
|
||||
'ModalService',
|
||||
'Notifications',
|
||||
'SettingsService',
|
||||
'Authentication',
|
||||
function ($q, $scope, $state, $transition$, UserService, ModalService, Notifications, SettingsService, Authentication) {
|
||||
function ($q, $scope, $state, $transition$, UserService, Notifications, SettingsService, Authentication) {
|
||||
$scope.state = {
|
||||
updatePasswordError: '',
|
||||
};
|
||||
|
@ -27,7 +30,7 @@ angular.module('portainer.app').controller('UserController', [
|
|||
};
|
||||
|
||||
$scope.deleteUser = function () {
|
||||
ModalService.confirmDeletion('Do you want to remove this user? This user will not be able to login into Portainer anymore.', function onConfirm(confirmed) {
|
||||
confirmDelete('Do you want to remove this user? This user will not be able to login into Portainer anymore.').then((confirmed) => {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
@ -39,26 +42,20 @@ angular.module('portainer.app').controller('UserController', [
|
|||
const role = $scope.formValues.Administrator ? 1 : 2;
|
||||
const oldUsername = $scope.user.Username;
|
||||
const username = $scope.formValues.username;
|
||||
let promise = Promise.resolve(true);
|
||||
|
||||
if (username != oldUsername) {
|
||||
promise = new Promise((resolve) =>
|
||||
ModalService.confirmWarn({
|
||||
title: 'Are you sure?',
|
||||
message: `Are you sure you want to rename the user ${oldUsername} to ${username}?`,
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Update',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
},
|
||||
callback: resolve,
|
||||
})
|
||||
);
|
||||
}
|
||||
const confirmed = await promise;
|
||||
if (!confirmed) {
|
||||
return;
|
||||
const confirmed = await confirm({
|
||||
title: 'Are you sure?',
|
||||
modalType: ModalType.Warn,
|
||||
message: `Are you sure you want to rename the user ${oldUsername} to ${username}?`,
|
||||
confirmButton: buildConfirmButton('Update'),
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UserService.updateUser($scope.user.Id, { role, username })
|
||||
.then(function success() {
|
||||
Notifications.success('Success', 'User successfully updated');
|
||||
|
@ -71,7 +68,7 @@ angular.module('portainer.app').controller('UserController', [
|
|||
|
||||
$scope.updatePassword = async function () {
|
||||
const isCurrentUser = Authentication.getUserDetails().ID === $scope.user.Id;
|
||||
const confirmed = !isCurrentUser || (await ModalService.confirmChangePassword());
|
||||
const confirmed = !isCurrentUser || (await confirmChangePassword());
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import _ from 'lodash-es';
|
||||
import { confirmDelete } from '@@/modals/confirm';
|
||||
|
||||
angular.module('portainer.app').controller('UsersController', [
|
||||
'$q',
|
||||
|
@ -7,11 +8,10 @@ angular.module('portainer.app').controller('UsersController', [
|
|||
'UserService',
|
||||
'TeamService',
|
||||
'TeamMembershipService',
|
||||
'ModalService',
|
||||
'Notifications',
|
||||
'Authentication',
|
||||
'SettingsService',
|
||||
function ($q, $scope, $state, UserService, TeamService, TeamMembershipService, ModalService, Notifications, Authentication, SettingsService) {
|
||||
function ($q, $scope, $state, UserService, TeamService, TeamMembershipService, Notifications, Authentication, SettingsService) {
|
||||
$scope.state = {
|
||||
userCreationError: '',
|
||||
validUsername: false,
|
||||
|
@ -91,7 +91,7 @@ angular.module('portainer.app').controller('UsersController', [
|
|||
}
|
||||
|
||||
$scope.removeAction = function (selectedItems) {
|
||||
ModalService.confirmDeletion('Do you want to remove the selected users? They will not be able to login into Portainer anymore.', function onConfirm(confirmed) {
|
||||
confirmDelete('Do you want to remove the selected users? They will not be able to login into Portainer anymore.').then((confirmed) => {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue