refactor(docker/switch/component): implement new design [EE-3688] (#7239)

* refactor(docker/switch/component): implement new design [EE=3688]

* revert create volume

* revert por-switch on exec.html

* refactor(container): switch fields on container creation page and edition page

* refactor(container): switch fields on networking/secret/servicewebhook/swarmvisual

* bug fixed

* code review issues

* merge code

* fix teaser for container edition

* fix encode secret toggle bug on adding secret page

* fixed a bug for service webhook toggle
pull/7300/head
Zhang Hao 2022-07-20 08:39:44 +08:00 committed by GitHub
parent e07253bcef
commit 43bbeed141
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 194 additions and 86 deletions

View File

@ -0,0 +1,23 @@
export default class ContainerCapabilitiesController {
/* @ngInject */
constructor($scope) {
this.$scope = $scope;
this.oldCapabilities = [];
}
createOnChangeHandler(capability) {
return (checked) => {
this.$scope.$evalAsync(() => {
capability.allowed = checked;
});
};
}
$doCheck() {
if (this.oldCapabilities.length !== this.capabilities.length) {
this.oldCapabilities = this.capabilities;
this.capabilitiesOnChange = Object.fromEntries(this.capabilities.map((cap) => [cap.capability, this.createOnChangeHandler(cap)]));
}
}
}

View File

@ -1,6 +1,9 @@
import controller from './container-capabilities.controller';
angular.module('portainer.docker').component('containerCapabilities', { angular.module('portainer.docker').component('containerCapabilities', {
templateUrl: './containerCapabilities.html', templateUrl: './containerCapabilities.html',
bindings: { bindings: {
capabilities: '=', capabilities: '=',
}, },
controller,
}); });

View File

@ -1,18 +1,15 @@
<form class="form-horizontal" style="margin-top: 15px"> <form class="form-horizontal" style="margin-top: 15px">
<div class="col-sm-12 form-section-title"> Container capabilities </div> <div class="col-sm-12 form-section-title"> Container capabilities </div>
<div class="form-group"> <div class="form-group flex flex-wrap gap-y-2 px-5">
<div ng-repeat="cap in $ctrl.capabilities" class="col-xs-12 col-sm-6 col-md-4"> <div ng-repeat="cap in $ctrl.capabilities" class="w-1/3 text-center">
<div class="row"> <por-switch-field
<div class="col-xs-8"> label-class="'col-sm-6'"
<label for="capability" class="control-label text-left" style="display: flex; padding: 0"> tooltip="cap.description"
{{ cap.capability }} checked="cap.allowed"
<portainer-tooltip message="cap.description"></portainer-tooltip> label="cap.capability"
</label> name="'capability'"
</div> on-change="($ctrl.capabilitiesOnChange[cap.capability])"
<div class="col-xs-4"> ></por-switch-field>
<label class="switch"> <input type="checkbox" name="capability" ng-model="cap.allowed" /><i></i> </label>
</div>
</div>
</div> </div>
</div> </div>
</form> </form>

View File

@ -110,6 +110,42 @@ angular.module('portainer.docker').controller('CreateContainerController', [
settingUnlimitedResources: false, settingUnlimitedResources: false,
}; };
$scope.onAlwaysPullChange = onAlwaysPullChange;
$scope.handlePublishAllPortsChange = handlePublishAllPortsChange;
$scope.handleAutoRemoveChange = handleAutoRemoveChange;
$scope.handlePrivilegedChange = handlePrivilegedChange;
$scope.handleInitChange = handleInitChange;
function onAlwaysPullChange(checked) {
return $scope.$evalAsync(() => {
$scope.formValues.alwaysPull = checked;
});
}
function handlePublishAllPortsChange(checked) {
return $scope.$evalAsync(() => {
$scope.config.HostConfig.PublishAllPorts = checked;
});
}
function handleAutoRemoveChange(checked) {
return $scope.$evalAsync(() => {
$scope.config.HostConfig.AutoRemove = checked;
});
}
function handlePrivilegedChange(checked) {
return $scope.$evalAsync(() => {
$scope.config.HostConfig.Privileged = checked;
});
}
function handleInitChange(checked) {
return $scope.$evalAsync(() => {
$scope.config.HostConfig.Init = checked;
});
}
$scope.handleEnvVarChange = handleEnvVarChange; $scope.handleEnvVarChange = handleEnvVarChange;
function handleEnvVarChange(value) { function handleEnvVarChange(value) {
$scope.formValues.Env = value; $scope.formValues.Env = value;

View File

@ -48,11 +48,15 @@
<!-- always-pull --> <!-- always-pull -->
<div class="form-group"> <div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<label for="ownership" class="control-label text-left"> <por-switch-field
Always pull the image name="'alwaysPull'"
<portainer-tooltip message="'When enabled, Portainer will automatically try to pull the specified image before creating the container.'"></portainer-tooltip> label-class="'col-sm-2'"
</label> checked="formValues.alwaysPull"
<label class="switch" style="margin-left: 20px"> <input type="checkbox" ng-model="formValues.alwaysPull" ng-disabled="!state.pullImageValidity" /><i></i> </label> disabled="!state.pullImageValidity"
label="'Always pull the image'"
on-change="(onAlwaysPullChange)"
tooltip="'When enabled, Portainer will automatically try to pull the specified image before creating the container.'"
></por-switch-field>
</div> </div>
</div> </div>
<!-- !always-pull --> <!-- !always-pull -->
@ -64,18 +68,12 @@
<div class="col-sm-12 form-section-title"> Webhooks </div> <div class="col-sm-12 form-section-title"> Webhooks </div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<label class="control-label text-left"> <por-switch-field
Create a container webhook feature-id="'container-webhook'"
<portainer-tooltip label-class="'col-sm-2'"
position="'top'" label="'Create a container webhook'"
message="'Create a webhook (or callback URI) to automate the recreate this container. Sending a POST request to this callback URI (without requiring any authentication) will pull the most up-to-date version of the associated image and recreate this container.'" tooltip="'Create a webhook (or callback URI) to automate the recreate this container. Sending a POST request to this callback URI (without requiring any authentication) will pull the most up-to-date version of the associated image and recreate this container.'"
></portainer-tooltip> ></por-switch-field>
</label>
<label class="switch box-selector-item limited business" style="margin-left: 20px">
<input type="checkbox" ng-model="formValues.EnableWebhook" disabled="disabled" ng-checked="false" />
<i class="orange-icon" aria-hidden="true" style="margin-right: 2px"></i>
</label>
<be-feature-indicator feature="containerWebhookFeature"></be-feature-indicator>
</div> </div>
</div> </div>
</div> </div>
@ -85,13 +83,13 @@
<!-- publish-exposed-ports --> <!-- publish-exposed-ports -->
<div class="form-group"> <div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<label class="control-label text-left"> <por-switch-field
Publish all exposed network ports to random host ports label-class="'col-sm-2'"
<portainer-tooltip checked="config.HostConfig.PublishAllPorts"
message="'When enabled, Portainer will let Docker automatically map a random port on the host to each one defined in the image Dockerfile.'" label="'Publish all exposed network ports to random host ports'"
></portainer-tooltip> on-change="(handlePublishAllPortsChange)"
</label> tooltip="'When enabled, Portainer will let Docker automatically map a random port on the host to each one defined in the image Dockerfile.'"
<label class="switch" style="margin-left: 20px"> <input type="checkbox" ng-model="config.HostConfig.PublishAllPorts" /><i></i> </label> ></por-switch-field>
</div> </div>
</div> </div>
<!-- !publish-exposed-ports --> <!-- !publish-exposed-ports -->
@ -156,13 +154,13 @@
<!-- autoremove --> <!-- autoremove -->
<div class="form-group"> <div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<label for="ownership" class="control-label text-left"> <por-switch-field
Auto remove label-class="'col-sm-2'"
<portainer-tooltip checked="config.HostConfig.AutoRemove"
message="'When enabled, Portainer will automatically remove the container when it exits. This is useful when you want to use the container only once.'" label="'Auto remove'"
></portainer-tooltip> on-change="(handleAutoRemoveChange)"
</label> tooltip="'When enabled, Portainer will automatically remove the container when it exits. This is useful when you want to use the container only once.'"
<label class="switch" style="margin-left: 20px"> <input type="checkbox" ng-model="config.HostConfig.AutoRemove" /><i></i> </label> ></por-switch-field>
</div> </div>
</div> </div>
<!-- !autoremove --> <!-- !autoremove -->
@ -614,16 +612,19 @@
<!-- privileged-mode --> <!-- privileged-mode -->
<div class="form-group" ng-if="isAdmin || allowPrivilegedMode"> <div class="form-group" ng-if="isAdmin || allowPrivilegedMode">
<div class="col-sm-12"> <div class="col-sm-12">
<label for="privileged_mode" class="control-label text-left"> Privileged mode </label> <por-switch-field
<label class="switch" style="margin-left: 20px"> <input type="checkbox" name="privileged_mode" ng-model="config.HostConfig.Privileged" /><i></i> </label> label-class="'col-sm-2'"
checked="config.HostConfig.Privileged"
label="'Privileged mode'"
on-change="(handlePrivilegedChange)"
></por-switch-field>
</div> </div>
</div> </div>
<!-- !privileged-mode --> <!-- !privileged-mode -->
<!-- init --> <!-- init -->
<div class="form-group" ng-if="applicationState.endpoint.apiVersion >= 1.37"> <div class="form-group" ng-if="applicationState.endpoint.apiVersion >= 1.37">
<div class="col-sm-12"> <div class="col-sm-12">
<label for="init" class="control-label text-left"> Init </label> <por-switch-field label-class="'col-sm-2'" checked="config.HostConfig.Init" label="'Init'" on-change="(handleInitChange)"></por-switch-field>
<label class="switch" style="margin-left: 20px"> <input type="checkbox" name="init" ng-model="config.HostConfig.Init" /><i></i> </label>
</div> </div>
</div> </div>
<!-- !init --> <!-- !init -->

View File

@ -107,17 +107,18 @@
<td>Finished</td> <td>Finished</td>
<td>{{ container.State.FinishedAt | getisodate }}</td> <td>{{ container.State.FinishedAt | getisodate }}</td>
</tr> </tr>
<tr ng-if="isAdmin && displayRecreateButton && applicationState.endpoint.type !== 4"> <tr ng-if="isAdmin && displayCreateWebhookButton && applicationState.endpoint.type !== 4">
<td colspan="1"> <td colspan="6">
Container webhook <div class="form-group">
<portainer-tooltip <div class="col-sm-12">
position="'top'" <por-switch-field
message="'Webhook (or callback URI) used to automate the recreation of this container. Sending a POST request to this callback URI (without requiring any authentication) will pull the most up-to-date version of the associated image and recreate this container.'" tooltip="'Webhook (or callback URI) used to automate the recreation of this container. Sending a POST request to this callback URI (without requiring any authentication) will pull the most up-to-date version of the associated image and recreate this container.'"
></portainer-tooltip> label-class="'col-sm-2'"
<label class="switch box-selector-item limited business" style="margin-left: 20px"> label="'Container webhook'"
<input disable-authorization="DockerContainerUpdate" type="checkbox" ng-model="WebhookExists" disabled="disabled" ng-checked="false" /><i></i> feature-id="'container-webhook'"
</label> ></por-switch-field>
<be-feature-indicator feature="containerWebhookFeature"></be-feature-indicator> </div>
</div>
</td> </td>
</tr> </tr>
<tr authorization="DockerContainerLogs, DockerContainerInspect, DockerContainerStats, DockerExecStart"> <tr authorization="DockerContainerLogs, DockerContainerInspect, DockerContainerStats, DockerExecStart">

View File

@ -52,6 +52,7 @@ angular.module('portainer.docker').controller('ContainerController', [
$scope.activityTime = 0; $scope.activityTime = 0;
$scope.portBindings = []; $scope.portBindings = [];
$scope.displayRecreateButton = false; $scope.displayRecreateButton = false;
$scope.displayCreateWebhookButton = false;
$scope.containerWebhookFeature = FeatureId.CONTAINER_WEBHOOK; $scope.containerWebhookFeature = FeatureId.CONTAINER_WEBHOOK;
$scope.config = { $scope.config = {
@ -150,6 +151,7 @@ angular.module('portainer.docker').controller('ContainerController', [
!allowPrivilegedModeForRegularUsers; !allowPrivilegedModeForRegularUsers;
$scope.displayRecreateButton = !inSwarm && !autoRemove && (admin || !settingRestrictsRegularUsers); $scope.displayRecreateButton = !inSwarm && !autoRemove && (admin || !settingRestrictsRegularUsers);
$scope.displayCreateWebhookButton = $scope.displayRecreateButton;
}) })
.catch(function error(err) { .catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve container info'); Notifications.error('Failure', err, 'Unable to retrieve container info');

View File

@ -23,6 +23,14 @@ angular.module('portainer.docker').controller('CreateSecretController', [
actionInProgress: false, actionInProgress: false,
}; };
$scope.handleEncodeSecretChange = handleEncodeSecretChange;
function handleEncodeSecretChange(checked) {
return $scope.$evalAsync(() => {
$scope.formValues.encodeSecret = checked;
});
}
$scope.addLabel = function () { $scope.addLabel = function () {
$scope.formValues.Labels.push({ key: '', value: '' }); $scope.formValues.Labels.push({ key: '', value: '' });
}; };

View File

@ -24,11 +24,13 @@
<!-- encode-secret --> <!-- encode-secret -->
<div class="form-group"> <div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<label for="encode_secret" class="control-label text-left"> <por-switch-field
Encode secret label-class="'col-sm-2'"
<portainer-tooltip message="'Secrets need to be base64 encoded. Disable this if your secret is already base64 encoded.'"></portainer-tooltip> checked="formValues.encodeSecret"
</label> label="'Encode secret'"
<label class="switch" style="margin-left: 20px"> <input type="checkbox" name="encode_secret" ng-model="formValues.encodeSecret" /><i></i> </label> on-change="(handleEncodeSecretChange)"
tooltip="'Secrets need to be base64 encoded. Disable this if your secret is already base64 encoded.'"
></por-switch-field>
</div> </div>
</div> </div>
<!-- !encode-secret --> <!-- !encode-secret -->

View File

@ -111,6 +111,14 @@ angular.module('portainer.docker').controller('CreateServiceController', [
$scope.allowBindMounts = false; $scope.allowBindMounts = false;
$scope.handleWebHookChange = handleWebHookChange;
function handleWebHookChange(checked) {
return $scope.$evalAsync(() => {
$scope.formValues.Webhook = checked;
});
}
$scope.handleEnvVarChange = handleEnvVarChange; $scope.handleEnvVarChange = handleEnvVarChange;
function handleEnvVarChange(value) { function handleEnvVarChange(value) {
$scope.formValues.Env = value; $scope.formValues.Env = value;

View File

@ -96,14 +96,13 @@
<div class="col-sm-12 form-section-title"> Webhooks </div> <div class="col-sm-12 form-section-title"> Webhooks </div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<label class="control-label text-left"> <por-switch-field
Create a service webhook label-class="'col-sm-2'"
<portainer-tooltip checked="$ctrl.formValues.Webhook"
position="'top'" label="'Create a service webhook'"
message="'Create a webhook (or callback URI) to automate the update of this service. Sending a POST request to this callback URI (without requiring any authentication) will pull the most up-to-date version of the associated image and re-deploy this service.'" on-change="(handleWebHookChange)"
></portainer-tooltip> tooltip="'Create a webhook (or callback URI) to automate the update of this service. Sending a POST request to this callback URI (without requiring any authentication) will pull the most up-to-date version of the associated image and re-deploy this service.'"
</label> ></por-switch-field>
<label class="switch" style="margin-left: 20px"> <input type="checkbox" ng-model="formValues.Webhook" /><i></i> </label>
</div> </div>
</div> </div>
</div> </div>

View File

@ -67,14 +67,13 @@
</tr> </tr>
<tr ng-if="isAdmin && applicationState.endpoint.type !== 4"> <tr ng-if="isAdmin && applicationState.endpoint.type !== 4">
<td colspan="{{ webhookURL ? '1' : '2' }}"> <td colspan="{{ webhookURL ? '1' : '2' }}">
Service webhook <por-switch-field
<portainer-tooltip tooltip="'Webhook (or callback URI) used to automate the update of this service. Sending a POST request to this callback URI (without requiring any authentication) will pull the most up-to-date version of the associated image and re-deploy this service.'"
position="'top'" checked="WebhookExists"
message="'Webhook (or callback URI) used to automate the update of this service. Sending a POST request to this callback URI (without requiring any authentication) will pull the most up-to-date version of the associated image and re-deploy this service.'" disabled="!isAdmin"
></portainer-tooltip> on-change="(onWebhookChange)"
<label class="switch" style="margin-left: 20px"> label="'Service webhook'"
<input disable-authorization="DockerServiceUpdate" type="checkbox" ng-model="WebhookExists" ng-click="updateWebhook(service)" /><i></i> ></por-switch-field>
</label>
</td> </td>
<td ng-if="webhookURL"> <td ng-if="webhookURL">
<span class="text-muted">{{ webhookURL | truncatelr }}</span> <span class="text-muted">{{ webhookURL | truncatelr }}</span>

View File

@ -331,6 +331,13 @@ angular.module('portainer.docker').controller('ServiceController', [
updateServiceArray(service, 'Hosts', service.Hosts); updateServiceArray(service, 'Hosts', service.Hosts);
}; };
$scope.onWebhookChange = function (enabled) {
$scope.$evalAsync(() => {
$scope.updateWebhook($scope.service);
$scope.WebhookExists = enabled;
});
};
$scope.updateWebhook = function updateWebhook(service) { $scope.updateWebhook = function updateWebhook(service) {
if ($scope.WebhookExists) { if ($scope.WebhookExists) {
WebhookService.deleteWebhook($scope.webhookID) WebhookService.deleteWebhook($scope.webhookID)

View File

@ -16,6 +16,20 @@ angular.module('portainer.docker').controller('SwarmVisualizerController', [
refreshRate: '5', refreshRate: '5',
}; };
$scope.handleChangeDisplayOnlyRunningTasks = function handleChangeDisplayOnlyRunningTasks(enabled) {
$scope.$evalAsync(() => {
$scope.state.DisplayOnlyRunningTasks = enabled;
$scope.changeDisplayOnlyRunningTasks();
});
};
$scope.handleChangeDisplayNodeLabels = function handleChangeDisplayNodeLabels(enabled) {
$scope.$evalAsync(() => {
$scope.state.DisplayNodeLabels = enabled;
$scope.changeDisplayNodeLabels();
});
};
$scope.$on('$destroy', function () { $scope.$on('$destroy', function () {
stopRepeater(); stopRepeater();
}); });

View File

@ -31,14 +31,22 @@
<div class="col-sm-12 form-section-title"> Options </div> <div class="col-sm-12 form-section-title"> Options </div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<label class="control-label text-left"> Only display running tasks </label> <por-switch-field
<label class="switch" style="margin-left: 20px"> label-class="'col-sm-2'"
<input type="checkbox" ng-model="state.DisplayOnlyRunningTasks" ng-change="changeDisplayOnlyRunningTasks()" /><i></i> checked="state.DisplayOnlyRunningTasks"
</label> label="'Only display running tasks'"
on-change="(handleChangeDisplayOnlyRunningTasks)"
></por-switch-field>
</div> </div>
</div>
<div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<label class="control-label text-left"> Display node labels </label> <por-switch-field
<label class="switch" style="margin-left: 20px"> <input type="checkbox" ng-model="state.DisplayNodeLabels" ng-change="changeDisplayNodeLabels()" /><i></i> </label> label-class="'col-sm-2'"
checked="state.DisplayNodeLabels"
label="'Display node labels'"
on-change="(handleChangeDisplayNodeLabels)"
></por-switch-field>
</div> </div>
</div> </div>
</form> </form>