[MM-10718] Move custom branding to TE (#8871)
* move custom branding to TE * move brand's enterprise code to server and remove BrandInterface
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
ebdceb8e52
Коммит
312edbe531
@@ -34,11 +34,8 @@ func TestUploadBrandImage(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ok, resp := Client.UploadBrandImage(data)
|
||||
_, resp := Client.UploadBrandImage(data)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
if ok {
|
||||
t.Fatal("Should return false, set brand image not allowed")
|
||||
}
|
||||
|
||||
// status code returns either forbidden or unauthorized
|
||||
// note: forbidden is set as default at Client4.SetProfileImage when request is terminated early by server
|
||||
@@ -53,5 +50,5 @@ func TestUploadBrandImage(t *testing.T) {
|
||||
}
|
||||
|
||||
_, resp = th.SystemAdminClient.UploadBrandImage(data)
|
||||
CheckNotImplementedStatus(t, resp)
|
||||
CheckCreatedStatus(t, resp)
|
||||
}
|
||||
|
||||
10
app/app.go
10
app/app.go
@@ -53,7 +53,6 @@ type App struct {
|
||||
Jobs *jobs.JobServer
|
||||
|
||||
AccountMigration einterfaces.AccountMigrationInterface
|
||||
Brand einterfaces.BrandInterface
|
||||
Cluster einterfaces.ClusterInterface
|
||||
Compliance einterfaces.ComplianceInterface
|
||||
DataRetention einterfaces.DataRetentionInterface
|
||||
@@ -258,12 +257,6 @@ func RegisterAccountMigrationInterface(f func(*App) einterfaces.AccountMigration
|
||||
accountMigrationInterface = f
|
||||
}
|
||||
|
||||
var brandInterface func(*App) einterfaces.BrandInterface
|
||||
|
||||
func RegisterBrandInterface(f func(*App) einterfaces.BrandInterface) {
|
||||
brandInterface = f
|
||||
}
|
||||
|
||||
var clusterInterface func(*App) einterfaces.ClusterInterface
|
||||
|
||||
func RegisterClusterInterface(f func(*App) einterfaces.ClusterInterface) {
|
||||
@@ -358,9 +351,6 @@ func (a *App) initEnterprise() {
|
||||
if accountMigrationInterface != nil {
|
||||
a.AccountMigration = accountMigrationInterface(a)
|
||||
}
|
||||
if brandInterface != nil {
|
||||
a.Brand = brandInterface(a)
|
||||
}
|
||||
if clusterInterface != nil {
|
||||
a.Cluster = clusterInterface(a)
|
||||
}
|
||||
|
||||
56
app/brand.go
56
app/brand.go
@@ -4,23 +4,60 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
"image/png"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
const (
|
||||
BRAND_FILE_PATH = "brand/"
|
||||
BRAND_FILE_NAME = "image.png"
|
||||
)
|
||||
|
||||
func (a *App) SaveBrandImage(imageData *multipart.FileHeader) *model.AppError {
|
||||
if len(*a.Config().FileSettings.DriverName) == 0 {
|
||||
return model.NewAppError("SaveBrandImage", "api.admin.upload_brand_image.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
if a.Brand == nil {
|
||||
return model.NewAppError("SaveBrandImage", "api.admin.upload_brand_image.not_available.app_error", nil, "", http.StatusNotImplemented)
|
||||
file, err := imageData.Open()
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
return model.NewAppError("SaveBrandImage", "brand.save_brand_image.open.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if err := a.Brand.SaveBrandImage(imageData); err != nil {
|
||||
return err
|
||||
// Decode image config first to check dimensions before loading the whole thing into memory later on
|
||||
config, _, err := image.DecodeConfig(file)
|
||||
if err != nil {
|
||||
return model.NewAppError("SaveBrandImage", "brand.save_brand_image.decode_config.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
} else if config.Width*config.Height > model.MaxImageSize {
|
||||
return model.NewAppError("SaveBrandImage", "brand.save_brand_image.too_large.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
file.Seek(0, 0)
|
||||
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
return model.NewAppError("SaveBrandImage", "brand.save_brand_image.decode.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err = png.Encode(buf, img)
|
||||
if err != nil {
|
||||
return model.NewAppError("SaveBrandImage", "brand.save_brand_image.encode.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
a.MoveFile(BRAND_FILE_PATH+BRAND_FILE_NAME, BRAND_FILE_PATH+t.Format("2006-01-02T15:04:05")+".png")
|
||||
|
||||
if _, err := a.WriteFile(buf, BRAND_FILE_PATH+BRAND_FILE_NAME); err != nil {
|
||||
return model.NewAppError("SaveBrandImage", "brand.save_brand_image.save_image.app_error", nil, "", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -31,13 +68,10 @@ func (a *App) GetBrandImage() ([]byte, *model.AppError) {
|
||||
return nil, model.NewAppError("GetBrandImage", "api.admin.get_brand_image.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
if a.Brand == nil {
|
||||
return nil, model.NewAppError("GetBrandImage", "api.admin.get_brand_image.not_available.app_error", nil, "", http.StatusNotImplemented)
|
||||
img, err := a.ReadFile(BRAND_FILE_PATH + BRAND_FILE_NAME)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if img, err := a.Brand.GetBrandImage(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return img, nil
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package einterfaces
|
||||
|
||||
import (
|
||||
"mime/multipart"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
type BrandInterface interface {
|
||||
SaveBrandImage(*multipart.FileHeader) *model.AppError
|
||||
GetBrandImage() ([]byte, *model.AppError)
|
||||
}
|
||||
@@ -46,7 +46,6 @@ type Features struct {
|
||||
Compliance *bool `json:"compliance"`
|
||||
Cluster *bool `json:"cluster"`
|
||||
Metrics *bool `json:"metrics"`
|
||||
CustomBrand *bool `json:"custom_brand"`
|
||||
MHPNS *bool `json:"mhpns"`
|
||||
SAML *bool `json:"saml"`
|
||||
Elasticsearch *bool `json:"elastic_search"`
|
||||
@@ -70,7 +69,6 @@ func (f *Features) ToMap() map[string]interface{} {
|
||||
"compliance": *f.Compliance,
|
||||
"cluster": *f.Cluster,
|
||||
"metrics": *f.Metrics,
|
||||
"custom_brand": *f.CustomBrand,
|
||||
"mhpns": *f.MHPNS,
|
||||
"saml": *f.SAML,
|
||||
"elastic_search": *f.Elasticsearch,
|
||||
@@ -119,10 +117,6 @@ func (f *Features) SetDefaults() {
|
||||
f.Metrics = NewBool(*f.FutureFeatures)
|
||||
}
|
||||
|
||||
if f.CustomBrand == nil {
|
||||
f.CustomBrand = NewBool(*f.FutureFeatures)
|
||||
}
|
||||
|
||||
if f.MHPNS == nil {
|
||||
f.MHPNS = NewBool(*f.FutureFeatures)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ func TestLicenseFeaturesToMap(t *testing.T) {
|
||||
CheckTrue(t, m["compliance"].(bool))
|
||||
CheckTrue(t, m["cluster"].(bool))
|
||||
CheckTrue(t, m["metrics"].(bool))
|
||||
CheckTrue(t, m["custom_brand"].(bool))
|
||||
CheckTrue(t, m["mhpns"].(bool))
|
||||
CheckTrue(t, m["saml"].(bool))
|
||||
CheckTrue(t, m["elastic_search"].(bool))
|
||||
@@ -44,7 +43,6 @@ func TestLicenseFeaturesSetDefaults(t *testing.T) {
|
||||
CheckTrue(t, *f.Compliance)
|
||||
CheckTrue(t, *f.Cluster)
|
||||
CheckTrue(t, *f.Metrics)
|
||||
CheckTrue(t, *f.CustomBrand)
|
||||
CheckTrue(t, *f.MHPNS)
|
||||
CheckTrue(t, *f.SAML)
|
||||
CheckTrue(t, *f.Elasticsearch)
|
||||
@@ -66,7 +64,6 @@ func TestLicenseFeaturesSetDefaults(t *testing.T) {
|
||||
*f.Compliance = true
|
||||
*f.Cluster = true
|
||||
*f.Metrics = true
|
||||
*f.CustomBrand = true
|
||||
*f.MHPNS = true
|
||||
*f.SAML = true
|
||||
*f.Elasticsearch = true
|
||||
@@ -85,7 +82,6 @@ func TestLicenseFeaturesSetDefaults(t *testing.T) {
|
||||
CheckTrue(t, *f.Compliance)
|
||||
CheckTrue(t, *f.Cluster)
|
||||
CheckTrue(t, *f.Metrics)
|
||||
CheckTrue(t, *f.CustomBrand)
|
||||
CheckTrue(t, *f.MHPNS)
|
||||
CheckTrue(t, *f.SAML)
|
||||
CheckTrue(t, *f.Elasticsearch)
|
||||
@@ -169,7 +165,6 @@ func TestLicenseToFromJson(t *testing.T) {
|
||||
CheckBool(t, *f1.Compliance, *f.Compliance)
|
||||
CheckBool(t, *f1.Cluster, *f.Cluster)
|
||||
CheckBool(t, *f1.Metrics, *f.Metrics)
|
||||
CheckBool(t, *f1.CustomBrand, *f.CustomBrand)
|
||||
CheckBool(t, *f1.MHPNS, *f.MHPNS)
|
||||
CheckBool(t, *f1.SAML, *f.SAML)
|
||||
CheckBool(t, *f1.Elasticsearch, *f.Elasticsearch)
|
||||
|
||||
@@ -612,18 +612,15 @@ func GenerateClientConfig(c *model.Config, diagnosticId string, license *model.L
|
||||
props["PasswordRequireNumber"] = strconv.FormatBool(*c.PasswordSettings.Number)
|
||||
props["PasswordRequireSymbol"] = strconv.FormatBool(*c.PasswordSettings.Symbol)
|
||||
props["CustomUrlSchemes"] = strings.Join(*c.DisplaySettings.CustomUrlSchemes, ",")
|
||||
props["EnableCustomBrand"] = strconv.FormatBool(*c.TeamSettings.EnableCustomBrand)
|
||||
props["CustomBrandText"] = *c.TeamSettings.CustomBrandText
|
||||
props["CustomDescriptionText"] = *c.TeamSettings.CustomDescriptionText
|
||||
|
||||
if license != nil {
|
||||
props["ExperimentalHideTownSquareinLHS"] = strconv.FormatBool(*c.TeamSettings.ExperimentalHideTownSquareinLHS)
|
||||
props["ExperimentalTownSquareIsReadOnly"] = strconv.FormatBool(*c.TeamSettings.ExperimentalTownSquareIsReadOnly)
|
||||
props["ExperimentalEnableAuthenticationTransfer"] = strconv.FormatBool(*c.ServiceSettings.ExperimentalEnableAuthenticationTransfer)
|
||||
|
||||
if *license.Features.CustomBrand {
|
||||
props["EnableCustomBrand"] = strconv.FormatBool(*c.TeamSettings.EnableCustomBrand)
|
||||
props["CustomBrandText"] = *c.TeamSettings.CustomBrandText
|
||||
props["CustomDescriptionText"] = *c.TeamSettings.CustomDescriptionText
|
||||
}
|
||||
|
||||
if *license.Features.LDAP {
|
||||
props["EnableLdap"] = strconv.FormatBool(*c.LdapSettings.Enable)
|
||||
props["LdapLoginFieldName"] = *c.LdapSettings.LoginFieldName
|
||||
|
||||
@@ -137,7 +137,6 @@ func GetClientLicense(l *model.License) map[string]string {
|
||||
props["GoogleOAuth"] = strconv.FormatBool(*l.Features.GoogleOAuth)
|
||||
props["Office365OAuth"] = strconv.FormatBool(*l.Features.Office365OAuth)
|
||||
props["Compliance"] = strconv.FormatBool(*l.Features.Compliance)
|
||||
props["CustomBrand"] = strconv.FormatBool(*l.Features.CustomBrand)
|
||||
props["MHPNS"] = strconv.FormatBool(*l.Features.MHPNS)
|
||||
props["Announcement"] = strconv.FormatBool(*l.Features.Announcement)
|
||||
props["Elasticsearch"] = strconv.FormatBool(*l.Features.Elasticsearch)
|
||||
|
||||
Ссылка в новой задаче
Block a user