diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/config.go | 81 | ||||
-rw-r--r-- | utils/config_test.go | 114 | ||||
-rw-r--r-- | utils/html.go | 10 | ||||
-rw-r--r-- | utils/i18n.go | 12 | ||||
-rw-r--r-- | utils/license.go | 3 | ||||
-rw-r--r-- | utils/mail.go | 6 | ||||
-rw-r--r-- | utils/timezone.go | 25 |
7 files changed, 214 insertions, 37 deletions
diff --git a/utils/config.go b/utils/config.go index 8befef94d..35a21dea4 100644 --- a/utils/config.go +++ b/utils/config.go @@ -9,7 +9,6 @@ import ( "io" "io/ioutil" "os" - "path" "path/filepath" "strconv" "strings" @@ -51,21 +50,17 @@ func FindConfigFile(fileName string) (path string) { return "" } +// FindDir looks for the given directory in nearby ancestors, falling back to `./` if not found. func FindDir(dir string) (string, bool) { - fileName := "." - found := false - if _, err := os.Stat("./" + dir + "/"); err == nil { - fileName, _ = filepath.Abs("./" + dir + "/") - found = true - } else if _, err := os.Stat("../" + dir + "/"); err == nil { - fileName, _ = filepath.Abs("../" + dir + "/") - found = true - } else if _, err := os.Stat("../../" + dir + "/"); err == nil { - fileName, _ = filepath.Abs("../../" + dir + "/") - found = true + for _, parent := range []string{".", "..", "../.."} { + foundDir, err := filepath.Abs(filepath.Join(parent, dir)) + if err != nil { + continue + } else if _, err := os.Stat(foundDir); err == nil { + return foundDir, true + } } - - return fileName + "/", found + return "./", false } func DisableDebugLogForTest() { @@ -136,11 +131,10 @@ func ConfigureLog(s *model.LogSettings) { func GetLogFileLocation(fileLocation string) string { if fileLocation == "" { - logDir, _ := FindDir("logs") - return logDir + LOG_FILENAME - } else { - return path.Join(fileLocation, LOG_FILENAME) + fileLocation, _ = FindDir("logs") } + + return filepath.Join(fileLocation, LOG_FILENAME) } func SaveConfig(fileName string, config *model.Config) *model.AppError { @@ -414,8 +408,6 @@ func GenerateClientConfig(c *model.Config, diagnosticId string, license *model.L props["SupportEmail"] = *c.SupportSettings.SupportEmail props["EnableFileAttachments"] = strconv.FormatBool(*c.FileSettings.EnableFileAttachments) - props["EnableMobileFileUpload"] = strconv.FormatBool(*c.FileSettings.EnableMobileUpload) - props["EnableMobileFileDownload"] = strconv.FormatBool(*c.FileSettings.EnableMobileDownload) props["EnablePublicLink"] = strconv.FormatBool(c.FileSettings.EnablePublicLink) props["WebsocketPort"] = fmt.Sprintf("%v", *c.ServiceSettings.WebsocketPort) @@ -449,8 +441,55 @@ func GenerateClientConfig(c *model.Config, diagnosticId string, license *model.L hasImageProxy := c.ServiceSettings.ImageProxyType != nil && *c.ServiceSettings.ImageProxyType != "" && c.ServiceSettings.ImageProxyURL != nil && *c.ServiceSettings.ImageProxyURL != "" props["HasImageProxy"] = strconv.FormatBool(hasImageProxy) + // Set default values for all options that require a license. + props["ExperimentalTownSquareIsReadOnly"] = "false" + props["ExperimentalEnableAuthenticationTransfer"] = "true" + props["EnableCustomBrand"] = "false" + props["CustomBrandText"] = "" + props["CustomDescriptionText"] = "" + props["EnableLdap"] = "false" + props["LdapLoginFieldName"] = "" + props["LdapNicknameAttributeSet"] = "false" + props["LdapFirstNameAttributeSet"] = "false" + props["LdapLastNameAttributeSet"] = "false" + props["LdapLoginButtonColor"] = "" + props["LdapLoginButtonBorderColor"] = "" + props["LdapLoginButtonTextColor"] = "" + props["EnableMultifactorAuthentication"] = "false" + props["EnforceMultifactorAuthentication"] = "false" + props["EnableCompliance"] = "false" + props["EnableMobileFileDownload"] = "true" + props["EnableMobileFileUpload"] = "true" + props["EnableSaml"] = "false" + props["SamlLoginButtonText"] = "" + props["SamlFirstNameAttributeSet"] = "false" + props["SamlLastNameAttributeSet"] = "false" + props["SamlNicknameAttributeSet"] = "false" + props["SamlLoginButtonColor"] = "" + props["SamlLoginButtonBorderColor"] = "" + props["SamlLoginButtonTextColor"] = "" + props["EnableCluster"] = "false" + props["EnableMetrics"] = "false" + props["EnableSignUpWithGoogle"] = "false" + props["EnableSignUpWithOffice365"] = "false" + props["PasswordMinimumLength"] = "0" + props["PasswordRequireLowercase"] = "false" + props["PasswordRequireUppercase"] = "false" + props["PasswordRequireNumber"] = "false" + props["PasswordRequireSymbol"] = "false" + props["EnableBanner"] = "false" + props["BannerText"] = "" + props["BannerColor"] = "" + props["BannerTextColor"] = "" + props["AllowBannerDismissal"] = "false" props["EnableThemeSelection"] = "true" + props["DefaultTheme"] = "" props["AllowCustomThemes"] = "true" + props["AllowedThemes"] = "" + props["DataRetentionEnableMessageDeletion"] = "false" + props["DataRetentionMessageRetentionDays"] = "0" + props["DataRetentionEnableFileDeletion"] = "false" + props["DataRetentionFileRetentionDays"] = "0" if license != nil { props["ExperimentalTownSquareIsReadOnly"] = strconv.FormatBool(*c.TeamSettings.ExperimentalTownSquareIsReadOnly) @@ -480,6 +519,8 @@ func GenerateClientConfig(c *model.Config, diagnosticId string, license *model.L if *license.Features.Compliance { props["EnableCompliance"] = strconv.FormatBool(*c.ComplianceSettings.Enable) + props["EnableMobileFileDownload"] = strconv.FormatBool(*c.FileSettings.EnableMobileDownload) + props["EnableMobileFileUpload"] = strconv.FormatBool(*c.FileSettings.EnableMobileUpload) } if *license.Features.SAML { diff --git a/utils/config_test.go b/utils/config_test.go index 5809422f1..f816e2ee8 100644 --- a/utils/config_test.go +++ b/utils/config_test.go @@ -12,6 +12,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/mattermost/mattermost-server/model" ) func TestConfig(t *testing.T) { @@ -21,6 +23,15 @@ func TestConfig(t *testing.T) { InitTranslations(cfg.LocalizationSettings) } +func TestTimezoneConfig(t *testing.T) { + TranslationsPreInit() + supportedTimezones := LoadTimezones("timezones.json") + assert.Equal(t, len(supportedTimezones) > 0, true) + + supportedTimezones2 := LoadTimezones("timezones_file_does_not_exists.json") + assert.Equal(t, len(supportedTimezones2) > 0, true) +} + func TestFindConfigFile(t *testing.T) { dir, err := ioutil.TempDir("", "") require.NoError(t, err) @@ -193,14 +204,97 @@ func TestValidateLocales(t *testing.T) { } func TestGetClientConfig(t *testing.T) { - TranslationsPreInit() - cfg, _, err := LoadConfig("config.json") - require.Nil(t, err) + t.Parallel() + testCases := []struct { + description string + config *model.Config + diagnosticId string + license *model.License + expectedFields map[string]string + }{ + { + "unlicensed", + &model.Config{ + EmailSettings: model.EmailSettings{ + EmailNotificationContentsType: sToP(model.EMAIL_NOTIFICATION_CONTENTS_FULL), + }, + ThemeSettings: model.ThemeSettings{ + // Ignored, since not licensed. + AllowCustomThemes: bToP(false), + }, + }, + "", + nil, + map[string]string{ + "DiagnosticId": "", + "EmailNotificationContentsType": "full", + "AllowCustomThemes": "true", + }, + }, + { + "licensed, but not for theme management", + &model.Config{ + EmailSettings: model.EmailSettings{ + EmailNotificationContentsType: sToP(model.EMAIL_NOTIFICATION_CONTENTS_FULL), + }, + ThemeSettings: model.ThemeSettings{ + // Ignored, since not licensed. + AllowCustomThemes: bToP(false), + }, + }, + "tag1", + &model.License{ + Features: &model.Features{ + ThemeManagement: bToP(false), + }, + }, + map[string]string{ + "DiagnosticId": "tag1", + "EmailNotificationContentsType": "full", + "AllowCustomThemes": "true", + }, + }, + { + "licensed for theme management", + &model.Config{ + EmailSettings: model.EmailSettings{ + EmailNotificationContentsType: sToP(model.EMAIL_NOTIFICATION_CONTENTS_FULL), + }, + ThemeSettings: model.ThemeSettings{ + AllowCustomThemes: bToP(false), + }, + }, + "tag2", + &model.License{ + Features: &model.Features{ + ThemeManagement: bToP(true), + }, + }, + map[string]string{ + "DiagnosticId": "tag2", + "EmailNotificationContentsType": "full", + "AllowCustomThemes": "false", + }, + }, + } - configMap := GenerateClientConfig(cfg, "", nil) - if configMap["EmailNotificationContentsType"] != *cfg.EmailSettings.EmailNotificationContentsType { - t.Fatal("EmailSettings.EmailNotificationContentsType not exposed to client config") + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.description, func(t *testing.T) { + t.Parallel() + + testCase.config.SetDefaults() + if testCase.license != nil { + testCase.license.Features.SetDefaults() + } + + configMap := GenerateClientConfig(testCase.config, testCase.diagnosticId, testCase.license) + for expectedField, expectedValue := range testCase.expectedFields { + assert.Equal(t, expectedValue, configMap[expectedField]) + } + }) } + } func TestReadConfig(t *testing.T) { @@ -213,3 +307,11 @@ func TestReadConfig(t *testing.T) { assert.Equal(t, "http://foo.bar", *config.ServiceSettings.SiteURL) } + +func sToP(s string) *string { + return &s +} + +func bToP(b bool) *bool { + return &b +} diff --git a/utils/html.go b/utils/html.go index 6bbe55c6d..f9a7abe5b 100644 --- a/utils/html.go +++ b/utils/html.go @@ -5,8 +5,10 @@ package utils import ( "bytes" + "errors" "html/template" "io" + "path/filepath" "reflect" "sync/atomic" @@ -39,7 +41,7 @@ func NewHTMLTemplateWatcher(directory string) (*HTMLTemplateWatcher, error) { return nil, err } - if htmlTemplates, err := template.ParseGlob(templatesDir + "*.html"); err != nil { + if htmlTemplates, err := template.ParseGlob(filepath.Join(templatesDir, "*.html")); err != nil { return nil, err } else { ret.templates.Store(htmlTemplates) @@ -56,7 +58,7 @@ func NewHTMLTemplateWatcher(directory string) (*HTMLTemplateWatcher, error) { case event := <-watcher.Events: if event.Op&fsnotify.Write == fsnotify.Write { l4g.Info("Re-parsing templates because of modified file %v", event.Name) - if htmlTemplates, err := template.ParseGlob(templatesDir + "*.html"); err != nil { + if htmlTemplates, err := template.ParseGlob(filepath.Join(templatesDir, "*.html")); err != nil { l4g.Error("Failed to parse templates %v", err) } else { ret.templates.Store(htmlTemplates) @@ -103,6 +105,10 @@ func (t *HTMLTemplate) Render() string { } func (t *HTMLTemplate) RenderToWriter(w io.Writer) error { + if t.Templates == nil { + return errors.New("no html templates") + } + if err := t.Templates.ExecuteTemplate(w, t.TemplateName, t); err != nil { l4g.Error(T("api.api.render.error"), t.TemplateName, err) return err diff --git a/utils/i18n.go b/utils/i18n.go index 8ed82d19f..7b8d1fef0 100644 --- a/utils/i18n.go +++ b/utils/i18n.go @@ -23,13 +23,15 @@ var settings model.LocalizationSettings // this functions loads translations from filesystem // and assign english while loading server config func TranslationsPreInit() error { + // Set T even if we fail to load the translations. Lots of shutdown handling code will + // segfault trying to handle the error, and the untranslated IDs are strictly better. + T = TfuncWithFallback("en") + TDefault = TfuncWithFallback("en") + if err := InitTranslationsWithDir("i18n"); err != nil { return err } - T = TfuncWithFallback("en") - TDefault = TfuncWithFallback("en") - return nil } @@ -51,9 +53,9 @@ func InitTranslationsWithDir(dir string) error { for _, f := range files { if filepath.Ext(f.Name()) == ".json" { filename := f.Name() - locales[strings.Split(filename, ".")[0]] = i18nDirectory + filename + locales[strings.Split(filename, ".")[0]] = filepath.Join(i18nDirectory, filename) - if err := i18n.LoadTranslationFile(i18nDirectory + filename); err != nil { + if err := i18n.LoadTranslationFile(filepath.Join(i18nDirectory, filename)); err != nil { return err } } diff --git a/utils/license.go b/utils/license.go index 2853a58d0..cf874b62b 100644 --- a/utils/license.go +++ b/utils/license.go @@ -12,6 +12,7 @@ import ( "encoding/pem" "io/ioutil" "os" + "path/filepath" "strconv" "strings" @@ -114,7 +115,7 @@ func GetLicenseFileFromDisk(fileName string) []byte { func GetLicenseFileLocation(fileLocation string) string { if fileLocation == "" { configDir, _ := FindDir("config") - return configDir + "mattermost.mattermost-license" + return filepath.Join(configDir, "mattermost.mattermost-license") } else { return fileLocation } diff --git a/utils/mail.go b/utils/mail.go index c59406a18..2a2da9bf1 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -221,10 +221,10 @@ func SendMailUsingConfigAdvanced(mimeTo, smtpTo string, from mail.Address, subje return err } - return SendMail(c, mimeTo, smtpTo, from, subject, htmlBody, attachments, mimeHeaders, fileBackend) + return SendMail(c, mimeTo, smtpTo, from, subject, htmlBody, attachments, mimeHeaders, fileBackend, time.Now()) } -func SendMail(c *smtp.Client, mimeTo, smtpTo string, from mail.Address, subject, htmlBody string, attachments []*model.FileInfo, mimeHeaders map[string]string, fileBackend FileBackend) *model.AppError { +func SendMail(c *smtp.Client, mimeTo, smtpTo string, from mail.Address, subject, htmlBody string, attachments []*model.FileInfo, mimeHeaders map[string]string, fileBackend FileBackend, date time.Time) *model.AppError { l4g.Debug(T("utils.mail.send_mail.sending.debug"), mimeTo, subject) htmlMessage := "\r\n<html><body>" + htmlBody + "</body></html>" @@ -249,7 +249,7 @@ func SendMail(c *smtp.Client, mimeTo, smtpTo string, from mail.Address, subject, m := gomail.NewMessage(gomail.SetCharset("UTF-8")) m.SetHeaders(headers) - m.SetDateHeader("Date", time.Now()) + m.SetDateHeader("Date", date) m.SetBody("text/plain", txtBody) m.AddAlternative("text/html", htmlMessage) diff --git a/utils/timezone.go b/utils/timezone.go new file mode 100644 index 000000000..ea5f15140 --- /dev/null +++ b/utils/timezone.go @@ -0,0 +1,25 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package utils + +import ( + "encoding/json" + "io/ioutil" + + "github.com/mattermost/mattermost-server/model" +) + +func LoadTimezones(fileName string) model.SupportedTimezones { + var supportedTimezones model.SupportedTimezones + + if timezoneFile := FindConfigFile(fileName); timezoneFile == "" { + return model.DefaultSupportedTimezones + } else if raw, err := ioutil.ReadFile(timezoneFile); err != nil { + return model.DefaultSupportedTimezones + } else if err := json.Unmarshal(raw, &supportedTimezones); err != nil { + return model.DefaultSupportedTimezones + } else { + return supportedTimezones + } +} |