diff --git a/app/agent/components/host-browser/hostBrowserController.js b/app/agent/components/host-browser/hostBrowserController.js
index 22f18960e..4d5ddaae9 100644
--- a/app/agent/components/host-browser/hostBrowserController.js
+++ b/app/agent/components/host-browser/hostBrowserController.js
@@ -1,11 +1,12 @@
import _ from 'lodash-es';
+import { confirmDelete } from '@@/modals/confirm';
const ROOT_PATH = '/host';
export class HostBrowserController {
/* @ngInject */
- constructor($async, HostBrowserService, Notifications, FileSaver, ModalService) {
- Object.assign(this, { $async, HostBrowserService, Notifications, FileSaver, ModalService });
+ constructor($async, HostBrowserService, Notifications, FileSaver) {
+ Object.assign(this, { $async, HostBrowserService, Notifications, FileSaver });
this.state = {
path: ROOT_PATH,
@@ -95,7 +96,7 @@ export class HostBrowserController {
confirmDeleteFile(name) {
const filePath = this.buildPath(this.state.path, name);
- this.ModalService.confirmDeletion(`Are you sure that you want to delete ${this.getRelativePath(filePath)} ?`, (confirmed) => {
+ confirmDelete(`Are you sure that you want to delete ${this.getRelativePath(filePath)}?`).then((confirmed) => {
if (!confirmed) {
return;
}
diff --git a/app/agent/components/volume-browser/volumeBrowserController.js b/app/agent/components/volume-browser/volumeBrowserController.js
index 61524f5cb..c0f30734e 100644
--- a/app/agent/components/volume-browser/volumeBrowserController.js
+++ b/app/agent/components/volume-browser/volumeBrowserController.js
@@ -1,9 +1,10 @@
import _ from 'lodash-es';
+import { confirmDelete } from '@@/modals/confirm';
export class VolumeBrowserController {
/* @ngInject */
- constructor($async, HttpRequestHelper, VolumeBrowserService, FileSaver, Blob, ModalService, Notifications) {
- Object.assign(this, { $async, HttpRequestHelper, VolumeBrowserService, FileSaver, Blob, ModalService, Notifications });
+ constructor($async, HttpRequestHelper, VolumeBrowserService, FileSaver, Blob, Notifications) {
+ Object.assign(this, { $async, HttpRequestHelper, VolumeBrowserService, FileSaver, Blob, Notifications });
this.state = {
path: '/',
};
@@ -47,7 +48,7 @@ export class VolumeBrowserController {
confirmDelete(file) {
const filePath = this.state.path === '/' ? file : `${this.state.path}/${file}`;
- this.ModalService.confirmDeletion(`Are you sure that you want to delete ${filePath} ?`, (confirmed) => {
+ confirmDelete(`Are you sure that you want to delete ${filePath} ?`).then((confirmed) => {
if (!confirmed) {
return;
}
diff --git a/app/assets/css/app.css b/app/assets/css/app.css
index 520f7bc83..ffbe4a6b5 100644
--- a/app/assets/css/app.css
+++ b/app/assets/css/app.css
@@ -401,34 +401,6 @@ input[type='checkbox'] {
float: none !important;
}
-.bootbox-form .bootbox-input-checkbox {
- display: none;
-}
-
-.bootbox-form .visible {
- position: initial !important;
- display: initial !important;
- margin-left: 10px !important;
- margin-top: -2px !important;
-}
-
-.bootbox-form label {
- padding-left: 0;
-}
-
-.bootbox-checkbox-list {
- max-height: 200px;
- overflow-y: auto;
- background-color: var(--white-color);
- border: 1px solid var(--grey-48);
- border-radius: 4px;
-}
-
-:root[theme='dark'] .bootbox-checkbox-list,
-:root[theme='highcontrast'] .bootbox-checkbox-list {
- background-color: var(--bg-modal-content-color);
-}
-
.small-select {
display: inline-block;
padding: 0px 6px;
@@ -440,10 +412,6 @@ input[type='checkbox'] {
font-size: 14px;
}
-.bootbox-form .checkbox i {
- margin-left: 21px;
-}
-
.visualizer_container {
display: flex;
flex-direction: row;
@@ -584,20 +552,6 @@ input[type='checkbox'] {
padding: 0 !important;
}
-.modal::before {
- content: '';
- display: inline-block;
- height: 100%;
- vertical-align: middle;
- margin-right: -4px;
-}
-
-.modal-dialog {
- display: inline-block;
- text-align: left;
- vertical-align: middle;
-}
-
.striked::after {
border-bottom: 0.2em solid var(--grey-26);
content: '';
@@ -630,17 +584,6 @@ input[type='checkbox'] {
margin-right: -50%;
}
-/*bootbox override */
-.modal-open {
- padding-right: 0 !important;
-}
-
-.image-zoom-modal .modal-dialog {
- width: 80%;
-}
-
-/*!bootbox override*/
-
/*toaster override*/
#toast-container > div {
opacity: 0.9;
@@ -732,20 +675,6 @@ json-tree .branch-preview {
cursor: not-allowed;
}
-/* used for bootbox prompt with inputType radio */
-.form-check.radio {
- margin-left: 15px;
-}
-
-.inline-text {
- display: inline;
- position: absolute;
- font-family: 'Montserrat';
- font-size: smaller;
- margin-left: 5px;
- margin-right: 5px;
-}
-
.web-editor {
background-color: var(--bg-webeditor-color);
border-radius: 8px;
diff --git a/app/assets/css/bootstrap-override.css b/app/assets/css/bootstrap-override.css
index 3bdf61b0e..526fef18a 100644
--- a/app/assets/css/bootstrap-override.css
+++ b/app/assets/css/bootstrap-override.css
@@ -284,65 +284,7 @@ input:checked + .slider:before {
width: 450px;
}
-.modal-content {
- padding: 20px;
-}
-
-.background-error {
- padding-top: 55px;
- background-image: url(../images/icon-error.svg);
- background-repeat: no-repeat;
- background-position: top left;
-}
-
-.background-warning {
- padding-top: 55px;
- background-image: url(../images/icon-warning.svg);
- background-repeat: no-repeat;
- background-position: top left;
-}
-
-.modal-header {
- margin-bottom: 10px;
- padding: 0px;
- border-bottom: none;
-}
-
-.modal-header .close {
- margin-top: 0px;
-}
-
-.modal-header .modal-title {
- font-weight: bold;
-}
-
-.modal-body {
- padding: 10px 0px;
- border-bottom: none;
-}
-
-.modal-body .bootbox-body {
- font-size: 12px;
- color: var(--text-bootbox);
-}
-
-.modal-footer {
- padding: 10px 0px;
- border-top: none;
- display: flex;
-}
-
-.modal-footer .bootbox-cancel {
- width: 100%;
-}
-
-.modal-footer .bootbox-accept {
- width: 100%;
-}
-
-.bootbox-checkbox-list {
- border: 0px;
-}
+/* !Modal */
/* Status Indicator Inside Table Section Label Style */
.table .label {
diff --git a/app/assets/css/vendor-override.css b/app/assets/css/vendor-override.css
index 224c352ca..48bbfffc6 100644
--- a/app/assets/css/vendor-override.css
+++ b/app/assets/css/vendor-override.css
@@ -17,8 +17,7 @@
background-color: var(--bg-hover-table-color);
}
-.switch i,
-.bootbox-form .checkbox i {
+.switch i {
background: var(--bg-switch-box-color);
}
@@ -77,25 +76,6 @@ fieldset[disabled] .form-control {
background-color: var(--bg-modal-content-color);
}
-.modal-header {
- border-bottom: 1px solid var(--border-modal-header-color);
-}
-
-.modal-footer {
- border-top: 1px solid var(--border-modal-header-color);
-}
-
-.close {
- color: var(--button-close-color);
- opacity: var(--button-opacity);
-}
-
-.close:hover,
-.close:focus {
- color: var(--button-close-color);
- opacity: var(--button-opacity-hover);
-}
-
code {
color: var(--text-code-color);
background-color: var(--bg-code-color);
diff --git a/app/docker/components/datatables/services-datatable/actions/servicesDatatableActionsController.js b/app/docker/components/datatables/services-datatable/actions/servicesDatatableActionsController.js
index de6849191..91ba1fb5a 100644
--- a/app/docker/components/datatables/services-datatable/actions/servicesDatatableActionsController.js
+++ b/app/docker/components/datatables/services-datatable/actions/servicesDatatableActionsController.js
@@ -1,13 +1,15 @@
+import { confirmDelete } from '@@/modals/confirm';
+import { confirmServiceForceUpdate } from '@/react/docker/services/common/update-service-modal';
+
angular.module('portainer.docker').controller('ServicesDatatableActionsController', [
'$q',
'$state',
'ServiceService',
'ServiceHelper',
'Notifications',
- 'ModalService',
'ImageHelper',
'WebhookService',
- function ($q, $state, ServiceService, ServiceHelper, Notifications, ModalService, ImageHelper, WebhookService) {
+ function ($q, $state, ServiceService, ServiceHelper, Notifications, ImageHelper, WebhookService) {
const ctrl = this;
this.scaleAction = function scaleService(service) {
@@ -26,29 +28,22 @@ angular.module('portainer.docker').controller('ServicesDatatableActionsControlle
};
this.removeAction = function (selectedItems) {
- ModalService.confirmDeletion(
- 'Do you want to remove the selected service(s)? All the containers associated to the selected service(s) will be removed too.',
- function onConfirm(confirmed) {
- if (!confirmed) {
- return;
- }
- removeServices(selectedItems);
+ confirmDelete('Do you want to remove the selected service(s)? All the containers associated to the selected service(s) will be removed too.').then((confirmed) => {
+ if (!confirmed) {
+ return;
}
- );
+ removeServices(selectedItems);
+ });
};
this.updateAction = function (selectedItems) {
- ModalService.confirmServiceForceUpdate(
- 'Do you want to force an update of the selected service(s)? All the tasks associated to the selected service(s) will be recreated.',
- function (result) {
+ confirmServiceForceUpdate('Do you want to force an update of the selected service(s)? All the tasks associated to the selected service(s) will be recreated.').then(
+ (result) => {
if (!result) {
return;
}
- var pullImage = false;
- if (result[0]) {
- pullImage = true;
- }
- forceUpdateServices(selectedItems, pullImage);
+
+ forceUpdateServices(selectedItems, result.pullLatest);
}
);
};
diff --git a/app/docker/views/configs/configsController.js b/app/docker/views/configs/configsController.js
index 4870625e7..911c858d4 100644
--- a/app/docker/views/configs/configsController.js
+++ b/app/docker/views/configs/configsController.js
@@ -1,5 +1,5 @@
import angular from 'angular';
-import { confirmDeletionAsync } from 'Portainer/services/modal.service/confirm';
+import { confirmDelete } from '@@/modals/confirm';
class ConfigsController {
/* @ngInject */
@@ -33,7 +33,7 @@ class ConfigsController {
}
async removeAction(selectedItems) {
- const confirmed = await confirmDeletionAsync('Do you want to remove the selected config(s)?');
+ const confirmed = await confirmDelete('Do you want to remove the selected config(s)?');
if (!confirmed) {
return null;
}
diff --git a/app/docker/views/configs/create/createConfigController.js b/app/docker/views/configs/create/createConfigController.js
index 1dac12043..ea5fdf40c 100644
--- a/app/docker/views/configs/create/createConfigController.js
+++ b/app/docker/views/configs/create/createConfigController.js
@@ -1,14 +1,14 @@
import _ from 'lodash-es';
import angular from 'angular';
import { AccessControlFormData } from 'Portainer/components/accessControlForm/porAccessControlFormModel';
+import { confirmWebEditorDiscard } from '@@/modals/confirm';
class CreateConfigController {
/* @ngInject */
- constructor($async, $state, $transition$, $window, ModalService, Notifications, ConfigService, Authentication, FormValidator, ResourceControlService) {
+ constructor($async, $state, $transition$, $window, Notifications, ConfigService, Authentication, FormValidator, ResourceControlService) {
this.$state = $state;
this.$transition$ = $transition$;
this.$window = $window;
- this.ModalService = ModalService;
this.Notifications = Notifications;
this.ConfigService = ConfigService;
this.Authentication = Authentication;
@@ -67,7 +67,7 @@ class CreateConfigController {
async uiCanExit() {
if (this.formValues.displayCodeEditor && this.formValues.ConfigContent && this.state.isEditorDirty) {
- return this.ModalService.confirmWebEditorDiscard();
+ return confirmWebEditorDiscard();
}
}
diff --git a/app/docker/views/containers/create/createContainerController.js b/app/docker/views/containers/create/createContainerController.js
index feafae51b..26cc19632 100644
--- a/app/docker/views/containers/create/createContainerController.js
+++ b/app/docker/views/containers/create/createContainerController.js
@@ -2,8 +2,11 @@ import _ from 'lodash-es';
import { PorImageRegistryModel } from 'Docker/models/porImageRegistry';
+import { confirmDestructive } from '@@/modals/confirm';
import * as envVarsUtils from '@/portainer/helpers/env-vars';
import { FeatureId } from '@/react/portainer/feature-flags/enums';
+import { buildConfirmButton } from '@@/modals/utils';
+
import { ContainerCapabilities, ContainerCapability } from '../../../models/containerCapabilities';
import { AccessControlFormData } from '../../../../portainer/components/accessControlForm/porAccessControlFormModel';
import { ContainerDetailsViewModel } from '../../../models/container';
@@ -30,7 +33,6 @@ angular.module('portainer.docker').controller('CreateContainerController', [
'ContainerService',
'ImageService',
'FormValidator',
- 'ModalService',
'RegistryService',
'SystemService',
'PluginService',
@@ -56,7 +58,6 @@ angular.module('portainer.docker').controller('CreateContainerController', [
ContainerService,
ImageService,
FormValidator,
- ModalService,
RegistryService,
SystemService,
PluginService,
@@ -1000,18 +1001,12 @@ angular.module('portainer.docker').controller('CreateContainerController', [
function showConfirmationModal() {
var deferred = $q.defer();
- ModalService.confirmDestructive({
- title: 'Are you sure ?',
+ confirmDestructive({
+ title: 'Are you sure?',
message: 'A container with the same name already exists. Portainer can automatically remove it and re-create one. Do you want to replace it?',
- buttons: {
- confirm: {
- label: 'Replace',
- className: 'btn-danger',
- },
- },
- callback: function onConfirm(confirmed) {
- deferred.resolve(confirmed);
- },
+ confirmButton: buildConfirmButton('Replace', 'danger'),
+ }).then(function onConfirm(confirmed) {
+ deferred.resolve(confirmed);
});
return deferred.promise;
diff --git a/app/docker/views/containers/edit/containerController.js b/app/docker/views/containers/edit/containerController.js
index 86e0c5235..81cd0ed04 100644
--- a/app/docker/views/containers/edit/containerController.js
+++ b/app/docker/views/containers/edit/containerController.js
@@ -1,9 +1,10 @@
import moment from 'moment';
import _ from 'lodash-es';
import { PorImageRegistryModel } from 'Docker/models/porImageRegistry';
-import { confirmContainerDeletion } from '@/portainer/services/modal.service/prompt';
+import { confirmContainerDeletion } from '@/react/docker/containers/common/confirm-container-delete-modal';
import { FeatureId } from '@/react/portainer/feature-flags/enums';
import { ResourceControlType } from '@/react/portainer/access-control/types';
+import { confirmContainerRecreation } from '@/react/docker/containers/ItemView/ConfirmRecreationModal';
angular.module('portainer.docker').controller('ContainerController', [
'$q',
@@ -18,7 +19,6 @@ angular.module('portainer.docker').controller('ContainerController', [
'ImageHelper',
'NetworkService',
'Notifications',
- 'ModalService',
'ResourceControlService',
'RegistryService',
'ImageService',
@@ -38,7 +38,6 @@ angular.module('portainer.docker').controller('ContainerController', [
ImageHelper,
NetworkService,
Notifications,
- ModalService,
ResourceControlService,
RegistryService,
ImageService,
@@ -275,20 +274,20 @@ angular.module('portainer.docker').controller('ContainerController', [
};
$scope.confirmRemove = function () {
- var title = 'You are about to remove a container.';
- if ($scope.container.State.Running) {
- title = 'You are about to remove a running container.';
- }
+ return $async(async () => {
+ var title = 'You are about to remove a container.';
+ if ($scope.container.State.Running) {
+ title = 'You are about to remove a running container.';
+ }
+
+ const result = await confirmContainerDeletion(title);
- confirmContainerDeletion(title, function (result) {
if (!result) {
return;
}
- var cleanAssociatedVolumes = false;
- if (result[0]) {
- cleanAssociatedVolumes = true;
- }
- removeContainer(cleanAssociatedVolumes);
+ const { removeVolumes } = result;
+
+ removeContainer(removeVolumes);
});
};
@@ -397,15 +396,12 @@ angular.module('portainer.docker').controller('ContainerController', [
$scope.recreate = function () {
const cannotPullImage = !$scope.container.Config.Image || $scope.container.Config.Image.toLowerCase().startsWith('sha256');
- ModalService.confirmContainerRecreation(cannotPullImage, function (result) {
+ confirmContainerRecreation(cannotPullImage).then(function (result) {
if (!result) {
return;
}
- var pullImage = false;
- if (result[0]) {
- pullImage = true;
- }
- recreateContainer(pullImage);
+
+ recreateContainer(result.pullLatest);
});
};
diff --git a/app/docker/views/images/build/buildImageController.js b/app/docker/views/images/build/buildImageController.js
index a332900d8..1e8e60f10 100644
--- a/app/docker/views/images/build/buildImageController.js
+++ b/app/docker/views/images/build/buildImageController.js
@@ -1,9 +1,10 @@
+import { confirmWebEditorDiscard } from '@@/modals/confirm';
import { options } from './options';
angular.module('portainer.docker').controller('BuildImageController', BuildImageController);
/* @ngInject */
-function BuildImageController($scope, $async, $window, ModalService, BuildService, Notifications, HttpRequestHelper, endpoint) {
+function BuildImageController($scope, $async, $window, BuildService, Notifications, HttpRequestHelper, endpoint) {
$scope.endpoint = endpoint;
$scope.options = options;
@@ -154,7 +155,7 @@ function BuildImageController($scope, $async, $window, ModalService, BuildServic
this.uiCanExit = async function () {
if ($scope.state.BuildType === 'editor' && $scope.formValues.DockerFileContent && $scope.state.isEditorDirty) {
- return ModalService.confirmWebEditorDiscard();
+ return confirmWebEditorDiscard();
}
};
diff --git a/app/docker/views/images/edit/imageController.js b/app/docker/views/images/edit/imageController.js
index c5910271d..af7f421e9 100644
--- a/app/docker/views/images/edit/imageController.js
+++ b/app/docker/views/images/edit/imageController.js
@@ -1,5 +1,6 @@
import _ from 'lodash-es';
import { PorImageRegistryModel } from 'Docker/models/porImageRegistry';
+import { confirmImageExport } from '@/react/docker/images/common/ConfirmExportModal';
angular.module('portainer.docker').controller('ImageController', [
'$async',
@@ -13,11 +14,9 @@ angular.module('portainer.docker').controller('ImageController', [
'RegistryService',
'Notifications',
'HttpRequestHelper',
- 'ModalService',
'FileSaver',
'Blob',
'endpoint',
- 'EndpointService',
'RegistryModalService',
function (
$async,
@@ -31,11 +30,9 @@ angular.module('portainer.docker').controller('ImageController', [
RegistryService,
Notifications,
HttpRequestHelper,
- ModalService,
FileSaver,
Blob,
endpoint,
- EndpointService,
RegistryModalService
) {
$scope.endpoint = endpoint;
@@ -90,6 +87,7 @@ angular.module('portainer.docker').controller('ImageController', [
return $async(async () => {
try {
const registryModel = await RegistryModalService.registryModal(repository, $scope.registries);
+
if (registryModel) {
$('#uploadResourceHint').show();
await ImageService.pushImage(registryModel);
@@ -171,7 +169,7 @@ angular.module('portainer.docker').controller('ImageController', [
return;
}
- ModalService.confirmImageExport(function (confirmed) {
+ confirmImageExport(function (confirmed) {
if (!confirmed) {
return;
}
diff --git a/app/docker/views/images/imagesController.js b/app/docker/views/images/imagesController.js
index e5fc58b67..5862059cb 100644
--- a/app/docker/views/images/imagesController.js
+++ b/app/docker/views/images/imagesController.js
@@ -1,5 +1,9 @@
import _ from 'lodash-es';
import { PorImageRegistryModel } from 'Docker/models/porImageRegistry';
+import { ModalType } from '@@/modals';
+import { confirmImageExport } from '@/react/docker/images/common/ConfirmExportModal';
+import { confirm } from '@@/modals/confirm';
+import { buildConfirmButton } from '@@/modals/utils';
angular.module('portainer.docker').controller('ImagesController', [
'$scope',
@@ -7,12 +11,11 @@ angular.module('portainer.docker').controller('ImagesController', [
'Authentication',
'ImageService',
'Notifications',
- 'ModalService',
'HttpRequestHelper',
'FileSaver',
'Blob',
'endpoint',
- function ($scope, $state, Authentication, ImageService, Notifications, ModalService, HttpRequestHelper, FileSaver, Blob, endpoint) {
+ function ($scope, $state, Authentication, ImageService, Notifications, HttpRequestHelper, FileSaver, Blob, endpoint) {
$scope.endpoint = endpoint;
$scope.isAdmin = Authentication.isAdmin();
@@ -52,7 +55,7 @@ angular.module('portainer.docker').controller('ImagesController', [
};
$scope.confirmRemovalAction = function (selectedItems, force) {
- ModalService.confirmImageForceRemoval(function (confirmed) {
+ confirmImageForceRemoval().then((confirmed) => {
if (!confirmed) {
return;
}
@@ -104,7 +107,7 @@ angular.module('portainer.docker').controller('ImagesController', [
return;
}
- ModalService.confirmImageExport(function (confirmed) {
+ confirmImageExport(function (confirmed) {
if (!confirmed) {
return;
}
@@ -158,3 +161,12 @@ angular.module('portainer.docker').controller('ImagesController', [
initView();
},
]);
+
+function confirmImageForceRemoval() {
+ return confirm({
+ title: 'Are you sure?',
+ modalType: ModalType.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.',
+ confirmButton: buildConfirmButton('Remote the image', 'danger'),
+ });
+}
diff --git a/app/docker/views/networks/networksController.js b/app/docker/views/networks/networksController.js
index 4830b9950..87d4b3797 100644
--- a/app/docker/views/networks/networksController.js
+++ b/app/docker/views/networks/networksController.js
@@ -1,6 +1,6 @@
import _ from 'lodash-es';
import DockerNetworkHelper from '@/docker/helpers/networkHelper';
-import { confirmDeletionAsync } from '@/portainer/services/modal.service/confirm';
+import { confirmDelete } from '@@/modals/confirm';
angular.module('portainer.docker').controller('NetworksController', [
'$q',
@@ -13,7 +13,7 @@ angular.module('portainer.docker').controller('NetworksController', [
'AgentService',
function ($q, $scope, $state, NetworkService, Notifications, HttpRequestHelper, endpoint, AgentService) {
$scope.removeAction = async function (selectedItems) {
- const confirmed = await confirmDeletionAsync('Do you want to remove the selected network(s)?');
+ const confirmed = await confirmDelete('Do you want to remove the selected network(s)?');
if (!confirmed) {
return null;
}
diff --git a/app/docker/views/secrets/secretsController.js b/app/docker/views/secrets/secretsController.js
index 2b14e0e1a..d59a69472 100644
--- a/app/docker/views/secrets/secretsController.js
+++ b/app/docker/views/secrets/secretsController.js
@@ -1,4 +1,4 @@
-import { confirmDeletionAsync } from 'Portainer/services/modal.service/confirm';
+import { confirmDelete } from '@@/modals/confirm';
angular.module('portainer.docker').controller('SecretsController', [
'$scope',
'$state',
@@ -6,7 +6,7 @@ angular.module('portainer.docker').controller('SecretsController', [
'Notifications',
function ($scope, $state, SecretService, Notifications) {
$scope.removeAction = async function (selectedItems) {
- const confirmed = await confirmDeletionAsync('Do you want to remove the selected secret(s)?');
+ const confirmed = await confirmDelete('Do you want to remove the selected secret(s)?');
if (!confirmed) {
return null;
}
diff --git a/app/docker/views/services/edit/serviceController.js b/app/docker/views/services/edit/serviceController.js
index 087771446..7ce4c7f2b 100644
--- a/app/docker/views/services/edit/serviceController.js
+++ b/app/docker/views/services/edit/serviceController.js
@@ -22,6 +22,10 @@ import _ from 'lodash-es';
import { PorImageRegistryModel } from 'Docker/models/porImageRegistry';
import * as envVarsUtils from '@/portainer/helpers/env-vars';
import { ResourceControlType } from '@/react/portainer/access-control/types';
+import { confirmServiceForceUpdate } from '@/react/docker/services/common/update-service-modal';
+import { confirm, confirmDelete } from '@@/modals/confirm';
+import { ModalType } from '@@/modals';
+import { buildConfirmButton } from '@@/modals/utils';
angular.module('portainer.docker').controller('ServiceController', [
'$q',
@@ -44,7 +48,6 @@ angular.module('portainer.docker').controller('ServiceController', [
'ContainerService',
'TaskHelper',
'Notifications',
- 'ModalService',
'PluginService',
'Authentication',
'VolumeService',
@@ -76,7 +79,6 @@ angular.module('portainer.docker').controller('ServiceController', [
ContainerService,
TaskHelper,
Notifications,
- ModalService,
PluginService,
Authentication,
VolumeService,
@@ -550,21 +552,16 @@ angular.module('portainer.docker').controller('ServiceController', [
}
$scope.rollbackService = function (service) {
- ModalService.confirmWarn({
+ confirm({
title: 'Rollback service',
message: 'Are you sure you want to rollback?',
- buttons: {
- confirm: {
- label: 'Yes',
- className: 'btn-danger',
- },
- },
- callback: function onConfirm(confirmed) {
- if (!confirmed) {
- return;
- }
- rollbackService(service);
- },
+ modalType: ModalType.Warn,
+ confirmButton: buildConfirmButton('Yes', 'danger'),
+ }).then((confirmed) => {
+ if (!confirmed) {
+ return;
+ }
+ rollbackService(service);
});
};
@@ -594,7 +591,7 @@ angular.module('portainer.docker').controller('ServiceController', [
};
$scope.removeService = function () {
- ModalService.confirmDeletion('Do you want to remove this service? All the containers associated to this service will be removed too.', function onConfirm(confirmed) {
+ confirmDelete('Do you want to remove this service? All the containers associated to this service will be removed too.').then((confirmed) => {
if (!confirmed) {
return;
}
@@ -621,15 +618,12 @@ angular.module('portainer.docker').controller('ServiceController', [
}
$scope.forceUpdateService = function (service) {
- ModalService.confirmServiceForceUpdate('Do you want to force an update of the service? All the tasks associated to the service will be recreated.', function (result) {
+ confirmServiceForceUpdate('Do you want to force an update of the service? All the tasks associated to the service will be recreated.').then(function (result) {
if (!result) {
return;
}
- var pullImage = false;
- if (result[0]) {
- pullImage = true;
- }
- forceUpdateService(service, pullImage);
+
+ forceUpdateService(service, result.pullLatest);
});
};
diff --git a/app/docker/views/volumes/edit/volumeController.js b/app/docker/views/volumes/edit/volumeController.js
index ac2850c23..8500feaca 100644
--- a/app/docker/views/volumes/edit/volumeController.js
+++ b/app/docker/views/volumes/edit/volumeController.js
@@ -1,16 +1,15 @@
import { ResourceControlType } from '@/react/portainer/access-control/types';
+import { confirmDelete } from '@@/modals/confirm';
angular.module('portainer.docker').controller('VolumeController', [
'$scope',
'$state',
'$transition$',
- '$q',
- 'ModalService',
'VolumeService',
'ContainerService',
'Notifications',
'HttpRequestHelper',
- function ($scope, $state, $transition$, $q, ModalService, VolumeService, ContainerService, Notifications, HttpRequestHelper) {
+ function ($scope, $state, $transition$, VolumeService, ContainerService, Notifications, HttpRequestHelper) {
$scope.resourceType = ResourceControlType.Volume;
$scope.onUpdateResourceControlSuccess = function () {
@@ -18,7 +17,7 @@ angular.module('portainer.docker').controller('VolumeController', [
};
$scope.removeVolume = function removeVolume() {
- ModalService.confirmDeletion('Do you want to remove this volume?', (confirmed) => {
+ confirmDelete('Do you want to remove this volume?').then((confirmed) => {
if (confirmed) {
VolumeService.remove($scope.volume)
.then(function success() {
diff --git a/app/docker/views/volumes/volumesController.js b/app/docker/views/volumes/volumesController.js
index 404aa47e4..0ca1352d6 100644
--- a/app/docker/views/volumes/volumesController.js
+++ b/app/docker/views/volumes/volumesController.js
@@ -1,3 +1,5 @@
+import { confirmDelete } from '@@/modals/confirm';
+
angular.module('portainer.docker').controller('VolumesController', [
'$q',
'$scope',
@@ -8,11 +10,10 @@ angular.module('portainer.docker').controller('VolumesController', [
'Notifications',
'HttpRequestHelper',
'Authentication',
- 'ModalService',
'endpoint',
- function ($q, $scope, $state, VolumeService, ServiceService, VolumeHelper, Notifications, HttpRequestHelper, Authentication, ModalService, endpoint) {
+ function ($q, $scope, $state, VolumeService, ServiceService, VolumeHelper, Notifications, HttpRequestHelper, Authentication, endpoint) {
$scope.removeAction = function (selectedItems) {
- ModalService.confirmDeletion('Do you want to remove the selected volume(s)?', (confirmed) => {
+ confirmDelete('Do you want to remove the selected volume(s)?').then((confirmed) => {
if (confirmed) {
var actionCount = selectedItems.length;
angular.forEach(selectedItems, function (volume) {
diff --git a/app/edge/components/group-form/groupFormController.js b/app/edge/components/group-form/groupFormController.js
index 78c1b602c..d7358e291 100644
--- a/app/edge/components/group-form/groupFormController.js
+++ b/app/edge/components/group-form/groupFormController.js
@@ -1,9 +1,10 @@
import _ from 'lodash-es';
-import { confirmDestructiveAsync } from '@/portainer/services/modal.service/confirm';
+import { confirmDestructive } from '@@/modals/confirm';
import { EdgeTypes } from '@/react/portainer/environments/types';
import { getEnvironments } from '@/react/portainer/environments/environment.service';
import { getTags } from '@/portainer/tags/tags.service';
import { notifyError } from '@/portainer/services/notifications';
+import { buildConfirmButton } from '@@/modals/utils';
import { groupTypeOptions } from './group-type-options';
import { tagOptions } from './tag-options';
@@ -77,19 +78,10 @@ export class EdgeGroupFormController {
dissociateEndpoint(endpoint) {
return this.$async(async () => {
- const confirmed = await confirmDestructiveAsync({
+ const confirmed = await confirmDestructive({
title: 'Confirm action',
message: 'Removing the environment from this group will remove its corresponding edge stacks',
- buttons: {
- cancel: {
- label: 'Cancel',
- className: 'btn-default',
- },
- confirm: {
- label: 'Confirm',
- className: 'btn-primary',
- },
- },
+ confirmButton: buildConfirmButton('Confirm'),
});
if (!confirmed) {
diff --git a/app/edge/views/edge-jobs/createEdgeJobView/createEdgeJobViewController.js b/app/edge/views/edge-jobs/createEdgeJobView/createEdgeJobViewController.js
index 134f6f110..97baf8ea8 100644
--- a/app/edge/views/edge-jobs/createEdgeJobView/createEdgeJobViewController.js
+++ b/app/edge/views/edge-jobs/createEdgeJobView/createEdgeJobViewController.js
@@ -1,6 +1,8 @@
+import { confirmWebEditorDiscard } from '@@/modals/confirm';
+
export class CreateEdgeJobViewController {
/* @ngInject */
- constructor($async, $q, $state, $window, ModalService, EdgeJobService, GroupService, Notifications, TagService) {
+ constructor($async, $q, $state, $window, EdgeJobService, GroupService, Notifications, TagService) {
this.state = {
actionInProgress: false,
isEditorDirty: false,
@@ -20,7 +22,6 @@ export class CreateEdgeJobViewController {
this.$q = $q;
this.$state = $state;
this.$window = $window;
- this.ModalService = ModalService;
this.Notifications = Notifications;
this.GroupService = GroupService;
this.EdgeJobService = EdgeJobService;
@@ -59,7 +60,7 @@ export class CreateEdgeJobViewController {
async uiCanExit() {
if (this.model.FileContent && this.state.isEditorDirty) {
- return this.ModalService.confirmWebEditorDiscard();
+ return confirmWebEditorDiscard();
}
}
diff --git a/app/edge/views/edge-jobs/edgeJob/edgeJobController.js b/app/edge/views/edge-jobs/edgeJob/edgeJobController.js
index b605b7c70..a65fa1275 100644
--- a/app/edge/views/edge-jobs/edgeJob/edgeJobController.js
+++ b/app/edge/views/edge-jobs/edgeJob/edgeJobController.js
@@ -1,9 +1,10 @@
import _ from 'lodash-es';
import { getEnvironments } from '@/react/portainer/environments/environment.service';
+import { confirmWebEditorDiscard } from '@@/modals/confirm';
export class EdgeJobController {
/* @ngInject */
- constructor($async, $q, $state, $window, ModalService, EdgeJobService, FileSaver, GroupService, HostBrowserService, Notifications, TagService) {
+ constructor($async, $q, $state, $window, EdgeJobService, FileSaver, GroupService, HostBrowserService, Notifications, TagService) {
this.state = {
actionInProgress: false,
showEditorTab: false,
@@ -14,7 +15,6 @@ export class EdgeJobController {
this.$q = $q;
this.$state = $state;
this.$window = $window;
- this.ModalService = ModalService;
this.EdgeJobService = EdgeJobService;
this.FileSaver = FileSaver;
this.GroupService = GroupService;
@@ -127,7 +127,7 @@ export class EdgeJobController {
async uiCanExit() {
if (this.edgeJob && this.edgeJob.FileContent !== this.oldFileContent && this.state.isEditorDirty) {
- return this.ModalService.confirmWebEditorDiscard();
+ return confirmWebEditorDiscard();
}
}
diff --git a/app/edge/views/edge-jobs/edgeJobsView/edgeJobsViewController.js b/app/edge/views/edge-jobs/edgeJobsView/edgeJobsViewController.js
index bda1d3a4a..d61c2af99 100644
--- a/app/edge/views/edge-jobs/edgeJobsView/edgeJobsViewController.js
+++ b/app/edge/views/edge-jobs/edgeJobsView/edgeJobsViewController.js
@@ -1,12 +1,12 @@
import _ from 'lodash-es';
+import { confirmDelete } from '@@/modals/confirm';
export class EdgeJobsViewController {
/* @ngInject */
- constructor($async, $state, EdgeJobService, ModalService, Notifications) {
+ constructor($async, $state, EdgeJobService, Notifications) {
this.$async = $async;
this.$state = $state;
this.EdgeJobService = EdgeJobService;
- this.ModalService = ModalService;
this.Notifications = Notifications;
this.removeAction = this.removeAction.bind(this);
@@ -15,7 +15,7 @@ export class EdgeJobsViewController {
}
removeAction(selectedItems) {
- this.ModalService.confirmDeletion('Do you want to remove the selected edge job(s) ?', (confirmed) => {
+ confirmDelete('Do you want to remove the selected edge job(s)?').then((confirmed) => {
if (!confirmed) {
return;
}
diff --git a/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.controller.js b/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.controller.js
index e47d0d290..2229e3cf4 100644
--- a/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.controller.js
+++ b/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.controller.js
@@ -1,11 +1,12 @@
import { EditorType } from '@/react/edge/edge-stacks/types';
import { PortainerEndpointTypes } from '@/portainer/models/endpoint/models';
import { getValidEditorTypes } from '@/react/edge/edge-stacks/utils';
+import { confirmWebEditorDiscard } from '@@/modals/confirm';
export default class CreateEdgeStackViewController {
/* @ngInject */
- constructor($state, $window, ModalService, EdgeStackService, EdgeGroupService, EdgeTemplateService, Notifications, FormHelper, $async, $scope) {
- Object.assign(this, { $state, $window, ModalService, EdgeStackService, EdgeGroupService, EdgeTemplateService, Notifications, FormHelper, $async, $scope });
+ constructor($state, $window, EdgeStackService, EdgeGroupService, EdgeTemplateService, Notifications, FormHelper, $async, $scope) {
+ Object.assign(this, { $state, $window, EdgeStackService, EdgeGroupService, EdgeTemplateService, Notifications, FormHelper, $async, $scope });
this.formValues = {
Name: '',
@@ -75,7 +76,7 @@ export default class CreateEdgeStackViewController {
uiCanExit() {
if (this.state.Method === 'editor' && this.formValues.StackFileContent && this.state.isEditorDirty) {
- return this.ModalService.confirmWebEditorDiscard();
+ return confirmWebEditorDiscard();
}
}
diff --git a/app/edge/views/edge-stacks/editEdgeStackView/editEdgeStackViewController.js b/app/edge/views/edge-stacks/editEdgeStackView/editEdgeStackViewController.js
index 36191a8e7..dc79edab8 100644
--- a/app/edge/views/edge-stacks/editEdgeStackView/editEdgeStackViewController.js
+++ b/app/edge/views/edge-stacks/editEdgeStackView/editEdgeStackViewController.js
@@ -1,13 +1,13 @@
import _ from 'lodash-es';
import { getEnvironments } from '@/react/portainer/environments/environment.service';
+import { confirmWebEditorDiscard } from '@@/modals/confirm';
export class EditEdgeStackViewController {
/* @ngInject */
- constructor($async, $state, $window, ModalService, EdgeGroupService, EdgeStackService, Notifications) {
+ constructor($async, $state, $window, EdgeGroupService, EdgeStackService, Notifications) {
this.$async = $async;
this.$state = $state;
this.$window = $window;
- this.ModalService = ModalService;
this.EdgeGroupService = EdgeGroupService;
this.EdgeStackService = EdgeStackService;
this.Notifications = Notifications;
@@ -64,7 +64,7 @@ export class EditEdgeStackViewController {
this.formValues.StackFileContent.replace(/(\r\n|\n|\r)/gm, '') !== this.oldFileContent.replace(/(\r\n|\n|\r)/gm, '') &&
this.state.isEditorDirty
) {
- return this.ModalService.confirmWebEditorDiscard();
+ return confirmWebEditorDiscard();
}
}
diff --git a/app/kubernetes/components/helm/helm-templates/helm-templates.controller.js b/app/kubernetes/components/helm/helm-templates/helm-templates.controller.js
index d14f6ad3b..957dcc911 100644
--- a/app/kubernetes/components/helm/helm-templates/helm-templates.controller.js
+++ b/app/kubernetes/components/helm/helm-templates/helm-templates.controller.js
@@ -1,9 +1,10 @@
import _ from 'lodash-es';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
+import { confirmWebEditorDiscard } from '@@/modals/confirm';
export default class HelmTemplatesController {
/* @ngInject */
- constructor($analytics, $async, $state, $window, $anchorScroll, Authentication, HelmService, KubernetesResourcePoolService, Notifications, ModalService) {
+ constructor($analytics, $async, $state, $window, $anchorScroll, Authentication, HelmService, KubernetesResourcePoolService, Notifications) {
this.$analytics = $analytics;
this.$async = $async;
this.$window = $window;
@@ -13,7 +14,6 @@ export default class HelmTemplatesController {
this.HelmService = HelmService;
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.Notifications = Notifications;
- this.ModalService = ModalService;
this.editorUpdate = this.editorUpdate.bind(this);
this.uiCanExit = this.uiCanExit.bind(this);
@@ -42,7 +42,7 @@ export default class HelmTemplatesController {
async uiCanExit() {
if (this.state.isEditorDirty) {
- return this.ModalService.confirmWebEditorDiscard();
+ return confirmWebEditorDiscard();
}
}
diff --git a/app/kubernetes/custom-templates/kube-create-custom-template-view/kube-create-custom-template-view.controller.js b/app/kubernetes/custom-templates/kube-create-custom-template-view/kube-create-custom-template-view.controller.js
index 0fa7e7440..cdda36e32 100644
--- a/app/kubernetes/custom-templates/kube-create-custom-template-view/kube-create-custom-template-view.controller.js
+++ b/app/kubernetes/custom-templates/kube-create-custom-template-view/kube-create-custom-template-view.controller.js
@@ -2,11 +2,12 @@ import { AccessControlFormData } from '@/portainer/components/accessControlForm/
import { getTemplateVariables, intersectVariables } from '@/react/portainer/custom-templates/components/utils';
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
import { editor, upload } from '@@/BoxSelector/common-options/build-methods';
+import { confirmWebEditorDiscard } from '@@/modals/confirm';
class KubeCreateCustomTemplateViewController {
/* @ngInject */
- constructor($async, $state, Authentication, CustomTemplateService, FormValidator, ModalService, Notifications, ResourceControlService) {
- Object.assign(this, { $async, $state, Authentication, CustomTemplateService, FormValidator, ModalService, Notifications, ResourceControlService });
+ constructor($async, $state, Authentication, CustomTemplateService, FormValidator, Notifications, ResourceControlService) {
+ Object.assign(this, { $async, $state, Authentication, CustomTemplateService, FormValidator, Notifications, ResourceControlService });
this.methodOptions = [editor, upload];
@@ -202,7 +203,7 @@ class KubeCreateCustomTemplateViewController {
uiCanExit() {
if (this.isEditorDirty()) {
- return this.ModalService.confirmWebEditorDiscard();
+ return confirmWebEditorDiscard();
}
}
}
diff --git a/app/kubernetes/custom-templates/kube-custom-templates-view/kube-custom-templates-view.controller.js b/app/kubernetes/custom-templates/kube-custom-templates-view/kube-custom-templates-view.controller.js
index 669f7c0d9..74d345b4f 100644
--- a/app/kubernetes/custom-templates/kube-custom-templates-view/kube-custom-templates-view.controller.js
+++ b/app/kubernetes/custom-templates/kube-custom-templates-view/kube-custom-templates-view.controller.js
@@ -1,9 +1,10 @@
import _ from 'lodash-es';
+import { confirmDelete } from '@@/modals/confirm';
export default class KubeCustomTemplatesViewController {
/* @ngInject */
- constructor($async, $state, Authentication, CustomTemplateService, FormValidator, ModalService, Notifications) {
- Object.assign(this, { $async, $state, Authentication, CustomTemplateService, FormValidator, ModalService, Notifications });
+ constructor($async, $state, Authentication, CustomTemplateService, FormValidator, Notifications) {
+ Object.assign(this, { $async, $state, Authentication, CustomTemplateService, FormValidator, Notifications });
this.state = {
selectedTemplate: null,
@@ -55,7 +56,7 @@ export default class KubeCustomTemplatesViewController {
confirmDelete(templateId) {
return this.$async(async () => {
- 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;
}
diff --git a/app/kubernetes/custom-templates/kube-edit-custom-template-view/kube-edit-custom-template-view.controller.js b/app/kubernetes/custom-templates/kube-edit-custom-template-view/kube-edit-custom-template-view.controller.js
index 2b8b3aed9..c58c08081 100644
--- a/app/kubernetes/custom-templates/kube-edit-custom-template-view/kube-edit-custom-template-view.controller.js
+++ b/app/kubernetes/custom-templates/kube-edit-custom-template-view/kube-edit-custom-template-view.controller.js
@@ -2,11 +2,12 @@ import { ResourceControlViewModel } from '@/react/portainer/access-control/model
import { AccessControlFormData } from '@/portainer/components/accessControlForm/porAccessControlFormModel';
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
import { getTemplateVariables, intersectVariables } from '@/react/portainer/custom-templates/components/utils';
+import { confirmWebEditorDiscard } from '@@/modals/confirm';
class KubeEditCustomTemplateViewController {
/* @ngInject */
- constructor($async, $state, ModalService, Authentication, CustomTemplateService, FormValidator, Notifications, ResourceControlService) {
- Object.assign(this, { $async, $state, ModalService, Authentication, CustomTemplateService, FormValidator, Notifications, ResourceControlService });
+ constructor($async, $state, Authentication, CustomTemplateService, FormValidator, Notifications, ResourceControlService) {
+ Object.assign(this, { $async, $state, Authentication, CustomTemplateService, FormValidator, Notifications, ResourceControlService });
this.isTemplateVariablesEnabled = isBE;
@@ -160,7 +161,7 @@ class KubeEditCustomTemplateViewController {
uiCanExit() {
if (this.isEditorDirty()) {
- return this.ModalService.confirmWebEditorDiscard();
+ return confirmWebEditorDiscard();
}
}
diff --git a/app/kubernetes/registries/kube-registry-access-view/kube-registry-access-view.controller.js b/app/kubernetes/registries/kube-registry-access-view/kube-registry-access-view.controller.js
index 0c85126e3..11f7cd889 100644
--- a/app/kubernetes/registries/kube-registry-access-view/kube-registry-access-view.controller.js
+++ b/app/kubernetes/registries/kube-registry-access-view/kube-registry-access-view.controller.js
@@ -1,12 +1,12 @@
+import { confirmDelete } from '@@/modals/confirm';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
export default class KubernetesRegistryAccessController {
/* @ngInject */
- constructor($async, $scope, $state, ModalService, EndpointService, Notifications, RegistryService, KubernetesResourcePoolService) {
+ constructor($async, $scope, $state, EndpointService, Notifications, RegistryService, KubernetesResourcePoolService) {
this.$async = $async;
this.$scope = $scope;
this.$state = $state;
- this.ModalService = ModalService;
this.Notifications = Notifications;
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.RegistryService = RegistryService;
@@ -34,7 +34,7 @@ export default class KubernetesRegistryAccessController {
const displayedMessage =
'This registry might be used by one or more applications inside this environment. Removing the registry access could lead to a service interruption for these applications.
Do you wish to continue?';
- this.ModalService.confirmDeletion(displayedMessage, (confirmed) => {
+ confirmDelete(displayedMessage).then((confirmed) => {
if (confirmed) {
return this.updateNamespaces(nsToUpdate);
}
diff --git a/app/kubernetes/views/applications/applicationsController.js b/app/kubernetes/views/applications/applicationsController.js
index ed4b7299b..a339ee2fd 100644
--- a/app/kubernetes/views/applications/applicationsController.js
+++ b/app/kubernetes/views/applications/applicationsController.js
@@ -5,9 +5,11 @@ import KubernetesApplicationHelper from 'Kubernetes/helpers/application';
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
import { KubernetesApplicationTypes } from 'Kubernetes/models/application/models';
import { KubernetesPortainerApplicationStackNameLabel } from 'Kubernetes/models/application/models';
+import { confirmDelete } from '@@/modals/confirm';
+
class KubernetesApplicationsController {
/* @ngInject */
- constructor($async, $state, Notifications, KubernetesApplicationService, HelmService, KubernetesConfigurationService, Authentication, ModalService, LocalStorage, StackService) {
+ constructor($async, $state, Notifications, KubernetesApplicationService, HelmService, KubernetesConfigurationService, Authentication, LocalStorage, StackService) {
this.$async = $async;
this.$state = $state;
this.Notifications = Notifications;
@@ -15,7 +17,6 @@ class KubernetesApplicationsController {
this.HelmService = HelmService;
this.KubernetesConfigurationService = KubernetesConfigurationService;
this.Authentication = Authentication;
- this.ModalService = ModalService;
this.LocalStorage = LocalStorage;
this.StackService = StackService;
@@ -63,14 +64,11 @@ class KubernetesApplicationsController {
}
removeStacksAction(selectedItems) {
- this.ModalService.confirmDeletion(
- 'Are you sure that you want to remove the selected stack(s) ? This will remove all the applications associated to the stack(s).',
- (confirmed) => {
- if (confirmed) {
- return this.$async(this.removeStacksActionAsync, selectedItems);
- }
+ confirmDelete('Are you sure that you want to remove the selected stack(s) ? This will remove all the applications associated to the stack(s).').then((confirmed) => {
+ if (confirmed) {
+ return this.$async(this.removeStacksActionAsync, selectedItems);
}
- );
+ });
}
async removeActionAsync(selectedItems) {
@@ -109,7 +107,7 @@ class KubernetesApplicationsController {
}
removeAction(selectedItems) {
- this.ModalService.confirmDeletion('Do you want to remove the selected application(s)?', (confirmed) => {
+ confirmDelete('Do you want to remove the selected application(s)?').then((confirmed) => {
if (confirmed) {
return this.$async(this.removeActionAsync, selectedItems);
}
diff --git a/app/kubernetes/views/applications/create/createApplicationController.js b/app/kubernetes/views/applications/create/createApplicationController.js
index db6a7e531..b3cb053f0 100644
--- a/app/kubernetes/views/applications/create/createApplicationController.js
+++ b/app/kubernetes/views/applications/create/createApplicationController.js
@@ -33,7 +33,10 @@ import KubernetesVolumeHelper from 'Kubernetes/helpers/volumeHelper';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
import { KubernetesNodeHelper } from 'Kubernetes/node/helper';
import { updateIngress, getIngresses } from '@/react/kubernetes/ingresses/service';
-import { confirmUpdateAppIngress } from '@/portainer/services/modal.service/prompt';
+import { confirmUpdateAppIngress } from '@/react/kubernetes/applications/CreateView/UpdateIngressPrompt';
+import { confirm, confirmUpdate, confirmWebEditorDiscard } from '@@/modals/confirm';
+import { buildConfirmButton } from '@@/modals/utils';
+import { ModalType } from '@@/modals';
import { placementOptions } from './placementTypes';
class KubernetesCreateApplicationController {
@@ -46,7 +49,6 @@ class KubernetesCreateApplicationController {
$state,
Notifications,
Authentication,
- ModalService,
KubernetesResourcePoolService,
KubernetesApplicationService,
KubernetesStackService,
@@ -64,7 +66,6 @@ class KubernetesCreateApplicationController {
this.$state = $state;
this.Notifications = Notifications;
this.Authentication = Authentication;
- this.ModalService = ModalService;
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.KubernetesApplicationService = KubernetesApplicationService;
this.KubernetesStackService = KubernetesStackService;
@@ -187,19 +188,16 @@ class KubernetesCreateApplicationController {
async updateApplicationViaWebEditor() {
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 overriden and may cause a service interruption. Do you wish to continue?',
- buttons: {
- confirm: {
- label: 'Update',
- className: 'btn-warning',
- },
- },
+ message: 'Any changes to this application will be overridden and may cause a service interruption. Do you wish to continue?',
+ confirmButton: buildConfirmButton('Update', 'warning'),
+ modalType: ModalType.Warn,
});
if (!confirmed) {
return;
}
+
this.state.updateWebEditorInProgress = true;
await this.StackService.updateKubeStack({ EndpointId: this.endpoint.Id, Id: this.application.StackId }, this.stackFileContent, null);
this.state.isEditorDirty = false;
@@ -214,7 +212,7 @@ class KubernetesCreateApplicationController {
async uiCanExit() {
if (this.stackFileContent && this.state.isEditorDirty) {
- return this.ModalService.confirmWebEditorDiscard();
+ return confirmWebEditorDiscard();
}
}
@@ -1055,11 +1053,11 @@ class KubernetesCreateApplicationController {
}
}
- async updateApplicationAsync(ingressesToUpdate, rulePlural) {
+ async updateApplicationAsync(ingressesToUpdate) {
if (ingressesToUpdate.length) {
try {
await Promise.all(ingressesToUpdate.map((ing) => updateIngress(this.endpoint.Id, ing)));
- this.Notifications.success('Success', `Ingress ${rulePlural} successfully updated`);
+ this.Notifications.success('Success', `Ingress ${ingressesToUpdate.length > 1 ? 'rules' : 'rule'} successfully updated`);
} catch (error) {
this.Notifications.error('Failure', error, 'Unable to update ingress');
}
@@ -1081,33 +1079,22 @@ class KubernetesCreateApplicationController {
const [ingressesToUpdate, servicePortsToUpdate] = await this.checkIngressesToUpdate();
// if there is an ingressesToUpdate, then show a warning modal with asking if they want to update the ingresses
if (ingressesToUpdate.length) {
- const rulePlural = ingressesToUpdate.length > 1 ? 'rules' : 'rule';
- const noMatchSentence =
- servicePortsToUpdate.length > 1
- ? `Service ports in this application no longer match the ingress ${rulePlural}.`
- : `A service port in this application no longer matches the ingress ${rulePlural} which may break ingress rule paths.`;
- const message = `
-
- - Updating the application may cause a service interruption.
- - ${noMatchSentence}
-
- `;
- const inputLabel = `Update ingress ${rulePlural} to match the service port changes`;
- confirmUpdateAppIngress(`Are you sure?`, message, inputLabel, (value) => {
- if (value === null) {
- return;
- }
- if (value.length === 0) {
- return this.$async(this.updateApplicationAsync, [], '');
- }
- if (value[0] === '1') {
- return this.$async(this.updateApplicationAsync, ingressesToUpdate, rulePlural);
- }
- });
+ const result = await confirmUpdateAppIngress(ingressesToUpdate, servicePortsToUpdate);
+ if (!result) {
+ return;
+ }
+
+ const { noMatch } = result;
+ if (!noMatch) {
+ return this.$async(this.updateApplicationAsync, []);
+ }
+ if (noMatch) {
+ return this.$async(this.updateApplicationAsync, ingressesToUpdate);
+ }
} else {
- this.ModalService.confirmUpdate('Updating the application may cause a service interruption. Do you wish to continue?', (confirmed) => {
+ confirmUpdate('Updating the application may cause a service interruption. Do you wish to continue?', (confirmed) => {
if (confirmed) {
- return this.$async(this.updateApplicationAsync, [], '');
+ return this.$async(this.updateApplicationAsync, []);
}
});
}
diff --git a/app/kubernetes/views/applications/edit/applicationController.js b/app/kubernetes/views/applications/edit/applicationController.js
index 5ec55e766..9d75296be 100644
--- a/app/kubernetes/views/applications/edit/applicationController.js
+++ b/app/kubernetes/views/applications/edit/applicationController.js
@@ -15,6 +15,9 @@ import { KubernetesServiceTypes } from 'Kubernetes/models/service/models';
import { KubernetesPodNodeAffinityNodeSelectorRequirementOperators } from 'Kubernetes/pod/models';
import { KubernetesPodContainerTypes } from 'Kubernetes/pod/models/index';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
+import { confirmUpdate, confirm } from '@@/modals/confirm';
+import { buildConfirmButton } from '@@/modals/utils';
+import { ModalType } from '@@/modals';
function computeTolerations(nodes, application) {
const pod = application.Pods[0];
@@ -108,7 +111,6 @@ class KubernetesApplicationController {
clipboard,
Notifications,
LocalStorage,
- ModalService,
KubernetesResourcePoolService,
KubernetesApplicationService,
KubernetesEventService,
@@ -122,7 +124,6 @@ class KubernetesApplicationController {
this.clipboard = clipboard;
this.Notifications = Notifications;
this.LocalStorage = LocalStorage;
- this.ModalService = ModalService;
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.StackService = StackService;
@@ -224,7 +225,7 @@ class KubernetesApplicationController {
}
rollbackApplication() {
- this.ModalService.confirmUpdate('Rolling back the application to a previous configuration may cause service interruption. Do you wish to continue?', (confirmed) => {
+ confirmUpdate('Rolling back the application to a previous configuration may cause service interruption. Do you wish to continue?', (confirmed) => {
if (confirmed) {
return this.$async(this.rollbackApplicationAsync);
}
@@ -234,10 +235,11 @@ class KubernetesApplicationController {
* REDEPLOY
*/
async redeployApplicationAsync() {
- const confirmed = await this.ModalService.confirmAsync({
+ const confirmed = await confirm({
+ modalType: ModalType.Warn,
title: 'Are you sure?',
message: 'Redeploying the application may cause a service interruption. Do you wish to continue?',
- buttons: { confirm: { label: 'Redeploy', className: 'btn-primary' } },
+ confirmButton: buildConfirmButton('Redeploy'),
});
if (!confirmed) {
return;
diff --git a/app/kubernetes/views/cluster/node/nodeController.js b/app/kubernetes/views/cluster/node/nodeController.js
index c0edb64a8..bfdeaddbc 100644
--- a/app/kubernetes/views/cluster/node/nodeController.js
+++ b/app/kubernetes/views/cluster/node/nodeController.js
@@ -8,6 +8,7 @@ import { KubernetesNodeLabelFormValues, KubernetesNodeTaintFormValues } from 'Ku
import { KubernetesNodeTaintEffects, KubernetesNodeAvailabilities } from 'Kubernetes/node/models';
import KubernetesFormValidationHelper from 'Kubernetes/helpers/formValidationHelper';
import { KubernetesNodeHelper } from 'Kubernetes/node/helper';
+import { confirmUpdate } from '@@/modals/confirm';
class KubernetesNodeController {
/* @ngInject */
@@ -16,7 +17,6 @@ class KubernetesNodeController {
$state,
Notifications,
LocalStorage,
- ModalService,
KubernetesNodeService,
KubernetesEventService,
KubernetesPodService,
@@ -29,7 +29,6 @@ class KubernetesNodeController {
this.$state = $state;
this.Notifications = Notifications;
this.LocalStorage = LocalStorage;
- this.ModalService = ModalService;
this.KubernetesNodeService = KubernetesNodeService;
this.KubernetesEventService = KubernetesEventService;
this.KubernetesPodService = KubernetesPodService;
@@ -266,7 +265,7 @@ class KubernetesNodeController {
const drainWarning = this.computeDrainWarning();
if (taintsWarning && !labelsWarning) {
- this.ModalService.confirmUpdate(
+ confirmUpdate(
'Changes to taints will immediately deschedule applications running on this node without the corresponding tolerations. Do you wish to continue?',
(confirmed) => {
if (confirmed) {
@@ -275,7 +274,7 @@ class KubernetesNodeController {
}
);
} else if (!taintsWarning && labelsWarning) {
- this.ModalService.confirmUpdate(
+ confirmUpdate(
'Removing or changing a label that is used might prevent applications from being scheduled on this node in the future. Do you wish to continue?',
(confirmed) => {
if (confirmed) {
@@ -284,7 +283,7 @@ class KubernetesNodeController {
}
);
} else if (taintsWarning && labelsWarning) {
- this.ModalService.confirmUpdate(
+ confirmUpdate(
'Changes to taints will immediately deschedule applications running on this node without the corresponding tolerations.
Removing or changing a label that is used might prevent applications from scheduling on this node in the future.\n\nDo you wish to continue?',
(confirmed) => {
if (confirmed) {
@@ -293,7 +292,7 @@ class KubernetesNodeController {
}
);
} else if (cordonWarning) {
- this.ModalService.confirmUpdate(
+ confirmUpdate(
'Marking this node as unschedulable will effectively cordon the node and prevent any new workload from being scheduled on that node. Are you sure?',
(confirmed) => {
if (confirmed) {
@@ -302,14 +301,11 @@ class KubernetesNodeController {
}
);
} else if (drainWarning) {
- this.ModalService.confirmUpdate(
- 'Draining this node will cause all workloads to be evicted from that node. This might lead to some service interruption. Are you sure?',
- (confirmed) => {
- if (confirmed) {
- return this.$async(this.updateNodeAsync);
- }
+ confirmUpdate('Draining this node will cause all workloads to be evicted from that node. This might lead to some service interruption. Are you sure?', (confirmed) => {
+ if (confirmed) {
+ return this.$async(this.updateNodeAsync);
}
- );
+ });
} else {
return this.$async(this.updateNodeAsync);
}
diff --git a/app/kubernetes/views/configurations/configurationsController.js b/app/kubernetes/views/configurations/configurationsController.js
index 2fa24c9dc..c35846fd0 100644
--- a/app/kubernetes/views/configurations/configurationsController.js
+++ b/app/kubernetes/views/configurations/configurationsController.js
@@ -1,16 +1,16 @@
import angular from 'angular';
+import { confirmDelete } from '@@/modals/confirm';
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
class KubernetesConfigurationsController {
/* @ngInject */
- constructor($async, $state, Notifications, Authentication, KubernetesConfigurationService, KubernetesApplicationService, ModalService) {
+ constructor($async, $state, Notifications, Authentication, KubernetesConfigurationService, KubernetesApplicationService) {
this.$async = $async;
this.$state = $state;
this.Notifications = Notifications;
this.Authentication = Authentication;
this.KubernetesConfigurationService = KubernetesConfigurationService;
this.KubernetesApplicationService = KubernetesApplicationService;
- this.ModalService = ModalService;
this.onInit = this.onInit.bind(this);
this.getConfigurations = this.getConfigurations.bind(this);
@@ -58,7 +58,7 @@ class KubernetesConfigurationsController {
}
removeAction(selectedItems) {
- this.ModalService.confirmDeletion('Do you want to remove the selected configuration(s)?', (confirmed) => {
+ confirmDelete('Do you want to remove the selected configuration(s)?').then((confirmed) => {
if (confirmed) {
return this.$async(this.removeActionAsync, selectedItems);
}
diff --git a/app/kubernetes/views/configurations/create/createConfigurationController.js b/app/kubernetes/views/configurations/create/createConfigurationController.js
index 3844ebe48..8c58a348d 100644
--- a/app/kubernetes/views/configurations/create/createConfigurationController.js
+++ b/app/kubernetes/views/configurations/create/createConfigurationController.js
@@ -6,18 +6,18 @@ import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelpe
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
import { getServiceAccounts } from 'Kubernetes/rest/serviceAccount';
+import { confirmWebEditorDiscard } from '@@/modals/confirm';
import { isConfigurationFormValid } from '../validation';
import { typeOptions } from './options';
class KubernetesCreateConfigurationController {
/* @ngInject */
- constructor($async, $state, $scope, $window, ModalService, Notifications, Authentication, KubernetesConfigurationService, KubernetesResourcePoolService, EndpointProvider) {
+ constructor($async, $state, $scope, $window, Notifications, Authentication, KubernetesConfigurationService, KubernetesResourcePoolService, EndpointProvider) {
this.$async = $async;
this.$state = $state;
this.$scope = $scope;
this.$window = $window;
this.EndpointProvider = EndpointProvider;
- this.ModalService = ModalService;
this.Notifications = Notifications;
this.Authentication = Authentication;
this.KubernetesConfigurationService = KubernetesConfigurationService;
@@ -176,7 +176,7 @@ class KubernetesCreateConfigurationController {
async uiCanExit() {
if (!this.formValues.IsSimple && this.formValues.DataYaml && this.state.isEditorDirty) {
- return this.ModalService.confirmWebEditorDiscard();
+ return confirmWebEditorDiscard();
}
}
diff --git a/app/kubernetes/views/configurations/edit/configurationController.js b/app/kubernetes/views/configurations/edit/configurationController.js
index 902b757ec..ada9bdcff 100644
--- a/app/kubernetes/views/configurations/edit/configurationController.js
+++ b/app/kubernetes/views/configurations/edit/configurationController.js
@@ -8,6 +8,7 @@ import KubernetesConfigurationConverter from 'Kubernetes/converters/configuratio
import KubernetesEventHelper from 'Kubernetes/helpers/eventHelper';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
+import { confirmUpdate, confirmWebEditorDiscard } from '@@/modals/confirm';
import { isConfigurationFormValid } from '../validation';
class KubernetesConfigurationController {
@@ -23,7 +24,6 @@ class KubernetesConfigurationController {
KubernetesConfigMapService,
KubernetesSecretService,
KubernetesResourcePoolService,
- ModalService,
KubernetesApplicationService,
KubernetesEventService
) {
@@ -33,7 +33,6 @@ class KubernetesConfigurationController {
this.clipboard = clipboard;
this.Notifications = Notifications;
this.LocalStorage = LocalStorage;
- this.ModalService = ModalService;
this.KubernetesConfigurationService = KubernetesConfigurationService;
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.KubernetesApplicationService = KubernetesApplicationService;
@@ -121,7 +120,7 @@ class KubernetesConfigurationController {
updateConfiguration() {
if (this.configuration.Used) {
const plural = this.configuration.Applications.length > 1 ? 's' : '';
- this.ModalService.confirmUpdate(
+ confirmUpdate(
`The changes will be propagated to ${this.configuration.Applications.length} running application${plural}. Are you sure you want to update this configuration?`,
(confirmed) => {
if (confirmed) {
@@ -240,7 +239,7 @@ class KubernetesConfigurationController {
async uiCanExit() {
if (!this.formValues.IsSimple && this.formValues.DataYaml !== this.oldDataYaml && this.state.isEditorDirty) {
- return this.ModalService.confirmWebEditorDiscard();
+ return confirmWebEditorDiscard();
}
}
diff --git a/app/kubernetes/views/configure/configureController.js b/app/kubernetes/views/configure/configureController.js
index d49a3a88e..4f2045e83 100644
--- a/app/kubernetes/views/configure/configureController.js
+++ b/app/kubernetes/views/configure/configureController.js
@@ -8,6 +8,8 @@ import { FeatureId } from '@/react/portainer/feature-flags/enums';
import { getIngressControllerClassMap, updateIngressControllerClassMap } from '@/react/kubernetes/cluster/ingressClass/utils';
import { getIsRBACEnabled } from '@/react/kubernetes/cluster/service';
+import { buildConfirmButton } from '@@/modals/utils';
+import { confirm } from '@@/modals/confirm';
class KubernetesConfigureController {
/* #region CONSTRUCTOR */
@@ -21,7 +23,6 @@ class KubernetesConfigureController {
KubernetesStorageService,
EndpointService,
EndpointProvider,
- ModalService,
KubernetesResourcePoolService,
KubernetesIngressService,
KubernetesMetricsService
@@ -33,7 +34,6 @@ class KubernetesConfigureController {
this.KubernetesStorageService = KubernetesStorageService;
this.EndpointService = EndpointService;
this.EndpointProvider = EndpointProvider;
- this.ModalService = ModalService;
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.KubernetesIngressService = KubernetesIngressService;
this.KubernetesMetricsService = KubernetesMetricsService;
@@ -386,15 +386,10 @@ class KubernetesConfigureController {
uiCanExit() {
if (!this.state.isSaving && (this.areControllersChanged() || this.areFormValuesChanged() || this.areStorageClassesChanged()) && !this.isIngressControllersLoading) {
- return this.ModalService.confirmAsync({
+ return confirm({
title: 'Are you sure?',
message: 'You currently have unsaved changes in the cluster setup view. Are you sure you want to leave?',
- buttons: {
- confirm: {
- label: 'Yes',
- className: 'btn-danger',
- },
- },
+ confirmButton: buildConfirmButton('Yes', 'danger'),
});
}
}
diff --git a/app/kubernetes/views/deploy/deployController.js b/app/kubernetes/views/deploy/deployController.js
index 6ef0d13a1..8e6c8020c 100644
--- a/app/kubernetes/views/deploy/deployController.js
+++ b/app/kubernetes/views/deploy/deployController.js
@@ -9,15 +9,15 @@ import { renderTemplate } from '@/react/portainer/custom-templates/components/ut
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
import { kubernetes } from '@@/BoxSelector/common-options/deployment-methods';
import { editor, git, customTemplate, url } from '@@/BoxSelector/common-options/build-methods';
+import { confirmWebEditorDiscard } from '@@/modals/confirm';
class KubernetesDeployController {
/* @ngInject */
- constructor($async, $state, $window, Authentication, ModalService, Notifications, KubernetesResourcePoolService, StackService, WebhookHelper, CustomTemplateService) {
+ constructor($async, $state, $window, Authentication, Notifications, KubernetesResourcePoolService, StackService, WebhookHelper, CustomTemplateService) {
this.$async = $async;
this.$state = $state;
this.$window = $window;
this.Authentication = Authentication;
- this.ModalService = ModalService;
this.Notifications = Notifications;
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.StackService = StackService;
@@ -321,7 +321,7 @@ class KubernetesDeployController {
async uiCanExit() {
if (this.formValues.EditorContent && this.state.isEditorDirty) {
- return this.ModalService.confirmWebEditorDiscard();
+ return confirmWebEditorDiscard();
}
}
diff --git a/app/kubernetes/views/resource-pools/edit/resourcePoolController.js b/app/kubernetes/views/resource-pools/edit/resourcePoolController.js
index 29e740c40..583e055e7 100644
--- a/app/kubernetes/views/resource-pools/edit/resourcePoolController.js
+++ b/app/kubernetes/views/resource-pools/edit/resourcePoolController.js
@@ -14,6 +14,7 @@ import KubernetesResourceQuotaConverter from 'Kubernetes/converters/resourceQuot
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
import { FeatureId } from '@/react/portainer/feature-flags/enums';
import { updateIngressControllerClassMap, getIngressControllerClassMap } from '@/react/kubernetes/cluster/ingressClass/utils';
+import { confirmUpdate } from '@@/modals/confirm';
class KubernetesResourcePoolController {
/* #region CONSTRUCTOR */
@@ -26,7 +27,6 @@ class KubernetesResourcePoolController {
Notifications,
LocalStorage,
EndpointService,
- ModalService,
KubernetesNodeService,
KubernetesMetricsService,
KubernetesResourceQuotaService,
@@ -45,7 +45,6 @@ class KubernetesResourcePoolController {
Notifications,
LocalStorage,
EndpointService,
- ModalService,
KubernetesNodeService,
KubernetesMetricsService,
KubernetesResourceQuotaService,
@@ -189,7 +188,7 @@ class KubernetesResourcePoolController {
${warnings.ingress ? messages.ingress + '
' : ''}
${warnings.registries ? messages.registries + '
' : ''}
Do you wish to continue?`;
- this.ModalService.confirmUpdate(displayedMessage, (confirmed) => {
+ confirmUpdate(displayedMessage, (confirmed) => {
if (confirmed) {
return this.$async(this.updateResourcePoolAsync, this.savedFormValues, this.formValues);
}
@@ -205,7 +204,7 @@ class KubernetesResourcePoolController {
: 'Marking this namespace as a system namespace will prevent non administrator users from managing it and the resources it contains. Are you sure?';
return new Promise((resolve) => {
- this.ModalService.confirmUpdate(message, resolve);
+ confirmUpdate(message, resolve);
});
}
diff --git a/app/kubernetes/views/resource-pools/resourcePoolsController.js b/app/kubernetes/views/resource-pools/resourcePoolsController.js
index 51e380004..3aacad7f1 100644
--- a/app/kubernetes/views/resource-pools/resourcePoolsController.js
+++ b/app/kubernetes/views/resource-pools/resourcePoolsController.js
@@ -1,12 +1,14 @@
import angular from 'angular';
+import { confirm } from '@@/modals/confirm';
+import { ModalType } from '@@/modals';
+import { buildConfirmButton } from '@@/modals/utils';
class KubernetesResourcePoolsController {
/* @ngInject */
- constructor($async, $state, Notifications, ModalService, KubernetesResourcePoolService, KubernetesNamespaceService) {
+ constructor($async, $state, Notifications, KubernetesResourcePoolService, KubernetesNamespaceService) {
this.$async = $async;
this.$state = $state;
this.Notifications = Notifications;
- this.ModalService = ModalService;
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.KubernetesNamespaceService = KubernetesNamespaceService;
@@ -53,7 +55,13 @@ class KubernetesResourcePoolsController {
const message = isTerminatingNS
? 'At least one namespace is in a terminating state. For terminating state namespaces, you may continue and force removal, but doing so without having properly cleaned up may lead to unstable and unpredictable behavior. Are you sure you wish to proceed?'
: 'Do you want to remove the selected namespace(s)? All the resources associated to the selected namespace(s) will be removed too. Are you sure you wish to proceed?';
- this.ModalService.confirmWithTitle(isTerminatingNS ? 'Force namespace removal' : 'Are you sure?', message, (confirmed) => {
+ confirm({
+ title: isTerminatingNS ? 'Force namespace removal' : 'Are you sure?',
+ message,
+ confirmButton: buildConfirmButton('Remove', 'danger'),
+
+ modalType: ModalType.Destructive,
+ }).then((confirmed) => {
if (confirmed) {
return this.$async(this.removeActionAsync, selectedItems);
}
diff --git a/app/kubernetes/views/volumes/edit/volumeController.js b/app/kubernetes/views/volumes/edit/volumeController.js
index 72864b107..aad4969d7 100644
--- a/app/kubernetes/views/volumes/edit/volumeController.js
+++ b/app/kubernetes/views/volumes/edit/volumeController.js
@@ -5,6 +5,7 @@ import KubernetesVolumeHelper from 'Kubernetes/helpers/volumeHelper';
import KubernetesEventHelper from 'Kubernetes/helpers/eventHelper';
import { KubernetesStorageClassAccessPolicies } from 'Kubernetes/models/storage-class/models';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
+import { confirmRedeploy } from '@/react/kubernetes/volumes/ItemView/ConfirmRedeployModal';
class KubernetesVolumeController {
/* @ngInject */
@@ -17,7 +18,6 @@ class KubernetesVolumeController {
KubernetesEventService,
KubernetesApplicationService,
KubernetesPersistentVolumeClaimService,
- ModalService,
KubernetesPodService
) {
this.$async = $async;
@@ -29,7 +29,6 @@ class KubernetesVolumeController {
this.KubernetesEventService = KubernetesEventService;
this.KubernetesApplicationService = KubernetesApplicationService;
this.KubernetesPersistentVolumeClaimService = KubernetesPersistentVolumeClaimService;
- this.ModalService = ModalService;
this.KubernetesPodService = KubernetesPodService;
this.onInit = this.onInit.bind(this);
@@ -104,12 +103,9 @@ class KubernetesVolumeController {
updateVolume() {
if (KubernetesVolumeHelper.isUsed(this.volume)) {
- this.ModalService.confirmRedeploy(
- 'One or multiple applications are currently using this volume. For the change to be taken into account these applications will need to be redeployed. Do you want us to reschedule it now?',
- (redeploy) => {
- return this.$async(this.updateVolumeAsync, redeploy);
- }
- );
+ confirmRedeploy().then((redeploy) => {
+ return this.$async(this.updateVolumeAsync, redeploy);
+ });
} else {
return this.$async(this.updateVolumeAsync, false);
}
diff --git a/app/kubernetes/views/volumes/volumesController.js b/app/kubernetes/views/volumes/volumesController.js
index 053f61357..6e0ab6152 100644
--- a/app/kubernetes/views/volumes/volumesController.js
+++ b/app/kubernetes/views/volumes/volumesController.js
@@ -3,6 +3,7 @@ import filesizeParser from 'filesize-parser';
import angular from 'angular';
import KubernetesVolumeHelper from 'Kubernetes/helpers/volumeHelper';
import KubernetesResourceQuotaHelper from 'Kubernetes/helpers/resourceQuotaHelper';
+import { confirmDelete } from '@@/modals/confirm';
function buildStorages(storages, volumes) {
_.forEach(storages, (s) => {
@@ -21,12 +22,11 @@ function computeSize(volumes) {
class KubernetesVolumesController {
/* @ngInject */
- constructor($async, $state, Notifications, Authentication, ModalService, LocalStorage, KubernetesStorageService, KubernetesVolumeService, KubernetesApplicationService) {
+ constructor($async, $state, Notifications, Authentication, LocalStorage, KubernetesStorageService, KubernetesVolumeService, KubernetesApplicationService) {
this.$async = $async;
this.$state = $state;
this.Notifications = Notifications;
this.Authentication = Authentication;
- this.ModalService = ModalService;
this.LocalStorage = LocalStorage;
this.KubernetesStorageService = KubernetesStorageService;
this.KubernetesVolumeService = KubernetesVolumeService;
@@ -63,7 +63,7 @@ class KubernetesVolumesController {
}
removeAction(selectedItems) {
- this.ModalService.confirmDeletion('Do you want to remove the selected volume(s)?', (confirmed) => {
+ confirmDelete('Do you want to remove the selected volume(s)?').then((confirmed) => {
if (confirmed) {
return this.$async(this.removeActionAsync, selectedItems);
}
diff --git a/app/portainer/components/forms/kubernetes-app-git-form/kubernetes-app-git-form.controller.js b/app/portainer/components/forms/kubernetes-app-git-form/kubernetes-app-git-form.controller.js
index ed76cc16c..813174654 100644
--- a/app/portainer/components/forms/kubernetes-app-git-form/kubernetes-app-git-form.controller.js
+++ b/app/portainer/components/forms/kubernetes-app-git-form/kubernetes-app-git-form.controller.js
@@ -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');
diff --git a/app/portainer/components/forms/kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.controller.js b/app/portainer/components/forms/kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.controller.js
index f1ccc10fb..f65317a78 100644
--- a/app/portainer/components/forms/kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.controller.js
+++ b/app/portainer/components/forms/kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.controller.js
@@ -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');
diff --git a/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.controller.js b/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.controller.js
index 9499c8576..ce156a5ce 100644
--- a/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.controller.js
+++ b/app/portainer/components/forms/stack-redeploy-git-form/stack-redeploy-git-form.controller.js
@@ -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() {
diff --git a/app/portainer/services/index.ts b/app/portainer/services/index.ts
index ccd88deee..d4438c485 100644
--- a/app/portainer/services/index.ts
+++ b/app/portainer/services/index.ts
@@ -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;
diff --git a/app/portainer/services/modal.service/confirm.ts b/app/portainer/services/modal.service/confirm.ts
deleted file mode 100644
index a6eecf26b..000000000
--- a/app/portainer/services/modal.service/confirm.ts
+++ /dev/null
@@ -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 =
- 'Disassociating this Edge environment will mark it as non associated and will clear the registered Edge ID.
' +
- 'Any agent started with the Edge key associated to this environment will be able to re-associate with this environment.
' +
- '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.
';
- 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);
-}
diff --git a/app/portainer/services/modal.service/index.ts b/app/portainer/services/modal.service/index.ts
deleted file mode 100644
index 1fc4ffad6..000000000
--- a/app/portainer/services/modal.service/index.ts
+++ /dev/null
@@ -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: ``,
- 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,
- };
-}
diff --git a/app/portainer/services/modal.service/prompt.ts b/app/portainer/services/modal.service/prompt.ts
deleted file mode 100644
index 963fcff38..000000000
--- a/app/portainer/services/modal.service/prompt.ts
+++ /dev/null
@@ -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) {
- 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',
- 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}`,
- 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',
- 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(`${message}
`);
- 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 = `
-
- Cannot re-pull as the image is inaccessible - either it no longer exists or the tag or name is no longer correct.
-
-
`;
- 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',
- 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',
- value: '1',
- },
- ],
- buttons: {
- confirm: {
- label: 'Update',
- className: 'btn-primary',
- },
- },
- callback,
- });
-
- customizeCheckboxPrompt(box, sanitizedMessage, defaultToggle);
-}
-
-function customizeCheckboxPrompt(
- box: JQuery,
- message: string,
- toggleCheckbox = false,
- showCheck = false
-) {
- box.find('.bootbox-body').prepend(`${message}
`);
- const checkbox = box.find('.bootbox-input-checkbox');
- checkbox.prop('checked', toggleCheckbox);
-
- if (showCheck) {
- checkbox.addClass('visible');
- }
-}
diff --git a/app/portainer/services/modal.service/utils.ts b/app/portainer/services/modal.service/utils.ts
deleted file mode 100644
index d7ba033b7..000000000
--- a/app/portainer/services/modal.service/utils.ts
+++ /dev/null
@@ -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 `
-
-
${sanitize(title)}
-
- `;
-}
-
-export function applyBoxCSS(box: JQuery) {
- box.css({
- 'vertical-align': 'middle',
- });
-}
diff --git a/app/portainer/services/notifications.ts b/app/portainer/services/notifications.ts
index e2cc3a644..b5214c2ec 100644
--- a/app/portainer/services/notifications.ts
+++ b/app/portainer/services/notifications.ts
@@ -15,6 +15,7 @@ toastr.options = {
closeButton: true,
progressBar: true,
tapToDismiss: false,
+ escapeHtml: true,
// custom button, using the lucide icon x.svg inside
closeHtml: `