1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-05 05:45:22 +02:00

feat(motd): add the ability to display motd and dimiss information panels (#2191)

* feat(api): add motd handler

* feat(app): add the motd api layer

* feat(motd): display motd and add the ability to dismiss information messages

* style(home): relocate important message before info01

* feat(api): silently fail when an error occurs during motd retrieval
This commit is contained in:
Anthony Lapenna 2018-08-21 20:40:42 +02:00 committed by GitHub
parent 74ca908759
commit 6ab6cfafb7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 269 additions and 108 deletions

View file

@ -3,6 +3,7 @@ angular.module('portainer')
.constant('API_ENDPOINT_DOCKERHUB', 'api/dockerhub')
.constant('API_ENDPOINT_ENDPOINTS', 'api/endpoints')
.constant('API_ENDPOINT_ENDPOINT_GROUPS', 'api/endpoint_groups')
.constant('API_ENDPOINT_MOTD', 'api/motd')
.constant('API_ENDPOINT_REGISTRIES', 'api/registries')
.constant('API_ENDPOINT_RESOURCE_CONTROLS', 'api/resource_controls')
.constant('API_ENDPOINT_SETTINGS', 'api/settings')

View file

@ -9,30 +9,22 @@
</div>
</div>
<div class="row" ng-if="!applicationState.endpoint.mode.agentProxy && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'">
<div class="col-sm-12">
<rd-widget>
<rd-widget-body>
<div class="col-sm-12 form-section-title">
Information
</div>
<div class="form-group">
<span class="small">
<p class="text-muted" ng-if="applicationState.endpoint.mode.role === 'MANAGER'">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
Portainer is connected to a node that is part of a Swarm cluster. Some resources located on other nodes in the cluster might not be available for management, have a look
at <a href="http://portainer.readthedocs.io/en/stable/agent.html" target="_blank">our agent setup</a> for more details.
</p>
<p class="text-muted" ng-if="applicationState.endpoint.mode.role === 'WORKER'">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
Portainer is connected to a worker node. Swarm management features will not be available.
</p>
</span>
</div>
</rd-widget-body>
</rd-widget>
</div>
</div>
<information-panel
ng-if="!applicationState.UI.dismissedInfoPanels['docker-dashboard-info-01'] && !applicationState.endpoint.mode.agentProxy && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'"
title-text="Information"
dismiss-action="dismissInformationPanel('docker-dashboard-info-01')">
<span class="small">
<p class="text-muted" ng-if="applicationState.endpoint.mode.role === 'MANAGER'">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
Portainer is connected to a node that is part of a Swarm cluster. Some resources located on other nodes in the cluster might not be available for management, have a look
at <a href="http://portainer.readthedocs.io/en/stable/agent.html" target="_blank">our agent setup</a> for more details.
</p>
<p class="text-muted" ng-if="applicationState.endpoint.mode.role === 'WORKER'">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
Portainer is connected to a worker node. Swarm management features will not be available.
</p>
</span>
</information-panel>
<div class="row" ng-if="(!applicationState.endpoint.mode.agentProxy || applicationState.endpoint.mode.provider !== 'DOCKER_SWARM_MODE') && info && endpoint">
<div class="col-sm-12">

View file

@ -1,6 +1,10 @@
angular.module('portainer.docker')
.controller('DashboardController', ['$scope', '$q', 'ContainerService', 'ImageService', 'NetworkService', 'VolumeService', 'SystemService', 'ServiceService', 'StackService', 'EndpointService', 'Notifications', 'EndpointProvider',
function ($scope, $q, ContainerService, ImageService, NetworkService, VolumeService, SystemService, ServiceService, StackService, EndpointService, Notifications, EndpointProvider) {
.controller('DashboardController', ['$scope', '$q', 'ContainerService', 'ImageService', 'NetworkService', 'VolumeService', 'SystemService', 'ServiceService', 'StackService', 'EndpointService', 'Notifications', 'EndpointProvider', 'StateManager',
function ($scope, $q, ContainerService, ImageService, NetworkService, VolumeService, SystemService, ServiceService, StackService, EndpointService, Notifications, EndpointProvider, StateManager) {
$scope.dismissInformationPanel = function(id) {
StateManager.dismissInformationPanel(id);
};
function initView() {
var endpointMode = $scope.applicationState.endpoint.mode;

View file

@ -1,7 +1,8 @@
angular.module('portainer.app').component('informationPanel', {
templateUrl: 'app/portainer/components/information-panel/informationPanel.html',
bindings: {
titleText: '@'
titleText: '@',
dismissAction: '&'
},
transclude: true
});

View file

@ -3,7 +3,12 @@
<rd-widget>
<rd-widget-body>
<div class="col-sm-12 form-section-title">
{{ $ctrl.titleText }}
<span style="float: left;">
{{ $ctrl.titleText }}
</span>
<span class="small" style="float: right;">
<a ng-click="$ctrl.dismissAction()"><i class="fa fa-times"></i> dismiss</a>
</span>
</div>
<div class="form-group">
<ng-transclude></ng-transclude>

View file

@ -0,0 +1,4 @@
function MotdViewModel(data) {
this.Message = data.Message;
this.Hash = data.Hash;
}

View file

@ -0,0 +1,7 @@
angular.module('portainer.app')
.factory('Motd', ['$resource', 'API_ENDPOINT_MOTD', function MotdFactory($resource, API_ENDPOINT_MOTD) {
'use strict';
return $resource(API_ENDPOINT_MOTD, {}, {
get: { method: 'GET' }
});
}]);

View file

@ -0,0 +1,22 @@
angular.module('portainer.app')
.factory('MotdService', ['$q', 'Motd', function MotdServiceFactory($q, Motd) {
'use strict';
var service = {};
service.motd = function() {
var deferred = $q.defer();
Motd.get().$promise
.then(function success(data) {
var motd = new MotdViewModel(data);
deferred.resolve(motd);
})
.catch(function error(err) {
deferred.reject({msg: 'Unable to retrieve information message', err: err});
});
return deferred.promise;
};
return service;
}]);

View file

@ -26,6 +26,12 @@ angular.module('portainer.app')
getApplicationState: function() {
return localStorageService.get('APPLICATION_STATE');
},
storeUIState: function(state) {
localStorageService.cookie.set('UI_STATE', state);
},
getUIState: function() {
return localStorageService.cookie.get('UI_STATE');
},
storeJWT: function(jwt) {
localStorageService.set('JWT', jwt);
},

View file

@ -9,7 +9,20 @@ function StateManagerFactory($q, SystemService, InfoHelper, LocalStorage, Settin
loading: true,
application: {},
endpoint: {},
UI: {}
UI: {
dismissedInfoPanels: {},
dismissedInfoHash: ''
}
};
manager.dismissInformationPanel = function(id) {
state.UI.dismissedInfoPanels[id] = true;
LocalStorage.storeUIState(state.UI);
};
manager.dismissImportantInformation = function(hash) {
state.UI.dismissedInfoHash = hash;
LocalStorage.storeUIState(state.UI);
};
manager.getState = function() {
@ -68,6 +81,11 @@ function StateManagerFactory($q, SystemService, InfoHelper, LocalStorage, Settin
manager.initialize = function () {
var deferred = $q.defer();
var UIState = LocalStorage.getUIState();
if (UIState) {
state.UI = UIState;
}
var endpointState = LocalStorage.getEndpointState();
if (endpointState) {
state.endpoint = endpointState;

View file

@ -7,7 +7,19 @@
<rd-header-content>Endpoints</rd-header-content>
</rd-header>
<information-panel title-text="Information">
<information-panel
ng-if="motd && applicationState.UI.dismissedInfoHash !== motd.Hash"
title-text="Important message"
dismiss-action="dismissImportantInformation(motd.Hash)">
<span class="text-muted">
<p ng-bind-html="motd.Message"></p>
</span>
</information-panel>
<information-panel
ng-if="!applicationState.UI.dismissedInfoPanels['home-info-01']"
title-text="Information"
dismiss-action="dismissInformationPanel('home-info-01')">
<span class="small text-muted">
<p ng-if="endpoints.length > 0">
Welcome to Portainer ! Click on any endpoint in the list below to access management features.

View file

@ -1,6 +1,6 @@
angular.module('portainer.app')
.controller('HomeController', ['$q', '$scope', '$state', 'Authentication', 'EndpointService', 'EndpointHelper', 'GroupService', 'Notifications', 'EndpointProvider', 'StateManager', 'ExtensionManager', 'ModalService',
function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, GroupService, Notifications, EndpointProvider, StateManager, ExtensionManager, ModalService) {
.controller('HomeController', ['$q', '$scope', '$state', 'Authentication', 'EndpointService', 'EndpointHelper', 'GroupService', 'Notifications', 'EndpointProvider', 'StateManager', 'ExtensionManager', 'ModalService', 'MotdService',
function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, GroupService, Notifications, EndpointProvider, StateManager, ExtensionManager, ModalService, MotdService) {
$scope.goToDashboard = function(endpoint) {
EndpointProvider.setEndpointID(endpoint.Id);
@ -12,6 +12,14 @@ function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, G
}
};
$scope.dismissImportantInformation = function(hash) {
StateManager.dismissImportantInformation(hash);
};
$scope.dismissInformationPanel = function(id) {
StateManager.dismissInformationPanel(id);
};
function triggerSnapshot() {
EndpointService.snapshot()
.then(function success(data) {
@ -57,6 +65,11 @@ function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, G
function initView() {
$scope.isAdmin = Authentication.getUserDetails().role === 1;
MotdService.motd()
.then(function success(data) {
$scope.motd = data;
});
$q.all({
endpoints: EndpointService.endpoints(),
groups: GroupService.groups()