mirror of https://github.com/portainer/portainer
feat(motd): add the ability to use custom style (#2918)
* feat(motd): rework motd display mechanism for more flexibility on motd content * feat(api): enhance MOTD * refactor(api): refactor MOTD related codebase * feat(motd): hash on messagepull/2926/head
parent
1d9166216a
commit
50f547a6e7
|
@ -1,7 +1,9 @@
|
||||||
package motd
|
package motd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/portainer/libhttp/response"
|
"github.com/portainer/libhttp/response"
|
||||||
"github.com/portainer/portainer/api"
|
"github.com/portainer/portainer/api"
|
||||||
|
@ -12,23 +14,42 @@ import (
|
||||||
type motdResponse struct {
|
type motdResponse struct {
|
||||||
Title string `json:"Title"`
|
Title string `json:"Title"`
|
||||||
Message string `json:"Message"`
|
Message string `json:"Message"`
|
||||||
|
ContentLayout map[string]string `json:"ContentLayout"`
|
||||||
|
Style string `json:"Style"`
|
||||||
Hash []byte `json:"Hash"`
|
Hash []byte `json:"Hash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) motd(w http.ResponseWriter, r *http.Request) {
|
type motdData struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Message []string `json:"message"`
|
||||||
|
ContentLayout map[string]string `json:"contentLayout"`
|
||||||
|
Style string `json:"style"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *Handler) motd(w http.ResponseWriter, r *http.Request) {
|
||||||
motd, err := client.Get(portainer.MessageOfTheDayURL, 0)
|
motd, err := client.Get(portainer.MessageOfTheDayURL, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.JSON(w, &motdResponse{Message: ""})
|
response.JSON(w, &motdResponse{Message: ""})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
title, err := client.Get(portainer.MessageOfTheDayTitleURL, 0)
|
var data motdData
|
||||||
|
err = json.Unmarshal(motd, &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.JSON(w, &motdResponse{Message: ""})
|
response.JSON(w, &motdResponse{Message: ""})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := crypto.HashFromBytes(motd)
|
message := strings.Join(data.Message, "\n")
|
||||||
response.JSON(w, &motdResponse{Title: string(title), Message: string(motd), Hash: hash})
|
|
||||||
|
hash := crypto.HashFromBytes([]byte(message))
|
||||||
|
resp := motdResponse{
|
||||||
|
Title: data.Title,
|
||||||
|
Message: message,
|
||||||
|
Hash: hash,
|
||||||
|
ContentLayout: data.ContentLayout,
|
||||||
|
Style: data.Style,
|
||||||
|
}
|
||||||
|
|
||||||
|
response.JSON(w, &resp)
|
||||||
}
|
}
|
||||||
|
|
|
@ -860,9 +860,7 @@ const (
|
||||||
// AssetsServerURL represents the URL of the Portainer asset server
|
// AssetsServerURL represents the URL of the Portainer asset server
|
||||||
AssetsServerURL = "https://portainer-io-assets.sfo2.digitaloceanspaces.com"
|
AssetsServerURL = "https://portainer-io-assets.sfo2.digitaloceanspaces.com"
|
||||||
// MessageOfTheDayURL represents the URL where Portainer MOTD message can be retrieved
|
// MessageOfTheDayURL represents the URL where Portainer MOTD message can be retrieved
|
||||||
MessageOfTheDayURL = AssetsServerURL + "/motd.html"
|
MessageOfTheDayURL = AssetsServerURL + "/motd.json"
|
||||||
// MessageOfTheDayTitleURL represents the URL where Portainer MOTD title can be retrieved
|
|
||||||
MessageOfTheDayTitleURL = AssetsServerURL + "/motd-title.txt"
|
|
||||||
// ExtensionDefinitionsURL represents the URL where Portainer extension definitions can be retrieved
|
// ExtensionDefinitionsURL represents the URL where Portainer extension definitions can be retrieved
|
||||||
ExtensionDefinitionsURL = AssetsServerURL + "/extensions-1.20.3.json"
|
ExtensionDefinitionsURL = AssetsServerURL + "/extensions-1.20.3.json"
|
||||||
// PortainerAgentHeader represents the name of the header available in any agent response
|
// PortainerAgentHeader represents the name of the header available in any agent response
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
angular.module('portainer.app').component('motdPanel', {
|
||||||
|
templateUrl: './motdPanel.html',
|
||||||
|
bindings: {
|
||||||
|
motd: '<',
|
||||||
|
dismissAction: '&?'
|
||||||
|
},
|
||||||
|
transclude: true
|
||||||
|
});
|
|
@ -0,0 +1,26 @@
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-body classes="motd-body">
|
||||||
|
<style ng-if="$ctrl.motd.Style">
|
||||||
|
{{ $ctrl.motd.Style }}
|
||||||
|
</style>
|
||||||
|
<div ng-style="{{ $ctrl.motd.ContentLayout ? $ctrl.motd.ContentLayout : {} }}">
|
||||||
|
<div class="col-sm-12 form-section-title">
|
||||||
|
<span style="float: left;">
|
||||||
|
{{ $ctrl.motd.Title }}
|
||||||
|
</span>
|
||||||
|
<span class="small" style="float: right;" ng-if="$ctrl.dismissAction">
|
||||||
|
<a ng-click="$ctrl.dismissAction()"><i class="fa fa-times"></i> dismiss</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<span class="text-muted">
|
||||||
|
<p ng-bind-html="$ctrl.motd.Message"></p>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -2,4 +2,6 @@ export function MotdViewModel(data) {
|
||||||
this.Title = data.Title;
|
this.Title = data.Title;
|
||||||
this.Message = data.Message;
|
this.Message = data.Message;
|
||||||
this.Hash = data.Hash;
|
this.Hash = data.Hash;
|
||||||
|
this.Style = data.Style;
|
||||||
|
this.ContentLayout = data.ContentLayout;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,11 @@
|
||||||
<rd-header-content>Endpoints</rd-header-content>
|
<rd-header-content>Endpoints</rd-header-content>
|
||||||
</rd-header>
|
</rd-header>
|
||||||
|
|
||||||
<information-panel
|
<motd-panel
|
||||||
ng-if="motd && motd.Message !== '' && applicationState.UI.dismissedInfoHash !== motd.Hash"
|
ng-if="motd && motd.Message !== '' && applicationState.UI.dismissedInfoHash !== motd.Hash"
|
||||||
title-text="{{ motd.Title }}"
|
motd="motd"
|
||||||
dismiss-action="dismissImportantInformation(motd.Hash)">
|
dismiss-action="dismissImportantInformation(motd.Hash)">
|
||||||
<span class="text-muted">
|
</motd-panel>
|
||||||
<p ng-bind-html="motd.Message"></p>
|
|
||||||
</span>
|
|
||||||
</information-panel>
|
|
||||||
|
|
||||||
<information-panel
|
<information-panel
|
||||||
ng-if="!isAdmin && endpoints.length === 0"
|
ng-if="!isAdmin && endpoints.length === 0"
|
||||||
|
|
Loading…
Reference in New Issue