From 8821fee6c270042dcd4346801ceb0e8d828ed733 Mon Sep 17 00:00:00 2001 From: Jason Blais Date: Tue, 3 Jul 2018 05:26:08 -0400 Subject: Disable Gfycat by default during Beta and move default keys to server (#9027) * Update default.json * Update diagnostics.go * Added default Gfycat API credentials to server * Fixed default Gfycat credentials --- app/diagnostics.go | 3 +++ 1 file changed, 3 insertions(+) (limited to 'app') diff --git a/app/diagnostics.go b/app/diagnostics.go index e9b3405be..6612093d4 100644 --- a/app/diagnostics.go +++ b/app/diagnostics.go @@ -208,6 +208,9 @@ func (a *App) trackConfig() { "enable_user_access_tokens": *cfg.ServiceSettings.EnableUserAccessTokens, "enable_custom_emoji": *cfg.ServiceSettings.EnableCustomEmoji, "enable_emoji_picker": *cfg.ServiceSettings.EnableEmojiPicker, + "enable_gif_picker": *cfg.ServiceSettings.EnableGifPicker, + "gfycat_api_key": isDefault(*cfg.ServiceSettings.GfycatApiKey, model.SERVICE_SETTINGS_DEFAULT_GFYCAT_API_KEY), + "gfycat_api_secret": isDefault(*cfg.ServiceSettings.GfycatApiSecret, model.SERVICE_SETTINGS_DEFAULT_GFYCAT_API_SECRET), "experimental_enable_authentication_transfer": *cfg.ServiceSettings.ExperimentalEnableAuthenticationTransfer, "restrict_custom_emoji_creation": *cfg.ServiceSettings.RestrictCustomEmojiCreation, "enable_testing": cfg.ServiceSettings.EnableTesting, -- cgit v1.2.3-1-g7c22 From 0e17babdd3175b3876c42adbac1df4c51f4f9a12 Mon Sep 17 00:00:00 2001 From: George Goldberg Date: Tue, 3 Jul 2018 16:54:59 +0100 Subject: Make sure diagnostic ID is loaded before client config. (#9032) --- app/app.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/app.go b/app/app.go index 911d359ae..96b9b6d13 100644 --- a/app/app.go +++ b/app/app.go @@ -184,7 +184,6 @@ func New(options ...Option) (outApp *App, outErr error) { }) }) - app.regenerateClientConfig() mlog.Info("Server is initializing...") @@ -207,6 +206,9 @@ func New(options ...Option) (outApp *App, outErr error) { return nil, errors.Wrapf(err, "unable to ensure asymmetric signing key") } + app.EnsureDiagnosticId() + app.regenerateClientConfig() + app.initJobs() app.AddLicenseListener(func() { app.initJobs() -- cgit v1.2.3-1-g7c22 From 8d3ea1bbf6f6ef6164d26b6801c46cfe7f936fa1 Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Thu, 5 Jul 2018 12:18:26 +0500 Subject: MM-10766 - Replacing default profile image font (#8955) * Updating default profile pic font * Updating profile image font * Updating test * Use new default font if configured for old one * Update OFL.txt --- app/user.go | 25 ++++++++++++++++++------- app/user_test.go | 2 +- 2 files changed, 19 insertions(+), 8 deletions(-) (limited to 'app') diff --git a/app/user.go b/app/user.go index b00ef19ef..acd3ee9aa 100644 --- a/app/user.go +++ b/app/user.go @@ -24,6 +24,7 @@ import ( "github.com/disintegration/imaging" "github.com/golang/freetype" + "github.com/golang/freetype/truetype" "github.com/mattermost/mattermost-server/einterfaces" "github.com/mattermost/mattermost-server/mlog" "github.com/mattermost/mattermost-server/model" @@ -696,12 +697,7 @@ func CreateProfileImage(username string, userId string, initialFont string) ([]b initial := string(strings.ToUpper(username)[0]) - fontDir, _ := utils.FindDir("fonts") - fontBytes, err := ioutil.ReadFile(filepath.Join(fontDir, initialFont)) - if err != nil { - return nil, model.NewAppError("CreateProfileImage", "api.user.create_profile_image.default_font.app_error", nil, err.Error(), http.StatusInternalServerError) - } - font, err := freetype.ParseFont(fontBytes) + font, err := getFont(initialFont) if err != nil { return nil, model.NewAppError("CreateProfileImage", "api.user.create_profile_image.default_font.app_error", nil, err.Error(), http.StatusInternalServerError) } @@ -719,7 +715,7 @@ func CreateProfileImage(username string, userId string, initialFont string) ([]b c.SetDst(dstImg) c.SetSrc(srcImg) - pt := freetype.Pt(IMAGE_PROFILE_PIXEL_DIMENSION/6, IMAGE_PROFILE_PIXEL_DIMENSION*2/3) + pt := freetype.Pt(IMAGE_PROFILE_PIXEL_DIMENSION/5, IMAGE_PROFILE_PIXEL_DIMENSION*2/3) _, err = c.DrawString(initial, pt) if err != nil { return nil, model.NewAppError("CreateProfileImage", "api.user.create_profile_image.initial.app_error", nil, err.Error(), http.StatusInternalServerError) @@ -734,6 +730,21 @@ func CreateProfileImage(username string, userId string, initialFont string) ([]b } } +func getFont(initialFont string) (*truetype.Font, error) { + // Some people have the old default font still set, so just treat that as if they're using the new default + if initialFont == "luximbi.ttf" { + initialFont = "nunito-bold.ttf" + } + + fontDir, _ := utils.FindDir("fonts") + fontBytes, err := ioutil.ReadFile(filepath.Join(fontDir, initialFont)) + if err != nil { + return nil, err + } + + return freetype.ParseFont(fontBytes) +} + func (a *App) GetProfileImage(user *model.User) ([]byte, bool, *model.AppError) { var img []byte readFailed := false diff --git a/app/user_test.go b/app/user_test.go index 7952eaa1f..959455121 100644 --- a/app/user_test.go +++ b/app/user_test.go @@ -97,7 +97,7 @@ func TestCreateOAuthUser(t *testing.T) { } func TestCreateProfileImage(t *testing.T) { - b, err := CreateProfileImage("Corey Hulen", "eo1zkdr96pdj98pjmq8zy35wba", "luximbi.ttf") + b, err := CreateProfileImage("Corey Hulen", "eo1zkdr96pdj98pjmq8zy35wba", "nunito-bold.ttf") if err != nil { t.Fatal(err) } -- cgit v1.2.3-1-g7c22 From 6299af0fa54a9cd658e0d7fb1e5552746830cebf Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Thu, 5 Jul 2018 09:08:49 -0400 Subject: Add ability to bulk import emoji (#9048) * Add ability to bulk import emoji * Improve error handling * Update test config --- app/import.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++ app/import_test.go | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 156 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/import.go b/app/import.go index baf936567..12353d562 100644 --- a/app/import.go +++ b/app/import.go @@ -33,6 +33,7 @@ type LineImportData struct { Post *PostImportData `json:"post"` DirectChannel *DirectChannelImportData `json:"direct_channel"` DirectPost *DirectPostImportData `json:"direct_post"` + Emoji *EmojiImportData `json:"emoji"` Version *int `json:"version"` } @@ -114,6 +115,11 @@ type UserChannelNotifyPropsImportData struct { MarkUnread *string `json:"mark_unread"` } +type EmojiImportData struct { + Name *string `json:"name"` + Image *string `json:"image"` +} + type ReactionImportData struct { User *string `json:"user"` CreateAt *int64 `json:"create_at"` @@ -337,6 +343,12 @@ func (a *App) ImportLine(line LineImportData, dryRun bool) *model.AppError { } else { return a.ImportDirectPost(line.DirectPost, dryRun) } + case line.Type == "emoji": + if line.Emoji == nil { + return model.NewAppError("BulkImport", "app.import.import_line.null_emoji.error", nil, "", http.StatusBadRequest) + } else { + return a.ImportEmoji(line.Emoji, dryRun) + } default: return model.NewAppError("BulkImport", "app.import.import_line.unknown_line_type.error", map[string]interface{}{"Type": line.Type}, "", http.StatusBadRequest) } @@ -1925,6 +1937,71 @@ func validateDirectPostImportData(data *DirectPostImportData, maxPostSize int) * return nil } +func (a *App) ImportEmoji(data *EmojiImportData, dryRun bool) *model.AppError { + if err := validateEmojiImportData(data); err != nil { + return err + } + + // If this is a Dry Run, do not continue any further. + if dryRun { + return nil + } + + var emoji *model.Emoji + var err *model.AppError + + emoji, err = a.GetEmojiByName(*data.Name) + if err != nil && err.StatusCode != http.StatusNotFound { + return err + } + + alreadyExists := emoji != nil + + if !alreadyExists { + emoji = &model.Emoji{ + Name: *data.Name, + } + emoji.PreSave() + } + + file, fileErr := os.Open(*data.Image) + if fileErr != nil { + return model.NewAppError("BulkImport", "app.import.emoji.bad_file.error", map[string]interface{}{"EmojiName": *data.Name}, "", http.StatusBadRequest) + } + + if _, err := a.WriteFile(file, getEmojiImagePath(emoji.Id)); err != nil { + return err + } + + if !alreadyExists { + if result := <-a.Srv.Store.Emoji().Save(emoji); result.Err != nil { + return result.Err + } + } + + return nil +} + +func validateEmojiImportData(data *EmojiImportData) *model.AppError { + if data == nil { + return model.NewAppError("BulkImport", "app.import.validate_emoji_import_data.empty.error", nil, "", http.StatusBadRequest) + } + + if data.Name == nil || len(*data.Name) == 0 { + return model.NewAppError("BulkImport", "app.import.validate_emoji_import_data.name_missing.error", nil, "", http.StatusBadRequest) + } + + if err := model.IsValidEmojiName(*data.Name); err != nil { + return err + } + + if data.Image == nil || len(*data.Image) == 0 { + return model.NewAppError("BulkImport", "app.import.validate_emoji_import_data.image_missing.error", nil, "", http.StatusBadRequest) + } + + return nil +} + // // -- Old SlackImport Functions -- // Import functions are sutible for entering posts and users into the database without diff --git a/app/import_test.go b/app/import_test.go index e7bc055a4..8a88937f9 100644 --- a/app/import_test.go +++ b/app/import_test.go @@ -3774,11 +3774,16 @@ func TestImportBulkImport(t *testing.T) { th := Setup() defer th.TearDown() + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = true }) + teamName := model.NewId() channelName := model.NewId() username := model.NewId() username2 := model.NewId() username3 := model.NewId() + emojiName := model.NewId() + testsDir, _ := utils.FindDir("tests") + testImage := filepath.Join(testsDir, "test.png") // Run bulk import with a valid 1 of everything. data1 := `{"type": "version", "version": 1} @@ -3791,7 +3796,8 @@ func TestImportBulkImport(t *testing.T) { {"type": "direct_channel", "direct_channel": {"members": ["` + username + `", "` + username2 + `"]}} {"type": "direct_channel", "direct_channel": {"members": ["` + username + `", "` + username2 + `", "` + username3 + `"]}} {"type": "direct_post", "direct_post": {"channel_members": ["` + username + `", "` + username2 + `"], "user": "` + username + `", "message": "Hello Direct Channel", "create_at": 123456789013}} -{"type": "direct_post", "direct_post": {"channel_members": ["` + username + `", "` + username2 + `", "` + username3 + `"], "user": "` + username + `", "message": "Hello Group Channel", "create_at": 123456789014}}` +{"type": "direct_post", "direct_post": {"channel_members": ["` + username + `", "` + username2 + `", "` + username3 + `"], "user": "` + username + `", "message": "Hello Group Channel", "create_at": 123456789014}} +{"type": "emoji", "emoji": {"name": "` + emojiName + `", "image": "` + testImage + `"}}` if err, line := th.App.BulkImport(strings.NewReader(data1), false, 2); err != nil || line != 0 { t.Fatalf("BulkImport should have succeeded: %v, %v", err.Error(), line) @@ -3833,3 +3839,75 @@ func TestImportProcessImportDataFileVersionLine(t *testing.T) { t.Fatalf("Expected error on invalid version line.") } } + +func TestImportValidateEmojiImportData(t *testing.T) { + data := EmojiImportData{ + Name: ptrStr("parrot"), + Image: ptrStr("/path/to/image"), + } + + err := validateEmojiImportData(&data) + assert.Nil(t, err, "Validation should succeed") + + *data.Name = "smiley" + err = validateEmojiImportData(&data) + assert.NotNil(t, err) + + *data.Name = "" + err = validateEmojiImportData(&data) + assert.NotNil(t, err) + + *data.Name = "" + *data.Image = "" + err = validateEmojiImportData(&data) + assert.NotNil(t, err) + + *data.Image = "/path/to/image" + data.Name = nil + err = validateEmojiImportData(&data) + assert.NotNil(t, err) + + data.Name = ptrStr("parrot") + data.Image = nil + err = validateEmojiImportData(&data) + assert.NotNil(t, err) +} + +func TestImportImportEmoji(t *testing.T) { + th := Setup() + defer th.TearDown() + + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = true }) + + testsDir, _ := utils.FindDir("tests") + testImage := filepath.Join(testsDir, "test.png") + + data := EmojiImportData{Name: ptrStr(model.NewId())} + err := th.App.ImportEmoji(&data, true) + assert.NotNil(t, err, "Invalid emoji should have failed dry run") + + result := <-th.App.Srv.Store.Emoji().GetByName(*data.Name) + assert.Nil(t, result.Data, "Emoji should not have been imported") + + data.Image = ptrStr(testImage) + err = th.App.ImportEmoji(&data, true) + assert.Nil(t, err, "Valid emoji should have passed dry run") + + data = EmojiImportData{Name: ptrStr(model.NewId())} + err = th.App.ImportEmoji(&data, false) + assert.NotNil(t, err, "Invalid emoji should have failed apply mode") + + data.Image = ptrStr("non-existent-file") + err = th.App.ImportEmoji(&data, false) + assert.NotNil(t, err, "Emoji with bad image file should have failed apply mode") + + data.Image = ptrStr(testImage) + err = th.App.ImportEmoji(&data, false) + assert.Nil(t, err, "Valid emoji should have succeeded apply mode") + + result = <-th.App.Srv.Store.Emoji().GetByName(*data.Name) + assert.NotNil(t, result.Data, "Emoji should have been imported") + + err = th.App.ImportEmoji(&data, false) + assert.Nil(t, err, "Second run should have succeeded apply mode") +} -- cgit v1.2.3-1-g7c22 From 4de50ddfc3ef09ddc0a56c8ff9bf2429df524aa5 Mon Sep 17 00:00:00 2001 From: Declan Freeman-Gleason Date: Thu, 5 Jul 2018 06:45:08 -0700 Subject: MM-10603: Ignore Redirects and Other Changes when Displaying Link Previews (#9025) * Don't use redirected URL in link preview. * Only show preview if OG data is provided. --- app/post.go | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'app') diff --git a/app/post.go b/app/post.go index e24018995..806263f5f 100644 --- a/app/post.go +++ b/app/post.go @@ -765,6 +765,11 @@ func (a *App) GetOpenGraphMetadata(requestURL string) *opengraph.OpenGraph { makeOpenGraphURLsAbsolute(og, requestURL) + // The URL should be the link the user provided in their message, not a redirected one. + if og.URL != "" { + og.URL = requestURL + } + return og } -- cgit v1.2.3-1-g7c22 From 0896b5c64ef224f0f8835b9727d1c1b94cbe7c29 Mon Sep 17 00:00:00 2001 From: George Goldberg Date: Fri, 6 Jul 2018 09:07:36 +0100 Subject: MM-11106: Allow systeadmin webook to post to read only town square. (#9051) --- app/webhook.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/webhook.go b/app/webhook.go index c887fec97..8926c94a8 100644 --- a/app/webhook.go +++ b/app/webhook.go @@ -587,6 +587,8 @@ func (a *App) HandleIncomingWebhook(hookId string, req *model.IncomingWebhookReq hook = result.Data.(*model.IncomingWebhook) } + uchan := a.Srv.Store.User().Get(hook.UserId) + if len(req.Props) == 0 { req.Props = make(model.StringInterface) } @@ -637,8 +639,15 @@ func (a *App) HandleIncomingWebhook(hookId string, req *model.IncomingWebhookReq return model.NewAppError("HandleIncomingWebhook", "web.incoming_webhook.channel_locked.app_error", nil, "", http.StatusForbidden) } + var user *model.User + if result := <-uchan; result.Err != nil { + return model.NewAppError("HandleIncomingWebhook", "web.incoming_webhook.user.app_error", nil, "err="+result.Err.Message, http.StatusForbidden) + } else { + user = result.Data.(*model.User) + } + if a.License() != nil && *a.Config().TeamSettings.ExperimentalTownSquareIsReadOnly && - channel.Name == model.DEFAULT_CHANNEL { + channel.Name == model.DEFAULT_CHANNEL && !a.RolesGrantPermission(user.GetRoles(), model.PERMISSION_MANAGE_SYSTEM.Id) { return model.NewAppError("HandleIncomingWebhook", "api.post.create_post.town_square_read_only", nil, "", http.StatusForbidden) } -- cgit v1.2.3-1-g7c22