diff --git a/app/export.go b/app/export.go index 4cb8c18ea9..1921a748e4 100644 --- a/app/export.go +++ b/app/export.go @@ -15,6 +15,41 @@ import ( "github.com/pkg/errors" ) +// We use this map to identify the exportable preferences. +// Here we link the preference category and name, to the name of the relevant filed in the import struct. +var exportablePreferences = map[ComparablePreference]string{{ + Category: model.PREFERENCE_CATEGORY_THEME, + Name: "", +}: "Theme", { + Category: model.PREFERENCE_CATEGORY_ADVANCED_SETTINGS, + Name: "feature_enabled_markdown_preview", +}: "UseMarkdownPreview", { + Category: model.PREFERENCE_CATEGORY_ADVANCED_SETTINGS, + Name: "formatting", +}: "UseFormatting", { + Category: model.PREFERENCE_CATEGORY_SIDEBAR_SETTINGS, + Name: "show_unread_section", +}: "ShowUnreadSection", { + Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, + Name: model.PREFERENCE_NAME_USE_MILITARY_TIME, +}: "UseMilitaryTime", { + Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, + Name: model.PREFERENCE_NAME_COLLAPSE_SETTING, +}: "CollapsePreviews", { + Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, + Name: model.PREFERENCE_NAME_MESSAGE_DISPLAY, +}: "MessageDisplay", { + Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, + Name: "channel_display_mode", +}: "ChannelDisplayMode", { + Category: model.PREFERENCE_CATEGORY_TUTORIAL_STEPS, + Name: "", +}: "TutorialStep", { + Category: model.PREFERENCE_CATEGORY_NOTIFICATIONS, + Name: model.PREFERENCE_NAME_EMAIL_INTERVAL, +}: "EmailInterval", +} + func (a *App) BulkExport(writer io.Writer, file string, pathToEmojiDir string, dirNameToExportEmoji string) *model.AppError { if err := a.ExportVersion(writer); err != nil { return err @@ -154,7 +189,45 @@ func (a *App) ExportAllUsers(writer io.Writer) *model.AppError { continue } - userLine := ImportLineFromUser(user) + // Gathering here the exportable preferences to pass them on to ImportLineFromUser + exportedPrefs := make(map[string]*string) + allPrefs, err := a.GetPreferencesForUser(user.Id) + if err != nil { + return err + } + for _, pref := range allPrefs { + // We need to manage the special cases + // Here we manage Tutorial steps + if pref.Category == model.PREFERENCE_CATEGORY_TUTORIAL_STEPS { + pref.Name = "" + // Then the email interval + } else if pref.Category == model.PREFERENCE_CATEGORY_NOTIFICATIONS && pref.Name == model.PREFERENCE_NAME_EMAIL_INTERVAL { + switch pref.Value { + case model.PREFERENCE_EMAIL_INTERVAL_NO_BATCHING_SECONDS: + pref.Value = model.PREFERENCE_EMAIL_INTERVAL_IMMEDIATELY + case model.PREFERENCE_EMAIL_INTERVAL_FIFTEEN_AS_SECONDS: + pref.Value = model.PREFERENCE_EMAIL_INTERVAL_FIFTEEN + case model.PREFERENCE_EMAIL_INTERVAL_HOUR_AS_SECONDS: + pref.Value = model.PREFERENCE_EMAIL_INTERVAL_HOUR + case "0": + pref.Value = "" + } + } + id, ok := exportablePreferences[ComparablePreference{ + Category: pref.Category, + Name: pref.Name, + }] + if ok { + prefPtr := pref.Value + if prefPtr != "" { + exportedPrefs[id] = &prefPtr + } else { + exportedPrefs[id] = nil + } + } + } + + userLine := ImportLineFromUser(user, exportedPrefs) userLine.User.NotifyProps = a.buildUserNotifyProps(user.NotifyProps) diff --git a/app/export_converters.go b/app/export_converters.go index 5fcce7bd89..a25b6aec98 100644 --- a/app/export_converters.go +++ b/app/export_converters.go @@ -38,20 +38,36 @@ func ImportLineFromChannel(channel *model.ChannelForExport) *LineImportData { } } -func ImportLineFromUser(user *model.User) *LineImportData { +func ImportLineFromUser(user *model.User, exportedPrefs map[string]*string) *LineImportData { + // Bulk Importer doesn't accept "empty string" for AuthService. + var authService *string + if user.AuthService != "" { + authService = &user.AuthService + } + return &LineImportData{ Type: "user", User: &UserImportData{ - Username: &user.Username, - Email: &user.Email, - AuthService: &user.AuthService, - AuthData: user.AuthData, - Nickname: &user.Nickname, - FirstName: &user.FirstName, - LastName: &user.LastName, - Position: &user.Position, - Roles: &user.Roles, - Locale: &user.Locale, + Username: &user.Username, + Email: &user.Email, + AuthService: authService, + AuthData: user.AuthData, + Nickname: &user.Nickname, + FirstName: &user.FirstName, + LastName: &user.LastName, + Position: &user.Position, + Roles: &user.Roles, + Locale: &user.Locale, + UseMarkdownPreview: exportedPrefs["UseMarkdownPreview"], + UseFormatting: exportedPrefs["UseFormatting"], + ShowUnreadSection: exportedPrefs["ShowUnreadSection"], + Theme: exportedPrefs["Theme"], + UseMilitaryTime: exportedPrefs["UseMilitaryTime"], + CollapsePreviews: exportedPrefs["CollapsePreviews"], + MessageDisplay: exportedPrefs["MessageDisplay"], + ChannelDisplayMode: exportedPrefs["ChannelDisplayMode"], + TutorialStep: exportedPrefs["TutorialStep"], + EmailInterval: exportedPrefs["EmailInterval"], }, } } diff --git a/app/export_test.go b/app/export_test.go index 771a98148d..11aeb7b375 100644 --- a/app/export_test.go +++ b/app/export_test.go @@ -1,14 +1,15 @@ package app import ( + "bytes" "os" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/store" - "github.com/stretchr/testify/require" ) func TestReactionsOfPost(t *testing.T) { @@ -164,3 +165,31 @@ func TestExportCustomEmoji(t *testing.T) { t.Fatal(err) } } + +func TestExportAllUsers(t *testing.T) { + th1 := Setup().InitBasic() + defer th1.TearDown() + + var b bytes.Buffer + err := th1.App.BulkExport(&b, "somefile", "somePath", "someDir") + require.Nil(t, err) + + th2 := Setup() + defer th2.TearDown() + err, i := th2.App.BulkImport(&b, false, 5) + assert.Nil(t, err) + assert.Equal(t, 0, i) + + users1, err := th1.App.GetUsers(&model.UserGetOptions{ + Page: 0, + PerPage: 10, + }) + assert.Nil(t, err) + users2, err := th2.App.GetUsers(&model.UserGetOptions{ + Page: 0, + PerPage: 10, + }) + assert.Nil(t, err) + assert.Equal(t, len(users1), len(users2)) + assert.ElementsMatch(t, users1, users2) +} diff --git a/app/import_types.go b/app/import_types.go index f8eb91f46e..ce15a37448 100644 --- a/app/import_types.go +++ b/app/import_types.go @@ -190,3 +190,8 @@ type LineImportWorkerError struct { type AttachmentImportData struct { Path *string `json:"path"` } + +type ComparablePreference struct { + Category string + Name string +}