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 message
pull/2926/head
Anthony Lapenna 2019-06-02 18:16:43 +12:00 committed by GitHub
parent 1d9166216a
commit 50f547a6e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 68 additions and 16 deletions

View File

@ -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"
@ -10,25 +12,44 @@ import (
) )
type motdResponse struct { type motdResponse struct {
Title string `json:"Title"` Title string `json:"Title"`
Message string `json:"Message"` Message string `json:"Message"`
Hash []byte `json:"Hash"` ContentLayout map[string]string `json:"ContentLayout"`
Style string `json:"Style"`
Hash []byte `json:"Hash"`
}
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) { 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)
} }

View File

@ -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

View File

@ -0,0 +1,8 @@
angular.module('portainer.app').component('motdPanel', {
templateUrl: './motdPanel.html',
bindings: {
motd: '<',
dismissAction: '&?'
},
transclude: true
});

View File

@ -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>

View File

@ -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;
} }

View File

@ -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"