[MM 12458] Include user preferences in bulk export (#9784)
* First version of the preference exporter * First working version of the preference export * Removed useless debug line * Added special case of empty string to harverst the omitempty feature * Removed dangling line from rebase * Moved exportablepreference in scope * Fix go vet issue * Fix formating * Add simple test * Add full test for BulkExportUsers * Improve variable naming
Этот коммит содержится в:
коммит произвёл
George Goldberg
родитель
ee9395f1b6
Коммит
0f35c4b24d
@@ -15,6 +15,41 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"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 {
|
func (a *App) BulkExport(writer io.Writer, file string, pathToEmojiDir string, dirNameToExportEmoji string) *model.AppError {
|
||||||
if err := a.ExportVersion(writer); err != nil {
|
if err := a.ExportVersion(writer); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -154,7 +189,45 @@ func (a *App) ExportAllUsers(writer io.Writer) *model.AppError {
|
|||||||
continue
|
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)
|
userLine.User.NotifyProps = a.buildUserNotifyProps(user.NotifyProps)
|
||||||
|
|
||||||
|
|||||||
@@ -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{
|
return &LineImportData{
|
||||||
Type: "user",
|
Type: "user",
|
||||||
User: &UserImportData{
|
User: &UserImportData{
|
||||||
Username: &user.Username,
|
Username: &user.Username,
|
||||||
Email: &user.Email,
|
Email: &user.Email,
|
||||||
AuthService: &user.AuthService,
|
AuthService: authService,
|
||||||
AuthData: user.AuthData,
|
AuthData: user.AuthData,
|
||||||
Nickname: &user.Nickname,
|
Nickname: &user.Nickname,
|
||||||
FirstName: &user.FirstName,
|
FirstName: &user.FirstName,
|
||||||
LastName: &user.LastName,
|
LastName: &user.LastName,
|
||||||
Position: &user.Position,
|
Position: &user.Position,
|
||||||
Roles: &user.Roles,
|
Roles: &user.Roles,
|
||||||
Locale: &user.Locale,
|
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"],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
"github.com/mattermost/mattermost-server/store"
|
"github.com/mattermost/mattermost-server/store"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestReactionsOfPost(t *testing.T) {
|
func TestReactionsOfPost(t *testing.T) {
|
||||||
@@ -164,3 +165,31 @@ func TestExportCustomEmoji(t *testing.T) {
|
|||||||
t.Fatal(err)
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -190,3 +190,8 @@ type LineImportWorkerError struct {
|
|||||||
type AttachmentImportData struct {
|
type AttachmentImportData struct {
|
||||||
Path *string `json:"path"`
|
Path *string `json:"path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ComparablePreference struct {
|
||||||
|
Category string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user