mirror of
https://github.com/portainer/portainer.git
synced 2025-08-02 20:35:25 +02:00
feat(settings): add settings management (#906)
This commit is contained in:
parent
5e74a3993b
commit
c7e306841a
93 changed files with 1086 additions and 457 deletions
|
@ -1,6 +1,6 @@
|
|||
angular.module('auth', [])
|
||||
.controller('AuthenticationController', ['$scope', '$state', '$stateParams', '$window', '$timeout', '$sanitize', 'Config', 'Authentication', 'Users', 'EndpointService', 'StateManager', 'EndpointProvider', 'Notifications',
|
||||
function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Config, Authentication, Users, EndpointService, StateManager, EndpointProvider, Notifications) {
|
||||
.controller('AuthenticationController', ['$scope', '$state', '$stateParams', '$window', '$timeout', '$sanitize', 'Authentication', 'Users', 'EndpointService', 'StateManager', 'EndpointProvider', 'Notifications',
|
||||
function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Authentication, Users, EndpointService, StateManager, EndpointProvider, Notifications) {
|
||||
|
||||
$scope.authData = {
|
||||
username: 'admin',
|
||||
|
@ -13,6 +13,8 @@ function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Config, Au
|
|||
error: false
|
||||
};
|
||||
|
||||
$scope.logo = StateManager.getState().application.logo;
|
||||
|
||||
if (!$scope.applicationState.application.authentication) {
|
||||
EndpointService.endpoints()
|
||||
.then(function success(data) {
|
||||
|
@ -59,10 +61,6 @@ function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Config, Au
|
|||
$state.go('dashboard');
|
||||
}
|
||||
|
||||
Config.$promise.then(function (c) {
|
||||
$scope.logo = c.logo;
|
||||
});
|
||||
|
||||
$scope.createAdminUser = function() {
|
||||
var password = $sanitize($scope.initPasswordData.password);
|
||||
Users.initAdminUser({password: password}, function (d) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('containerConsole', [])
|
||||
.controller('ContainerConsoleController', ['$scope', '$stateParams', 'Settings', 'Container', 'Image', 'Exec', '$timeout', 'EndpointProvider', 'Notifications',
|
||||
function ($scope, $stateParams, Settings, Container, Image, Exec, $timeout, EndpointProvider, Notifications) {
|
||||
.controller('ContainerConsoleController', ['$scope', '$stateParams', 'Container', 'Image', 'Exec', '$timeout', 'EndpointProvider', 'Notifications',
|
||||
function ($scope, $stateParams, Container, Image, Exec, $timeout, EndpointProvider, Notifications) {
|
||||
$scope.state = {};
|
||||
$scope.state.loaded = false;
|
||||
$scope.state.connected = false;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
angular.module('containers', [])
|
||||
.controller('ContainersController', ['$q', '$scope', '$filter', 'Container', 'ContainerService', 'ContainerHelper', 'Info', 'Settings', 'Notifications', 'Config', 'Pagination', 'EntityListService', 'ModalService', 'ResourceControlService', 'EndpointProvider',
|
||||
function ($q, $scope, $filter, Container, ContainerService, ContainerHelper, Info, Settings, Notifications, Config, Pagination, EntityListService, ModalService, ResourceControlService, EndpointProvider) {
|
||||
.controller('ContainersController', ['$q', '$scope', '$filter', 'Container', 'ContainerService', 'ContainerHelper', 'Info', 'Notifications', 'Pagination', 'EntityListService', 'ModalService', 'ResourceControlService', 'EndpointProvider',
|
||||
function ($q, $scope, $filter, Container, ContainerService, ContainerHelper, Info, Notifications, Pagination, EntityListService, ModalService, ResourceControlService, EndpointProvider) {
|
||||
$scope.state = {};
|
||||
$scope.state.pagination_count = Pagination.getPaginationCount('containers');
|
||||
$scope.state.displayAll = Settings.displayAll;
|
||||
$scope.state.displayAll = true;
|
||||
$scope.state.displayIP = false;
|
||||
$scope.sortType = 'State';
|
||||
$scope.sortReverse = false;
|
||||
|
@ -25,9 +25,6 @@ angular.module('containers', [])
|
|||
$scope.state.selectedItemCount = 0;
|
||||
Container.query(data, function (d) {
|
||||
var containers = d;
|
||||
if ($scope.containersToHideLabels) {
|
||||
containers = ContainerHelper.hideContainers(d, $scope.containersToHideLabels);
|
||||
}
|
||||
$scope.containers = containers.map(function (container) {
|
||||
var model = new ContainerViewModel(container);
|
||||
model.Status = $filter('containerstatus')(model.Status);
|
||||
|
@ -59,7 +56,7 @@ angular.module('containers', [])
|
|||
counter = counter - 1;
|
||||
if (counter === 0) {
|
||||
$('#loadContainersSpinner').hide();
|
||||
update({all: Settings.displayAll ? 1 : 0});
|
||||
update({all: $scope.state.displayAll ? 1 : 0});
|
||||
}
|
||||
};
|
||||
angular.forEach(items, function (c) {
|
||||
|
@ -134,8 +131,7 @@ angular.module('containers', [])
|
|||
};
|
||||
|
||||
$scope.toggleGetAll = function () {
|
||||
Settings.displayAll = $scope.state.displayAll;
|
||||
update({all: Settings.displayAll ? 1 : 0});
|
||||
update({all: $scope.state.displayAll ? 1 : 0});
|
||||
};
|
||||
|
||||
$scope.startAction = function () {
|
||||
|
@ -206,15 +202,16 @@ angular.module('containers', [])
|
|||
return swarm_hosts;
|
||||
}
|
||||
|
||||
Config.$promise.then(function (c) {
|
||||
$scope.containersToHideLabels = c.hiddenLabels;
|
||||
function initView(){
|
||||
if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM') {
|
||||
Info.get({}, function (d) {
|
||||
$scope.swarm_hosts = retrieveSwarmHostsInfo(d);
|
||||
update({all: Settings.displayAll ? 1 : 0});
|
||||
update({all: $scope.state.displayAll ? 1 : 0});
|
||||
});
|
||||
} else {
|
||||
update({all: Settings.displayAll ? 1 : 0});
|
||||
update({all: $scope.state.displayAll ? 1 : 0});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
}]);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// @@OLD_SERVICE_CONTROLLER: this service should be rewritten to use services.
|
||||
// See app/components/templates/templatesController.js as a reference.
|
||||
angular.module('createContainer', [])
|
||||
.controller('CreateContainerController', ['$q', '$scope', '$state', '$stateParams', '$filter', 'Config', 'Info', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'Network', 'ResourceControlService', 'Authentication', 'Notifications', 'ContainerService', 'ImageService', 'ControllerDataPipeline', 'FormValidator',
|
||||
function ($q, $scope, $state, $stateParams, $filter, Config, Info, Container, ContainerHelper, Image, ImageHelper, Volume, Network, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, ControllerDataPipeline, FormValidator) {
|
||||
.controller('CreateContainerController', ['$q', '$scope', '$state', '$stateParams', '$filter', 'Info', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'Network', 'ResourceControlService', 'Authentication', 'Notifications', 'ContainerService', 'ImageService', 'ControllerDataPipeline', 'FormValidator',
|
||||
function ($q, $scope, $state, $stateParams, $filter, Info, Container, ContainerHelper, Image, ImageHelper, Volume, Network, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, ControllerDataPipeline, FormValidator) {
|
||||
|
||||
$scope.formValues = {
|
||||
alwaysPull: true,
|
||||
|
@ -233,47 +233,41 @@ function ($q, $scope, $state, $stateParams, $filter, Config, Info, Container, Co
|
|||
}
|
||||
|
||||
function initView() {
|
||||
Config.$promise.then(function (c) {
|
||||
var containersToHideLabels = c.hiddenLabels;
|
||||
|
||||
Volume.query({}, function (d) {
|
||||
$scope.availableVolumes = d.Volumes;
|
||||
}, function (e) {
|
||||
Notifications.error('Failure', e, 'Unable to retrieve volumes');
|
||||
});
|
||||
|
||||
Network.query({}, function (d) {
|
||||
var networks = d;
|
||||
if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM' || $scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE') {
|
||||
networks = d.filter(function (network) {
|
||||
if (network.Scope === 'global') {
|
||||
return network;
|
||||
}
|
||||
});
|
||||
$scope.globalNetworkCount = networks.length;
|
||||
networks.push({Name: 'bridge'});
|
||||
networks.push({Name: 'host'});
|
||||
networks.push({Name: 'none'});
|
||||
}
|
||||
networks.push({Name: 'container'});
|
||||
$scope.availableNetworks = networks;
|
||||
if (!_.find(networks, {'Name': 'bridge'})) {
|
||||
$scope.config.HostConfig.NetworkMode = 'nat';
|
||||
}
|
||||
}, function (e) {
|
||||
Notifications.error('Failure', e, 'Unable to retrieve networks');
|
||||
});
|
||||
|
||||
Container.query({}, function (d) {
|
||||
var containers = d;
|
||||
if (containersToHideLabels) {
|
||||
containers = ContainerHelper.hideContainers(d, containersToHideLabels);
|
||||
}
|
||||
$scope.runningContainers = containers;
|
||||
}, function(e) {
|
||||
Notifications.error('Failure', e, 'Unable to retrieve running containers');
|
||||
});
|
||||
Volume.query({}, function (d) {
|
||||
$scope.availableVolumes = d.Volumes;
|
||||
}, function (e) {
|
||||
Notifications.error('Failure', e, 'Unable to retrieve volumes');
|
||||
});
|
||||
|
||||
Network.query({}, function (d) {
|
||||
var networks = d;
|
||||
if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM' || $scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE') {
|
||||
networks = d.filter(function (network) {
|
||||
if (network.Scope === 'global') {
|
||||
return network;
|
||||
}
|
||||
});
|
||||
$scope.globalNetworkCount = networks.length;
|
||||
networks.push({Name: 'bridge'});
|
||||
networks.push({Name: 'host'});
|
||||
networks.push({Name: 'none'});
|
||||
}
|
||||
networks.push({Name: 'container'});
|
||||
$scope.availableNetworks = networks;
|
||||
if (!_.find(networks, {'Name': 'bridge'})) {
|
||||
$scope.config.HostConfig.NetworkMode = 'nat';
|
||||
}
|
||||
}, function (e) {
|
||||
Notifications.error('Failure', e, 'Unable to retrieve networks');
|
||||
});
|
||||
|
||||
Container.query({}, function (d) {
|
||||
var containers = d;
|
||||
$scope.runningContainers = containers;
|
||||
}, function(e) {
|
||||
Notifications.error('Failure', e, 'Unable to retrieve running containers');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function validateForm(accessControlData, isAdmin) {
|
||||
|
@ -327,5 +321,4 @@ function ($q, $scope, $state, $stateParams, $filter, Config, Info, Container, Co
|
|||
}
|
||||
|
||||
initView();
|
||||
|
||||
}]);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('dashboard', [])
|
||||
.controller('DashboardController', ['$scope', '$q', 'Config', 'Container', 'ContainerHelper', 'Image', 'Network', 'Volume', 'Info', 'Notifications',
|
||||
function ($scope, $q, Config, Container, ContainerHelper, Image, Network, Volume, Info, Notifications) {
|
||||
.controller('DashboardController', ['$scope', '$q', 'Container', 'ContainerHelper', 'Image', 'Network', 'Volume', 'Info', 'Notifications',
|
||||
function ($scope, $q, Container, ContainerHelper, Image, Network, Volume, Info, Notifications) {
|
||||
|
||||
$scope.containerData = {
|
||||
total: 0
|
||||
|
@ -15,14 +15,10 @@ function ($scope, $q, Config, Container, ContainerHelper, Image, Network, Volume
|
|||
total: 0
|
||||
};
|
||||
|
||||
function prepareContainerData(d, containersToHideLabels) {
|
||||
function prepareContainerData(d) {
|
||||
var running = 0;
|
||||
var stopped = 0;
|
||||
|
||||
var containers = d;
|
||||
if (containersToHideLabels) {
|
||||
containers = ContainerHelper.hideContainers(d, containersToHideLabels);
|
||||
}
|
||||
|
||||
for (var i = 0; i < containers.length; i++) {
|
||||
var item = containers[i];
|
||||
|
@ -65,7 +61,7 @@ function ($scope, $q, Config, Container, ContainerHelper, Image, Network, Volume
|
|||
$scope.infoData = info;
|
||||
}
|
||||
|
||||
function fetchDashboardData(containersToHideLabels) {
|
||||
function initView() {
|
||||
$('#loadingViewSpinner').show();
|
||||
$q.all([
|
||||
Container.query({all: 1}).$promise,
|
||||
|
@ -74,7 +70,7 @@ function ($scope, $q, Config, Container, ContainerHelper, Image, Network, Volume
|
|||
Network.query({}).$promise,
|
||||
Info.get({}).$promise
|
||||
]).then(function (d) {
|
||||
prepareContainerData(d[0], containersToHideLabels);
|
||||
prepareContainerData(d[0]);
|
||||
prepareImageData(d[1]);
|
||||
prepareVolumeData(d[2]);
|
||||
prepareNetworkData(d[3]);
|
||||
|
@ -86,7 +82,5 @@ function ($scope, $q, Config, Container, ContainerHelper, Image, Network, Volume
|
|||
});
|
||||
}
|
||||
|
||||
Config.$promise.then(function (c) {
|
||||
fetchDashboardData(c.hiddenLabels);
|
||||
});
|
||||
initView();
|
||||
}]);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('images', [])
|
||||
.controller('ImagesController', ['$scope', '$state', 'Config', 'ImageService', 'Notifications', 'Pagination', 'ModalService',
|
||||
function ($scope, $state, Config, ImageService, Notifications, Pagination, ModalService) {
|
||||
.controller('ImagesController', ['$scope', '$state', 'ImageService', 'Notifications', 'Pagination', 'ModalService',
|
||||
function ($scope, $state, ImageService, Notifications, Pagination, ModalService) {
|
||||
$scope.state = {};
|
||||
$scope.state.pagination_count = Pagination.getPaginationCount('images');
|
||||
$scope.sortType = 'RepoTags';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('network', [])
|
||||
.controller('NetworkController', ['$scope', '$state', '$stateParams', '$filter', 'Config', 'Network', 'Container', 'ContainerHelper', 'Notifications',
|
||||
function ($scope, $state, $stateParams, $filter, Config, Network, Container, ContainerHelper, Notifications) {
|
||||
.controller('NetworkController', ['$scope', '$state', '$stateParams', '$filter', 'Network', 'Container', 'ContainerHelper', 'Notifications',
|
||||
function ($scope, $state, $stateParams, $filter, Network, Container, ContainerHelper, Notifications) {
|
||||
|
||||
$scope.removeNetwork = function removeNetwork(networkId) {
|
||||
$('#loadingViewSpinner').show();
|
||||
|
@ -36,21 +36,7 @@ function ($scope, $state, $stateParams, $filter, Config, Network, Container, Con
|
|||
});
|
||||
};
|
||||
|
||||
function getNetwork() {
|
||||
$('#loadingViewSpinner').show();
|
||||
Network.get({id: $stateParams.id}, function success(data) {
|
||||
$scope.network = data;
|
||||
getContainersInNetwork(data);
|
||||
}, function error(err) {
|
||||
$('#loadingViewSpinner').hide();
|
||||
Notifications.error('Failure', err, 'Unable to retrieve network info');
|
||||
});
|
||||
}
|
||||
|
||||
function filterContainersInNetwork(network, containers) {
|
||||
if ($scope.containersToHideLabels) {
|
||||
containers = ContainerHelper.hideContainers(containers, $scope.containersToHideLabels);
|
||||
}
|
||||
var containersInNetwork = [];
|
||||
containers.forEach(function(container) {
|
||||
var containerInNetwork = network.Containers[container.Id];
|
||||
|
@ -93,8 +79,16 @@ function ($scope, $state, $stateParams, $filter, Config, Network, Container, Con
|
|||
}
|
||||
}
|
||||
|
||||
Config.$promise.then(function (c) {
|
||||
$scope.containersToHideLabels = c.hiddenLabels;
|
||||
getNetwork();
|
||||
});
|
||||
function initView() {
|
||||
$('#loadingViewSpinner').show();
|
||||
Network.get({id: $stateParams.id}, function success(data) {
|
||||
$scope.network = data;
|
||||
getContainersInNetwork(data);
|
||||
}, function error(err) {
|
||||
$('#loadingViewSpinner').hide();
|
||||
Notifications.error('Failure', err, 'Unable to retrieve network info');
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
}]);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('networks', [])
|
||||
.controller('NetworksController', ['$scope', '$state', 'Network', 'Config', 'Notifications', 'Pagination',
|
||||
function ($scope, $state, Network, Config, Notifications, Pagination) {
|
||||
.controller('NetworksController', ['$scope', '$state', 'Network', 'Notifications', 'Pagination',
|
||||
function ($scope, $state, Network, Notifications, Pagination) {
|
||||
$scope.state = {};
|
||||
$scope.state.pagination_count = Pagination.getPaginationCount('networks');
|
||||
$scope.state.selectedItemCount = 0;
|
||||
|
@ -97,7 +97,7 @@ function ($scope, $state, Network, Config, Notifications, Pagination) {
|
|||
});
|
||||
};
|
||||
|
||||
function fetchNetworks() {
|
||||
function initView() {
|
||||
$('#loadNetworksSpinner').show();
|
||||
Network.query({}, function (d) {
|
||||
$scope.networks = d;
|
||||
|
@ -109,7 +109,5 @@ function ($scope, $state, Network, Config, Notifications, Pagination) {
|
|||
});
|
||||
}
|
||||
|
||||
Config.$promise.then(function (c) {
|
||||
fetchNetworks();
|
||||
});
|
||||
initView();
|
||||
}]);
|
||||
|
|
|
@ -1,66 +1,153 @@
|
|||
<rd-header>
|
||||
<rd-header-title title="Settings">
|
||||
<i id="loadingViewSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
|
||||
</rd-header-title>
|
||||
<rd-header-content>Settings</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-lock" title="Change user password"></rd-widget-header>
|
||||
<rd-widget-header icon="fa-cogs" title="Application settings"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal" style="margin-top: 15px;">
|
||||
<!-- current-password-input -->
|
||||
<form class="form-horizontal">
|
||||
<!-- logo -->
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Logo
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="current_password" class="col-sm-2 control-label text-left">Current password</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input type="password" class="form-control" ng-model="formValues.currentPassword" id="current_password">
|
||||
<div class="col-sm-12">
|
||||
<label for="toggle_logo" class="control-label text-left">
|
||||
Use custom logo
|
||||
</label>
|
||||
<label class="switch" style="margin-left: 20px;">
|
||||
<input type="checkbox" name="toggle_logo" ng-model="formValues.customLogo"><i></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="formValues.customLogo">
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
You can specify the URL to your logo here. For an optimal display, logo dimensions should be 155px by 55px.
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="logo_url" class="col-sm-1 control-label text-left">
|
||||
URL
|
||||
</label>
|
||||
<div class="col-sm-11">
|
||||
<input type="text" class="form-control" ng-model="settings.LogoURL" id="logo_url" placeholder="https://mycompany.com/logo.png">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !current-password-input -->
|
||||
<div class="form-group" ng-if="invalidPassword">
|
||||
<!-- !logo -->
|
||||
<!-- app-templates -->
|
||||
<div class="col-sm-12 form-section-title">
|
||||
App Templates
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<i class="fa fa-times red-icon" aria-hidden="true"></i>
|
||||
<span class="small text-muted">Current password is not valid</span>
|
||||
<label for="toggle_templates" class="control-label text-left">
|
||||
Use custom templates
|
||||
</label>
|
||||
<label class="switch" style="margin-left: 20px;">
|
||||
<input type="checkbox" name="toggle_templates" ng-model="formValues.customTemplates"><i></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- new-password-input -->
|
||||
<div class="form-group">
|
||||
<label for="new_password" class="col-sm-2 control-label text-left">New password</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input type="password" class="form-control" ng-model="formValues.newPassword" id="new_password">
|
||||
<div ng-if="formValues.customTemplates">
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
You can specify the URL to your own template definitions file here. See <a href="https://portainer.readthedocs.io/en/stable/templates.html" target="_blank">Portainer documentation</a> for more details.
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group" >
|
||||
<label for="templates_url" class="col-sm-1 control-label text-left">
|
||||
URL
|
||||
</label>
|
||||
<div class="col-sm-11">
|
||||
<input type="text" class="form-control" ng-model="settings.TemplatesURL" id="templates_url" placeholder="https://myserver.mydomain/templates.json">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !new-password-input -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<i ng-class="{true: 'fa fa-check green-icon', false: 'fa fa-times red-icon'}[formValues.newPassword.length >= 8]" aria-hidden="true"></i>
|
||||
<span class="small text-muted">Your new password must be at least 8 characters long</span>
|
||||
<label for="toggle_external_contrib" class="control-label text-left">
|
||||
Hide external contributions
|
||||
<portainer-tooltip position="bottom" message="When enabled, external contributions such as LinuxServer.io will not be displayed in the sidebar."></portainer-tooltip>
|
||||
</label>
|
||||
<label class="switch" style="margin-left: 20px;">
|
||||
<input type="checkbox" name="toggle_external_contrib" ng-model="formValues.externalContributions"><i></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- confirm-password-input -->
|
||||
<div class="form-group">
|
||||
<label for="confirm_password" class="col-sm-2 control-label text-left">Confirm password</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input type="password" class="form-control" ng-model="formValues.confirmPassword" id="confirm_password">
|
||||
<span class="input-group-addon"><i ng-class="{true: 'fa fa-check green-icon', false: 'fa fa-times red-icon'}[formValues.newPassword !== '' && formValues.newPassword === formValues.confirmPassword]" aria-hidden="true"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !confirm-password-input -->
|
||||
<!-- !app-templates -->
|
||||
<!-- actions -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="!formValues.currentPassword || formValues.newPassword.length < 8 || formValues.newPassword !== formValues.confirmPassword" ng-click="updatePassword()">Update password</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-click="saveApplicationSettings()">Save</button>
|
||||
<i id="updateSettingsSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
|
||||
<!-- <span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span> -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-tags" title="Filtered containers"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
You can hide containers with specific labels from Portainer UI. You need to specify the label name and value.
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="header_name" class="col-sm-1 control-label text-left">Name</label>
|
||||
<div class="col-sm-11 col-md-4">
|
||||
<input type="text" class="form-control" id="header_name" ng-model="formValues.labelName" placeholder="e.g. com.example.foo">
|
||||
</div>
|
||||
<label for="header_value" class="col-sm-1 margin-sm-top control-label text-left">Value</label>
|
||||
<div class="col-sm-11 col-md-4 margin-sm-top">
|
||||
<input type="text" class="form-control" id="header_value" ng-model="formValues.labelValue" placeholder="e.g. bar">
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-2 margin-sm-top">
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-click="addFilteredContainerLabel()"><i class="fa fa-plus space-right" aria-hidden="true"></i>Add label</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12 table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Value</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="label in settings.BlackListedLabels">
|
||||
<td>{{ label.name }}</td>
|
||||
<td>{{ label.value }}</td>
|
||||
<td><button type="button" class="btn btn-danger btn-xs" ng-click="removeFilteredContainerLabel($index)"><i class="fa fa-trash space-right" aria-hidden="true"></i>Remove</button></td>
|
||||
</tr>
|
||||
<tr ng-if="settings.BlackListedLabels.length === 0">
|
||||
<td colspan="2" class="text-center text-muted">No filtered containers labels.</td>
|
||||
</tr>
|
||||
<tr ng-if="!settings.BlackListedLabels">
|
||||
<td colspan="2" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !filtered-labels -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
|
|
|
@ -1,29 +1,94 @@
|
|||
angular.module('settings', [])
|
||||
.controller('SettingsController', ['$scope', '$state', '$sanitize', 'Authentication', 'UserService', 'Notifications',
|
||||
function ($scope, $state, $sanitize, Authentication, UserService, Notifications) {
|
||||
.controller('SettingsController', ['$scope', '$state', 'Notifications', 'SettingsService', 'StateManager', 'DEFAULT_TEMPLATES_URL',
|
||||
function ($scope, $state, Notifications, SettingsService, StateManager, DEFAULT_TEMPLATES_URL) {
|
||||
|
||||
$scope.formValues = {
|
||||
currentPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
customLogo: false,
|
||||
customTemplates: false,
|
||||
externalContributions: false,
|
||||
labelName: '',
|
||||
labelValue: ''
|
||||
};
|
||||
|
||||
$scope.updatePassword = function() {
|
||||
$scope.invalidPassword = false;
|
||||
var userID = Authentication.getUserDetails().ID;
|
||||
var currentPassword = $sanitize($scope.formValues.currentPassword);
|
||||
var newPassword = $sanitize($scope.formValues.newPassword);
|
||||
$scope.removeFilteredContainerLabel = function(index) {
|
||||
var settings = $scope.settings;
|
||||
settings.BlackListedLabels.splice(index, 1);
|
||||
|
||||
UserService.updateUserPassword(userID, currentPassword, newPassword)
|
||||
.then(function success() {
|
||||
Notifications.success('Success', 'Password successfully updated');
|
||||
$state.reload();
|
||||
updateSettings(settings, false);
|
||||
};
|
||||
|
||||
$scope.addFilteredContainerLabel = function() {
|
||||
var settings = $scope.settings;
|
||||
var label = {
|
||||
name: $scope.formValues.labelName,
|
||||
value: $scope.formValues.labelValue
|
||||
};
|
||||
settings.BlackListedLabels.push(label);
|
||||
|
||||
updateSettings(settings, true);
|
||||
};
|
||||
|
||||
$scope.saveApplicationSettings = function() {
|
||||
var settings = $scope.settings;
|
||||
|
||||
if (!$scope.formValues.customLogo) {
|
||||
settings.LogoURL = '';
|
||||
}
|
||||
|
||||
if (!$scope.formValues.customTemplates) {
|
||||
settings.TemplatesURL = DEFAULT_TEMPLATES_URL;
|
||||
}
|
||||
settings.DisplayExternalContributors = !$scope.formValues.externalContributions;
|
||||
|
||||
updateSettings(settings, false);
|
||||
};
|
||||
|
||||
function resetFormValues() {
|
||||
$scope.formValues.labelName = '';
|
||||
$scope.formValues.labelValue = '';
|
||||
}
|
||||
|
||||
function updateSettings(settings, resetForm) {
|
||||
$('#loadingViewSpinner').show();
|
||||
|
||||
SettingsService.update(settings)
|
||||
.then(function success(data) {
|
||||
Notifications.success('Settings updated');
|
||||
StateManager.updateLogo(settings.LogoURL);
|
||||
StateManager.updateExternalContributions(settings.DisplayExternalContributors);
|
||||
if (resetForm) {
|
||||
resetFormValues();
|
||||
}
|
||||
})
|
||||
.catch(function error(err) {
|
||||
if (err.invalidPassword) {
|
||||
$scope.invalidPassword = true;
|
||||
} else {
|
||||
Notifications.error('Failure', err, err.msg);
|
||||
}
|
||||
Notifications.error('Failure', err, 'Unable to update settings');
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadingViewSpinner').hide();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function initView() {
|
||||
$('#loadingViewSpinner').show();
|
||||
SettingsService.settings()
|
||||
.then(function success(data) {
|
||||
var settings = data;
|
||||
$scope.settings = settings;
|
||||
if (settings.LogoURL !== '') {
|
||||
$scope.formValues.customLogo = true;
|
||||
}
|
||||
if (settings.TemplatesURL !== DEFAULT_TEMPLATES_URL) {
|
||||
$scope.formValues.customTemplates = true;
|
||||
}
|
||||
$scope.formValues.externalContributions = !settings.DisplayExternalContributors;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve application settings');
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadingViewSpinner').hide();
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
}]);
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</li>
|
||||
<li class="sidebar-list">
|
||||
<a ui-sref="templates" ui-sref-active="active">App Templates <span class="menu-icon fa fa-rocket"></span></a>
|
||||
<div class="sidebar-sublist" ng-if="toggle && ($state.current.name === 'templates' || $state.current.name === 'templates_linuxserver')">
|
||||
<div class="sidebar-sublist" ng-if="toggle && displayExternalContributors && ($state.current.name === 'templates' || $state.current.name === 'templates_linuxserver')">
|
||||
<a ui-sref="templates_linuxserver" ui-sref-active="active">LinuxServer.io</a>
|
||||
</div>
|
||||
</li>
|
||||
|
@ -52,7 +52,7 @@
|
|||
<li class="sidebar-list" ng-if="applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE'">
|
||||
<a ui-sref="docker" ui-sref-active="active">Docker <span class="menu-icon fa fa-th"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-title" ng-if="isAdmin || isTeamLeader">
|
||||
<li class="sidebar-title" ng-if="!applicationState.application.authentication || isAdmin || isTeamLeader">
|
||||
<span>Portainer settings</span>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="applicationState.application.authentication && (isAdmin || isTeamLeader)">
|
||||
|
@ -64,6 +64,9 @@
|
|||
<li class="sidebar-list" ng-if="!applicationState.application.authentication || isAdmin">
|
||||
<a ui-sref="endpoints" ui-sref-active="active">Endpoints <span class="menu-icon fa fa-plug"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="!applicationState.application.authentication || isAdmin">
|
||||
<a ui-sref="settings" ui-sref-active="active">Settings <span class="menu-icon fa fa-cogs"></span></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="sidebar-footer">
|
||||
<div class="col-xs-12">
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
angular.module('sidebar', [])
|
||||
.controller('SidebarController', ['$q', '$scope', '$state', 'Settings', 'Config', 'EndpointService', 'StateManager', 'EndpointProvider', 'Notifications', 'Authentication', 'UserService',
|
||||
function ($q, $scope, $state, Settings, Config, EndpointService, StateManager, EndpointProvider, Notifications, Authentication, UserService) {
|
||||
.controller('SidebarController', ['$q', '$scope', '$state', 'Settings', 'EndpointService', 'StateManager', 'EndpointProvider', 'Notifications', 'Authentication', 'UserService',
|
||||
function ($q, $scope, $state, Settings, EndpointService, StateManager, EndpointProvider, Notifications, Authentication, UserService) {
|
||||
|
||||
Config.$promise.then(function (c) {
|
||||
$scope.logo = c.logo;
|
||||
});
|
||||
|
||||
$scope.uiVersion = Settings.uiVersion;
|
||||
$scope.uiVersion = StateManager.getState().application.version;
|
||||
$scope.displayExternalContributors = StateManager.getState().application.displayExternalContributors;
|
||||
$scope.logo = StateManager.getState().application.logo;
|
||||
$scope.endpoints = [];
|
||||
|
||||
$scope.switchEndpoint = function(endpoint) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('templates', [])
|
||||
.controller('TemplatesController', ['$scope', '$q', '$state', '$stateParams', '$anchorScroll', '$filter', 'Config', 'ContainerService', 'ContainerHelper', 'ImageService', 'NetworkService', 'TemplateService', 'TemplateHelper', 'VolumeService', 'Notifications', 'Pagination', 'ResourceControlService', 'Authentication', 'ControllerDataPipeline', 'FormValidator',
|
||||
function ($scope, $q, $state, $stateParams, $anchorScroll, $filter, Config, ContainerService, ContainerHelper, ImageService, NetworkService, TemplateService, TemplateHelper, VolumeService, Notifications, Pagination, ResourceControlService, Authentication, ControllerDataPipeline, FormValidator) {
|
||||
.controller('TemplatesController', ['$scope', '$q', '$state', '$stateParams', '$anchorScroll', '$filter', 'ContainerService', 'ContainerHelper', 'ImageService', 'NetworkService', 'TemplateService', 'TemplateHelper', 'VolumeService', 'Notifications', 'Pagination', 'ResourceControlService', 'Authentication', 'ControllerDataPipeline', 'FormValidator',
|
||||
function ($scope, $q, $state, $stateParams, $anchorScroll, $filter, ContainerService, ContainerHelper, ImageService, NetworkService, TemplateService, TemplateHelper, VolumeService, Notifications, Pagination, ResourceControlService, Authentication, ControllerDataPipeline, FormValidator) {
|
||||
$scope.state = {
|
||||
selectedTemplate: null,
|
||||
showAdvancedOptions: false,
|
||||
|
@ -159,31 +159,29 @@ function ($scope, $q, $state, $stateParams, $anchorScroll, $filter, Config, Cont
|
|||
|
||||
function initTemplates() {
|
||||
var templatesKey = $stateParams.key;
|
||||
Config.$promise.then(function (c) {
|
||||
$q.all({
|
||||
templates: TemplateService.getTemplates(templatesKey),
|
||||
containers: ContainerService.getContainers(0, c.hiddenLabels),
|
||||
networks: NetworkService.networks(),
|
||||
volumes: VolumeService.getVolumes()
|
||||
})
|
||||
.then(function success(data) {
|
||||
$scope.templates = data.templates;
|
||||
var availableCategories = [];
|
||||
angular.forEach($scope.templates, function(template) {
|
||||
availableCategories = availableCategories.concat(template.Categories);
|
||||
});
|
||||
$scope.availableCategories = _.sortBy(_.uniq(availableCategories));
|
||||
$scope.runningContainers = data.containers;
|
||||
$scope.availableNetworks = filterNetworksBasedOnProvider(data.networks);
|
||||
$scope.availableVolumes = data.volumes.Volumes;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
$scope.templates = [];
|
||||
Notifications.error('Failure', err, 'An error occured during apps initialization.');
|
||||
})
|
||||
.finally(function final(){
|
||||
$('#loadTemplatesSpinner').hide();
|
||||
$q.all({
|
||||
templates: TemplateService.getTemplates(templatesKey),
|
||||
containers: ContainerService.getContainers(0),
|
||||
networks: NetworkService.networks(),
|
||||
volumes: VolumeService.getVolumes()
|
||||
})
|
||||
.then(function success(data) {
|
||||
$scope.templates = data.templates;
|
||||
var availableCategories = [];
|
||||
angular.forEach($scope.templates, function(template) {
|
||||
availableCategories = availableCategories.concat(template.Categories);
|
||||
});
|
||||
$scope.availableCategories = _.sortBy(_.uniq(availableCategories));
|
||||
$scope.runningContainers = data.containers;
|
||||
$scope.availableNetworks = filterNetworksBasedOnProvider(data.networks);
|
||||
$scope.availableVolumes = data.volumes.Volumes;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
$scope.templates = [];
|
||||
Notifications.error('Failure', err, 'An error occured during apps initialization.');
|
||||
})
|
||||
.finally(function final(){
|
||||
$('#loadTemplatesSpinner').hide();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
68
app/components/userSettings/userSettings.html
Normal file
68
app/components/userSettings/userSettings.html
Normal file
|
@ -0,0 +1,68 @@
|
|||
<rd-header>
|
||||
<rd-header-title title="User settings">
|
||||
</rd-header-title>
|
||||
<rd-header-content>User settings</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-lock" title="Change user password"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal" style="margin-top: 15px;">
|
||||
<!-- current-password-input -->
|
||||
<div class="form-group">
|
||||
<label for="current_password" class="col-sm-2 control-label text-left">Current password</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input type="password" class="form-control" ng-model="formValues.currentPassword" id="current_password">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !current-password-input -->
|
||||
<div class="form-group" ng-if="invalidPassword">
|
||||
<div class="col-sm-12">
|
||||
<i class="fa fa-times red-icon" aria-hidden="true"></i>
|
||||
<span class="small text-muted">Current password is not valid</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- new-password-input -->
|
||||
<div class="form-group">
|
||||
<label for="new_password" class="col-sm-2 control-label text-left">New password</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input type="password" class="form-control" ng-model="formValues.newPassword" id="new_password">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !new-password-input -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<i ng-class="{true: 'fa fa-check green-icon', false: 'fa fa-times red-icon'}[formValues.newPassword.length >= 8]" aria-hidden="true"></i>
|
||||
<span class="small text-muted">Your new password must be at least 8 characters long</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- confirm-password-input -->
|
||||
<div class="form-group">
|
||||
<label for="confirm_password" class="col-sm-2 control-label text-left">Confirm password</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-lock" aria-hidden="true"></i></span>
|
||||
<input type="password" class="form-control" ng-model="formValues.confirmPassword" id="confirm_password">
|
||||
<span class="input-group-addon"><i ng-class="{true: 'fa fa-check green-icon', false: 'fa fa-times red-icon'}[formValues.newPassword !== '' && formValues.newPassword === formValues.confirmPassword]" aria-hidden="true"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !confirm-password-input -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="!formValues.currentPassword || formValues.newPassword.length < 8 || formValues.newPassword !== formValues.confirmPassword" ng-click="updatePassword()">Update password</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
29
app/components/userSettings/userSettingsController.js
Normal file
29
app/components/userSettings/userSettingsController.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
angular.module('userSettings', [])
|
||||
.controller('UserSettingsController', ['$scope', '$state', '$sanitize', 'Authentication', 'UserService', 'Notifications',
|
||||
function ($scope, $state, $sanitize, Authentication, UserService, Notifications) {
|
||||
$scope.formValues = {
|
||||
currentPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
};
|
||||
|
||||
$scope.updatePassword = function() {
|
||||
$scope.invalidPassword = false;
|
||||
var userID = Authentication.getUserDetails().ID;
|
||||
var currentPassword = $sanitize($scope.formValues.currentPassword);
|
||||
var newPassword = $sanitize($scope.formValues.newPassword);
|
||||
|
||||
UserService.updateUserPassword(userID, currentPassword, newPassword)
|
||||
.then(function success() {
|
||||
Notifications.success('Success', 'Password successfully updated');
|
||||
$state.reload();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
if (err.invalidPassword) {
|
||||
$scope.invalidPassword = true;
|
||||
} else {
|
||||
Notifications.error('Failure', err, err.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
}]);
|
Loading…
Add table
Add a link
Reference in a new issue