From 517faccc332ce48de43e597d1b2d29a3961241e7 Mon Sep 17 00:00:00 2001 From: FurmanovD <39990503+FurmanovD@users.noreply.github.com> Date: Mon, 17 Sep 2018 13:46:06 +0300 Subject: MM-11424 Extend bulk import to support themes across teams(#9305) (#9419) * MM-11424 Extend bulk import to support themes across teams(#9305) Also added: Advanced Settings: +'feature_enabled_markdown_preview' +'formatting' Sidebar Settings: +'show_unread_section' * MM-11424 (PR review) user teams' theme validator test updated * MM-11424 (PR review) added test with valid JSON of invalid theme(by structure) JSON string contains numeric and JSON object fields, not just a correct "string":"string" map --- app/import_functions.go | 44 +++++++++++++++++++++++++++++++++++++++++++ app/import_functions_test.go | 8 ++++++++ app/import_test.go | 6 ++++-- app/import_types.go | 28 +++++++++++++++------------ app/import_validators.go | 21 +++++++++++++++++++++ app/import_validators_test.go | 19 +++++++++++++++++++ 6 files changed, 112 insertions(+), 14 deletions(-) (limited to 'app') diff --git a/app/import_functions.go b/app/import_functions.go index ff22f23bb..98306d3a7 100644 --- a/app/import_functions.go +++ b/app/import_functions.go @@ -555,6 +555,33 @@ func (a *App) ImportUser(data *UserImportData, dryRun bool) *model.AppError { }) } + if data.UseMarkdownPreview != nil { + preferences = append(preferences, model.Preference{ + UserId: savedUser.Id, + Category: model.PREFERENCE_CATEGORY_ADVANCED_SETTINGS, + Name: "feature_enabled_markdown_preview", + Value: *data.UseMarkdownPreview, + }) + } + + if data.UseFormatting != nil { + preferences = append(preferences, model.Preference{ + UserId: savedUser.Id, + Category: model.PREFERENCE_CATEGORY_ADVANCED_SETTINGS, + Name: "formatting", + Value: *data.UseFormatting, + }) + } + + if data.ShowUnreadSection != nil { + preferences = append(preferences, model.Preference{ + UserId: savedUser.Id, + Category: model.PREFERENCE_CATEGORY_SIDEBAR_SETTINGS, + Name: "show_unread_section", + Value: *data.ShowUnreadSection, + }) + } + if len(preferences) > 0 { if result := <-a.Srv.Store.Preference().Save(&preferences); result.Err != nil { return model.NewAppError("BulkImport", "app.import.import_user.save_preferences.error", nil, result.Err.Error(), http.StatusInternalServerError) @@ -569,12 +596,23 @@ func (a *App) ImportUserTeams(user *model.User, data *[]UserTeamImportData) *mod return nil } + var teamThemePreferences model.Preferences for _, tdata := range *data { team, err := a.GetTeamByName(*tdata.Name) if err != nil { return err } + // Team-specific theme Preferences. + if tdata.Theme != nil { + teamThemePreferences = append(teamThemePreferences, model.Preference{ + UserId: user.Id, + Category: model.PREFERENCE_CATEGORY_THEME, + Name: team.Id, + Value: *tdata.Theme, + }) + } + var roles string isSchemeUser := true isSchemeAdmin := false @@ -622,6 +660,12 @@ func (a *App) ImportUserTeams(user *model.User, data *[]UserTeamImportData) *mod } } + if len(teamThemePreferences) > 0 { + if result := <-a.Srv.Store.Preference().Save(&teamThemePreferences); result.Err != nil { + return model.NewAppError("BulkImport", "app.import.import_user_teams.save_preferences.error", nil, result.Err.Error(), http.StatusInternalServerError) + } + } + return nil } diff --git a/app/import_functions_test.go b/app/import_functions_test.go index 10b1d5587..c76915ac6 100644 --- a/app/import_functions_test.go +++ b/app/import_functions_test.go @@ -1016,6 +1016,7 @@ func TestImportImportUser(t *testing.T) { data.Teams = &[]UserTeamImportData{ { Name: &teamName, + Theme: ptrStr(`{"awayIndicator":"#DBBD4E","buttonBg":"#23A1FF","buttonColor":"#FFFFFF","centerChannelBg":"#ffffff","centerChannelColor":"#333333","codeTheme":"github","image":"/static/files/a4a388b38b32678e83823ef1b3e17766.png","linkColor":"#2389d7","mentionBg":"#2389d7","mentionColor":"#ffffff","mentionHighlightBg":"#fff2bb","mentionHighlightLink":"#2f81b7","newMessageSeparator":"#FF8800","onlineIndicator":"#7DBE00","sidebarBg":"#fafafa","sidebarHeaderBg":"#3481B9","sidebarHeaderTextColor":"#ffffff","sidebarText":"#333333","sidebarTextActiveBorder":"#378FD2","sidebarTextActiveColor":"#111111","sidebarTextHoverBg":"#e6f2fa","sidebarUnreadText":"#333333","type":"Mattermost"}`), Roles: ptrStr("team_user team_admin"), Channels: &[]UserChannelImportData{ { @@ -1049,6 +1050,7 @@ func TestImportImportUser(t *testing.T) { } checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL, channel.Id, "true") + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_THEME, team.Id, *(*data.Teams)[0].Theme) // No more new member objects. if tmc, err := th.App.GetTeamMembers(team.Id, 0, 1000); err != nil { @@ -1074,6 +1076,9 @@ func TestImportImportUser(t *testing.T) { MessageDisplay: ptrStr("compact"), ChannelDisplayMode: ptrStr("centered"), TutorialStep: ptrStr("3"), + UseMarkdownPreview: ptrStr("true"), + UseFormatting: ptrStr("true"), + ShowUnreadSection: ptrStr("true"), } if err := th.App.ImportUser(&data, false); err != nil { t.Fatalf("Should have succeeded.") @@ -1091,6 +1096,9 @@ func TestImportImportUser(t *testing.T) { checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, "message_display", *data.MessageDisplay) checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, "channel_display_mode", *data.ChannelDisplayMode) checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_TUTORIAL_STEPS, user.Id, *data.TutorialStep) + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_ADVANCED_SETTINGS, "feature_enabled_markdown_preview", *data.UseMarkdownPreview) + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_ADVANCED_SETTINGS, "formatting", *data.UseFormatting) + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_SIDEBAR_SETTINGS, "show_unread_section", *data.ShowUnreadSection) // Change those preferences. data = UserImportData{ diff --git a/app/import_test.go b/app/import_test.go index 839c46bb8..89c8344e6 100644 --- a/app/import_test.go +++ b/app/import_test.go @@ -173,13 +173,15 @@ func TestImportBulkImport(t *testing.T) { emojiName := model.NewId() testsDir, _ := utils.FindDir("tests") testImage := filepath.Join(testsDir, "test.png") + teamTheme1 := `{\"awayIndicator\":\"#DBBD4E\",\"buttonBg\":\"#23A1FF\",\"buttonColor\":\"#FFFFFF\",\"centerChannelBg\":\"#ffffff\",\"centerChannelColor\":\"#333333\",\"codeTheme\":\"github\",\"image\":\"/static/files/a4a388b38b32678e83823ef1b3e17766.png\",\"linkColor\":\"#2389d7\",\"mentionBg\":\"#2389d7\",\"mentionColor\":\"#ffffff\",\"mentionHighlightBg\":\"#fff2bb\",\"mentionHighlightLink\":\"#2f81b7\",\"newMessageSeparator\":\"#FF8800\",\"onlineIndicator\":\"#7DBE00\",\"sidebarBg\":\"#fafafa\",\"sidebarHeaderBg\":\"#3481B9\",\"sidebarHeaderTextColor\":\"#ffffff\",\"sidebarText\":\"#333333\",\"sidebarTextActiveBorder\":\"#378FD2\",\"sidebarTextActiveColor\":\"#111111\",\"sidebarTextHoverBg\":\"#e6f2fa\",\"sidebarUnreadText\":\"#333333\",\"type\":\"Mattermost\"}` + teamTheme2 := `{\"awayIndicator\":\"#DBBD4E\",\"buttonBg\":\"#23A100\",\"buttonColor\":\"#EEEEEE\",\"centerChannelBg\":\"#ffffff\",\"centerChannelColor\":\"#333333\",\"codeTheme\":\"github\",\"image\":\"/static/files/a4a388b38b32678e83823ef1b3e17766.png\",\"linkColor\":\"#2389d7\",\"mentionBg\":\"#2389d7\",\"mentionColor\":\"#ffffff\",\"mentionHighlightBg\":\"#fff2bb\",\"mentionHighlightLink\":\"#2f81b7\",\"newMessageSeparator\":\"#FF8800\",\"onlineIndicator\":\"#7DBE00\",\"sidebarBg\":\"#fafafa\",\"sidebarHeaderBg\":\"#3481B9\",\"sidebarHeaderTextColor\":\"#ffffff\",\"sidebarText\":\"#333333\",\"sidebarTextActiveBorder\":\"#378FD2\",\"sidebarTextActiveColor\":\"#222222\",\"sidebarTextHoverBg\":\"#e6f2fa\",\"sidebarUnreadText\":\"#444444\",\"type\":\"Mattermost\"}` // Run bulk import with a valid 1 of everything. data1 := `{"type": "version", "version": 1} {"type": "team", "team": {"type": "O", "display_name": "lskmw2d7a5ao7ppwqh5ljchvr4", "name": "` + teamName + `"}} {"type": "channel", "channel": {"type": "O", "display_name": "xr6m6udffngark2uekvr3hoeny", "team": "` + teamName + `", "name": "` + channelName + `"}} -{"type": "user", "user": {"username": "` + username + `", "email": "` + username + `@example.com", "teams": [{"name": "` + teamName + `", "channels": [{"name": "` + channelName + `"}]}]}} -{"type": "user", "user": {"username": "` + username2 + `", "email": "` + username2 + `@example.com", "teams": [{"name": "` + teamName + `", "channels": [{"name": "` + channelName + `"}]}]}} +{"type": "user", "user": {"username": "` + username + `", "email": "` + username + `@example.com", "teams": [{"name": "` + teamName + `","theme": "` + teamTheme1 + `", "channels": [{"name": "` + channelName + `"}]}]}} +{"type": "user", "user": {"username": "` + username2 + `", "email": "` + username2 + `@example.com", "teams": [{"name": "` + teamName + `","theme": "` + teamTheme2 + `", "channels": [{"name": "` + channelName + `"}]}]}} {"type": "user", "user": {"username": "` + username3 + `", "email": "` + username3 + `@example.com", "teams": [{"name": "` + teamName + `", "channels": [{"name": "` + channelName + `"}]}]}} {"type": "post", "post": {"team": "` + teamName + `", "channel": "` + channelName + `", "user": "` + username + `", "message": "Hello World", "create_at": 123456789012, "attachments":[{"path": "` + testImage + `"}]}} {"type": "direct_channel", "direct_channel": {"members": ["` + username + `", "` + username2 + `"]}} diff --git a/app/import_types.go b/app/import_types.go index 7d53e5b16..168d43bab 100644 --- a/app/import_types.go +++ b/app/import_types.go @@ -40,18 +40,21 @@ type ChannelImportData struct { } type UserImportData struct { - ProfileImage *string `json:"profile_image"` - Username *string `json:"username"` - Email *string `json:"email"` - AuthService *string `json:"auth_service"` - AuthData *string `json:"auth_data"` - Password *string `json:"password"` - Nickname *string `json:"nickname"` - FirstName *string `json:"first_name"` - LastName *string `json:"last_name"` - Position *string `json:"position"` - Roles *string `json:"roles"` - Locale *string `json:"locale"` + ProfileImage *string `json:"profile_image"` + Username *string `json:"username"` + Email *string `json:"email"` + AuthService *string `json:"auth_service"` + AuthData *string `json:"auth_data"` + Password *string `json:"password"` + Nickname *string `json:"nickname"` + FirstName *string `json:"first_name"` + LastName *string `json:"last_name"` + Position *string `json:"position"` + Roles *string `json:"roles"` + Locale *string `json:"locale"` + UseMarkdownPreview *string `json:"feature_enabled_markdown_preview"` + UseFormatting *string `json:"formatting"` + ShowUnreadSection *string `json:"show_unread_section"` Teams *[]UserTeamImportData `json:"teams"` @@ -82,6 +85,7 @@ type UserNotifyPropsImportData struct { type UserTeamImportData struct { Name *string `json:"name"` Roles *string `json:"roles"` + Theme *string `json:"theme"` Channels *[]UserChannelImportData `json:"channels"` } diff --git a/app/import_validators.go b/app/import_validators.go index 9b19ae45e..b23ecd8f9 100644 --- a/app/import_validators.go +++ b/app/import_validators.go @@ -4,8 +4,10 @@ package app import ( + "encoding/json" "net/http" "os" + "strings" "unicode/utf8" "github.com/mattermost/mattermost-server/model" @@ -267,6 +269,18 @@ func validateUserImportData(data *UserImportData) *model.AppError { } } + if data.UseMarkdownPreview != nil && !model.IsValidTrueOrFalseString(*data.UseMarkdownPreview) { + return model.NewAppError("BulkImport", "app.import.validate_user_import_data.advanced_props_feature_markdown_preview.error", nil, "", http.StatusBadRequest) + } + + if data.UseFormatting != nil && !model.IsValidTrueOrFalseString(*data.UseFormatting) { + return model.NewAppError("BulkImport", "app.import.validate_user_import_data.advanced_props_formatting.error", nil, "", http.StatusBadRequest) + } + + if data.ShowUnreadSection != nil && !model.IsValidTrueOrFalseString(*data.ShowUnreadSection) { + return model.NewAppError("BulkImport", "app.import.validate_user_import_data.advanced_props_show_unread_section.error", nil, "", http.StatusBadRequest) + } + if data.Teams != nil { return validateUserTeamsImportData(data.Teams) } else { @@ -293,6 +307,13 @@ func validateUserTeamsImportData(data *[]UserTeamImportData) *model.AppError { return err } } + + if tdata.Theme != nil && 0 < len(strings.Trim(*tdata.Theme, " \t\r")) { + var unused map[string]string + if err := json.NewDecoder(strings.NewReader(*tdata.Theme)).Decode(&unused); err != nil { + return model.NewAppError("BulkImport", "app.import.validate_user_teams_import_data.invalid_team_theme.error", nil, err.Error(), http.StatusBadRequest) + } + } } return nil diff --git a/app/import_validators_test.go b/app/import_validators_test.go index 5fb0518f7..617b5cd96 100644 --- a/app/import_validators_test.go +++ b/app/import_validators_test.go @@ -692,6 +692,25 @@ func TestImportValidateUserTeamsImportData(t *testing.T) { if err := validateUserTeamsImportData(&data); err != nil { t.Fatal("Should have succeeded with valid roles.") } + + // Valid (with JSON string of theme) + data[0].Theme = ptrStr(`{"awayIndicator":"#DBBD4E","buttonBg":"#23A1FF","buttonColor":"#FFFFFF","centerChannelBg":"#ffffff","centerChannelColor":"#333333","codeTheme":"github","image":"/static/files/a4a388b38b32678e83823ef1b3e17766.png","linkColor":"#2389d7","mentionBg":"#2389d7","mentionColor":"#ffffff","mentionHighlightBg":"#fff2bb","mentionHighlightLink":"#2f81b7","newMessageSeparator":"#FF8800","onlineIndicator":"#7DBE00","sidebarBg":"#fafafa","sidebarHeaderBg":"#3481B9","sidebarHeaderTextColor":"#ffffff","sidebarText":"#333333","sidebarTextActiveBorder":"#378FD2","sidebarTextActiveColor":"#111111","sidebarTextHoverBg":"#e6f2fa","sidebarUnreadText":"#333333","type":"Mattermost"}`) + if err := validateUserTeamsImportData(&data); err != nil { + t.Fatal("Should have succeeded with valid theme.") + } + + // Invalid (invalid JSON string of theme) + data[0].Theme = ptrStr(`This is the invalid string which cannot be marshalled to JSON object :) + {"#DBBD4E","buttonBg", "#23A1FF", buttonColor`) + if err := validateUserTeamsImportData(&data); err == nil { + t.Fatal("Should have fail with invalid JSON string of theme.") + } + + // Invalid (valid JSON but invalid theme description) + data[0].Theme = ptrStr(`{"somekey": 25, "json_obj1": {"color": "#DBBD4E","buttonBg": "#23A1FF"}}`) + if err := validateUserTeamsImportData(&data); err == nil { + t.Fatal("Should have fail with valid JSON which contains invalid string of theme description.") + } + data[0].Theme = nil } func TestImportValidateUserChannelsImportData(t *testing.T) { -- cgit v1.2.3-1-g7c22