[MM-58834] Review user preferences export and import (#28286)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
e8ebbcc980
Коммит
9d5993d89d
@@ -29,40 +29,80 @@ import (
|
|||||||
|
|
||||||
// We use this map to identify the exportable preferences.
|
// We use this map to identify the exportable preferences.
|
||||||
// Here we link the preference category and name, to the name of the relevant field in the import struct.
|
// Here we link the preference category and name, to the name of the relevant field in the import struct.
|
||||||
var exportablePreferences = map[imports.ComparablePreference]string{{
|
var exportablePreferences = map[imports.ComparablePreference]string{
|
||||||
|
{
|
||||||
Category: model.PreferenceCategoryTheme,
|
Category: model.PreferenceCategoryTheme,
|
||||||
Name: "",
|
Name: "",
|
||||||
}: "Theme", {
|
}: "Theme",
|
||||||
|
{
|
||||||
Category: model.PreferenceCategoryAdvancedSettings,
|
Category: model.PreferenceCategoryAdvancedSettings,
|
||||||
Name: "feature_enabled_markdown_preview",
|
Name: "feature_enabled_markdown_preview",
|
||||||
}: "UseMarkdownPreview", {
|
}: "UseMarkdownPreview",
|
||||||
|
{
|
||||||
Category: model.PreferenceCategoryAdvancedSettings,
|
Category: model.PreferenceCategoryAdvancedSettings,
|
||||||
Name: "formatting",
|
Name: "formatting",
|
||||||
}: "UseFormatting", {
|
}: "UseFormatting",
|
||||||
|
{
|
||||||
|
Category: model.PreferenceCategoryAdvancedSettings,
|
||||||
|
Name: "send_on_ctrl_enter",
|
||||||
|
}: "SendOnCtrlEnter",
|
||||||
|
{
|
||||||
|
Category: model.PreferenceCategoryAdvancedSettings,
|
||||||
|
Name: "code_block_ctrl_enter",
|
||||||
|
}: "CodeBlockCtrlEnter",
|
||||||
|
{
|
||||||
|
Category: model.PreferenceCategoryAdvancedSettings,
|
||||||
|
Name: "join_leave",
|
||||||
|
}: "ShowJoinLeave",
|
||||||
|
{
|
||||||
|
Category: model.PreferenceCategoryAdvancedSettings,
|
||||||
|
Name: "unread_scroll_position",
|
||||||
|
}: "ShowUnreadScrollPosition",
|
||||||
|
{
|
||||||
|
Category: model.PreferenceCategoryAdvancedSettings,
|
||||||
|
Name: "sync_drafts",
|
||||||
|
}: "SyncDrafts",
|
||||||
|
{
|
||||||
Category: model.PreferenceCategorySidebarSettings,
|
Category: model.PreferenceCategorySidebarSettings,
|
||||||
Name: "show_unread_section",
|
Name: model.PreferenceNameShowUnreadSection,
|
||||||
}: "ShowUnreadSection", {
|
}: "ShowUnreadSection",
|
||||||
|
{
|
||||||
|
Category: model.PreferenceCategorySidebarSettings,
|
||||||
|
Name: model.PreferenceLimitVisibleDmsGms,
|
||||||
|
}: "LimitVisibleDmsGms",
|
||||||
|
{
|
||||||
Category: model.PreferenceCategoryDisplaySettings,
|
Category: model.PreferenceCategoryDisplaySettings,
|
||||||
Name: model.PreferenceNameUseMilitaryTime,
|
Name: model.PreferenceNameUseMilitaryTime,
|
||||||
}: "UseMilitaryTime", {
|
}: "UseMilitaryTime",
|
||||||
|
{
|
||||||
Category: model.PreferenceCategoryDisplaySettings,
|
Category: model.PreferenceCategoryDisplaySettings,
|
||||||
Name: model.PreferenceNameCollapseSetting,
|
Name: model.PreferenceNameCollapseSetting,
|
||||||
}: "CollapsePreviews", {
|
}: "CollapsePreviews",
|
||||||
|
{
|
||||||
Category: model.PreferenceCategoryDisplaySettings,
|
Category: model.PreferenceCategoryDisplaySettings,
|
||||||
Name: model.PreferenceNameMessageDisplay,
|
Name: model.PreferenceNameMessageDisplay,
|
||||||
}: "MessageDisplay", {
|
}: "MessageDisplay",
|
||||||
|
{
|
||||||
Category: model.PreferenceCategoryDisplaySettings,
|
Category: model.PreferenceCategoryDisplaySettings,
|
||||||
Name: "channel_display_mode",
|
Name: model.PreferenceNameChannelDisplayMode,
|
||||||
}: "CollapseConsecutive", {
|
}: "ChannelDisplayMode",
|
||||||
|
{
|
||||||
Category: model.PreferenceCategoryDisplaySettings,
|
Category: model.PreferenceCategoryDisplaySettings,
|
||||||
Name: "collapse_consecutive_messages",
|
Name: model.PreferenceNameCollapseConsecutive,
|
||||||
}: "ColorizeUsernames", {
|
}: "CollapseConsecutive",
|
||||||
|
{
|
||||||
Category: model.PreferenceCategoryDisplaySettings,
|
Category: model.PreferenceCategoryDisplaySettings,
|
||||||
Name: "colorize_usernames",
|
Name: model.PreferenceNameColorizeUsernames,
|
||||||
}: "ChannelDisplayMode", {
|
}: "ColorizeUsernames",
|
||||||
|
{
|
||||||
|
Category: model.PreferenceCategoryDisplaySettings,
|
||||||
|
Name: model.PreferenceNameNameFormat,
|
||||||
|
}: "NameFormat",
|
||||||
|
{
|
||||||
Category: model.PreferenceCategoryTutorialSteps,
|
Category: model.PreferenceCategoryTutorialSteps,
|
||||||
Name: "",
|
Name: "",
|
||||||
}: "TutorialStep", {
|
}: "TutorialStep",
|
||||||
|
{
|
||||||
Category: model.PreferenceCategoryNotifications,
|
Category: model.PreferenceCategoryNotifications,
|
||||||
Name: model.PreferenceNameEmailInterval,
|
Name: model.PreferenceNameEmailInterval,
|
||||||
}: "EmailInterval",
|
}: "EmailInterval",
|
||||||
|
|||||||
@@ -165,6 +165,13 @@ func ImportLineFromUser(user *model.User, exportedPrefs map[string]*string) *imp
|
|||||||
ChannelDisplayMode: exportedPrefs["ChannelDisplayMode"],
|
ChannelDisplayMode: exportedPrefs["ChannelDisplayMode"],
|
||||||
TutorialStep: exportedPrefs["TutorialStep"],
|
TutorialStep: exportedPrefs["TutorialStep"],
|
||||||
EmailInterval: exportedPrefs["EmailInterval"],
|
EmailInterval: exportedPrefs["EmailInterval"],
|
||||||
|
NameFormat: exportedPrefs["NameFormat"],
|
||||||
|
SendOnCtrlEnter: exportedPrefs["SendOnCtrlEnter"],
|
||||||
|
ShowJoinLeave: exportedPrefs["ShowJoinLeave"],
|
||||||
|
SyncDrafts: exportedPrefs["SyncDrafts"],
|
||||||
|
ShowUnreadScrollPosition: exportedPrefs["ShowUnreadScrollPosition"],
|
||||||
|
LimitVisibleDmsGms: exportedPrefs["LimitVisibleDmsGms"],
|
||||||
|
CodeBlockCtrlEnter: exportedPrefs["CodeBlockCtrlEnter"],
|
||||||
DeleteAt: &user.DeleteAt,
|
DeleteAt: &user.DeleteAt,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -238,6 +245,7 @@ func ImportUserChannelDataFromChannelMemberAndPreferences(member *model.ChannelM
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ImportLineForPost(post *model.PostForExport) *imports.LineImportData {
|
func ImportLineForPost(post *model.PostForExport) *imports.LineImportData {
|
||||||
|
f := []string(post.FlaggedBy)
|
||||||
return &imports.LineImportData{
|
return &imports.LineImportData{
|
||||||
Type: "post",
|
Type: "post",
|
||||||
Post: &imports.PostImportData{
|
Post: &imports.PostImportData{
|
||||||
@@ -249,6 +257,8 @@ func ImportLineForPost(post *model.PostForExport) *imports.LineImportData {
|
|||||||
Props: &post.Props,
|
Props: &post.Props,
|
||||||
CreateAt: &post.CreateAt,
|
CreateAt: &post.CreateAt,
|
||||||
EditAt: &post.EditAt,
|
EditAt: &post.EditAt,
|
||||||
|
IsPinned: &post.IsPinned,
|
||||||
|
FlaggedBy: &f,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,6 +268,7 @@ func ImportLineForDirectPost(post *model.DirectPostForExport) *imports.LineImpor
|
|||||||
if len(channelMembers) == 1 {
|
if len(channelMembers) == 1 {
|
||||||
channelMembers = []string{channelMembers[0], channelMembers[0]}
|
channelMembers = []string{channelMembers[0], channelMembers[0]}
|
||||||
}
|
}
|
||||||
|
f := []string(post.FlaggedBy)
|
||||||
return &imports.LineImportData{
|
return &imports.LineImportData{
|
||||||
Type: "direct_post",
|
Type: "direct_post",
|
||||||
DirectPost: &imports.DirectPostImportData{
|
DirectPost: &imports.DirectPostImportData{
|
||||||
@@ -268,17 +279,22 @@ func ImportLineForDirectPost(post *model.DirectPostForExport) *imports.LineImpor
|
|||||||
Props: &post.Props,
|
Props: &post.Props,
|
||||||
CreateAt: &post.CreateAt,
|
CreateAt: &post.CreateAt,
|
||||||
EditAt: &post.EditAt,
|
EditAt: &post.EditAt,
|
||||||
|
IsPinned: &post.IsPinned,
|
||||||
|
FlaggedBy: &f,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ImportReplyFromPost(post *model.ReplyForExport) *imports.ReplyImportData {
|
func ImportReplyFromPost(post *model.ReplyForExport) *imports.ReplyImportData {
|
||||||
|
f := []string(post.FlaggedBy)
|
||||||
return &imports.ReplyImportData{
|
return &imports.ReplyImportData{
|
||||||
User: &post.Username,
|
User: &post.Username,
|
||||||
Type: &post.Type,
|
Type: &post.Type,
|
||||||
Message: &post.Message,
|
Message: &post.Message,
|
||||||
CreateAt: &post.CreateAt,
|
CreateAt: &post.CreateAt,
|
||||||
EditAt: &post.EditAt,
|
EditAt: &post.EditAt,
|
||||||
|
IsPinned: &post.IsPinned,
|
||||||
|
FlaggedBy: &f,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -722,7 +722,7 @@ func (a *App) importUser(rctx request.CTX, data *imports.UserImportData, dryRun
|
|||||||
preferences = append(preferences, model.Preference{
|
preferences = append(preferences, model.Preference{
|
||||||
UserId: savedUser.Id,
|
UserId: savedUser.Id,
|
||||||
Category: model.PreferenceCategoryDisplaySettings,
|
Category: model.PreferenceCategoryDisplaySettings,
|
||||||
Name: "channel_display_mode",
|
Name: model.PreferenceNameChannelDisplayMode,
|
||||||
Value: *data.ChannelDisplayMode,
|
Value: *data.ChannelDisplayMode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -763,6 +763,69 @@ func (a *App) importUser(rctx request.CTX, data *imports.UserImportData, dryRun
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if data.SendOnCtrlEnter != nil {
|
||||||
|
preferences = append(preferences, model.Preference{
|
||||||
|
UserId: savedUser.Id,
|
||||||
|
Category: model.PreferenceCategoryAdvancedSettings,
|
||||||
|
Name: "send_on_ctrl_enter",
|
||||||
|
Value: *data.SendOnCtrlEnter,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.CodeBlockCtrlEnter != nil {
|
||||||
|
preferences = append(preferences, model.Preference{
|
||||||
|
UserId: savedUser.Id,
|
||||||
|
Category: model.PreferenceCategoryAdvancedSettings,
|
||||||
|
Name: "code_block_ctrl_enter",
|
||||||
|
Value: *data.CodeBlockCtrlEnter,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.ShowJoinLeave != nil {
|
||||||
|
preferences = append(preferences, model.Preference{
|
||||||
|
UserId: savedUser.Id,
|
||||||
|
Category: model.PreferenceCategoryAdvancedSettings,
|
||||||
|
Name: "join_leave",
|
||||||
|
Value: *data.ShowJoinLeave,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.ShowUnreadScrollPosition != nil {
|
||||||
|
preferences = append(preferences, model.Preference{
|
||||||
|
UserId: savedUser.Id,
|
||||||
|
Category: model.PreferenceCategoryAdvancedSettings,
|
||||||
|
Name: "unread_scroll_position",
|
||||||
|
Value: *data.ShowUnreadScrollPosition,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.SyncDrafts != nil {
|
||||||
|
preferences = append(preferences, model.Preference{
|
||||||
|
UserId: savedUser.Id,
|
||||||
|
Category: model.PreferenceCategoryAdvancedSettings,
|
||||||
|
Name: "sync_drafts",
|
||||||
|
Value: *data.SyncDrafts,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.LimitVisibleDmsGms != nil {
|
||||||
|
preferences = append(preferences, model.Preference{
|
||||||
|
UserId: savedUser.Id,
|
||||||
|
Category: model.PreferenceCategorySidebarSettings,
|
||||||
|
Name: model.PreferenceLimitVisibleDmsGms,
|
||||||
|
Value: *data.LimitVisibleDmsGms,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.NameFormat != nil {
|
||||||
|
preferences = append(preferences, model.Preference{
|
||||||
|
UserId: savedUser.Id,
|
||||||
|
Category: model.PreferenceCategoryDisplaySettings,
|
||||||
|
Name: model.PreferenceNameNameFormat,
|
||||||
|
Value: *data.NameFormat,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if data.EmailInterval != nil || savedUser.NotifyProps[model.EmailNotifyProp] == "false" {
|
if data.EmailInterval != nil || savedUser.NotifyProps[model.EmailNotifyProp] == "false" {
|
||||||
var intervalSeconds string
|
var intervalSeconds string
|
||||||
if value := savedUser.NotifyProps[model.EmailNotifyProp]; value == "false" {
|
if value := savedUser.NotifyProps[model.EmailNotifyProp]; value == "false" {
|
||||||
@@ -1184,6 +1247,9 @@ func (a *App) importReplies(rctx request.CTX, data []imports.ReplyImportData, po
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
usernames = append(usernames, *replyData.User)
|
usernames = append(usernames, *replyData.User)
|
||||||
|
if replyData.FlaggedBy != nil {
|
||||||
|
usernames = append(usernames, *replyData.FlaggedBy...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
users, err := a.getUsersByUsernames(usernames)
|
users, err := a.getUsersByUsernames(usernames)
|
||||||
@@ -1233,6 +1299,9 @@ func (a *App) importReplies(rctx request.CTX, data []imports.ReplyImportData, po
|
|||||||
if replyData.EditAt != nil {
|
if replyData.EditAt != nil {
|
||||||
reply.EditAt = *replyData.EditAt
|
reply.EditAt = *replyData.EditAt
|
||||||
}
|
}
|
||||||
|
if replyData.IsPinned != nil {
|
||||||
|
reply.IsPinned = *replyData.IsPinned
|
||||||
|
}
|
||||||
|
|
||||||
fileIDs := a.uploadAttachments(rctx, replyData.Attachments, reply, teamID, extractContent)
|
fileIDs := a.uploadAttachments(rctx, replyData.Attachments, reply, teamID, extractContent)
|
||||||
for _, fileID := range reply.FileIds {
|
for _, fileID := range reply.FileIds {
|
||||||
@@ -1274,6 +1343,27 @@ func (a *App) importReplies(rctx request.CTX, data []imports.ReplyImportData, po
|
|||||||
|
|
||||||
for _, postWithData := range postsWithData {
|
for _, postWithData := range postsWithData {
|
||||||
a.updateFileInfoWithPostId(rctx, postWithData.post)
|
a.updateFileInfoWithPostId(rctx, postWithData.post)
|
||||||
|
|
||||||
|
if postWithData.replyData.FlaggedBy != nil {
|
||||||
|
var preferences model.Preferences
|
||||||
|
|
||||||
|
for _, username := range *postWithData.replyData.FlaggedBy {
|
||||||
|
user := users[strings.ToLower(username)]
|
||||||
|
|
||||||
|
preferences = append(preferences, model.Preference{
|
||||||
|
UserId: user.Id,
|
||||||
|
Category: model.PreferenceCategoryFlaggedPost,
|
||||||
|
Name: postWithData.post.Id,
|
||||||
|
Value: "true",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(preferences) > 0 {
|
||||||
|
if err := a.Srv().Store().Preference().Save(preferences); err != nil {
|
||||||
|
return model.NewAppError("BulkImport", "app.import.import_post.save_preferences.error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1179,6 +1179,13 @@ func TestImportImportUser(t *testing.T) {
|
|||||||
UseFormatting: ptrStr("true"),
|
UseFormatting: ptrStr("true"),
|
||||||
ShowUnreadSection: ptrStr("true"),
|
ShowUnreadSection: ptrStr("true"),
|
||||||
EmailInterval: ptrStr("immediately"),
|
EmailInterval: ptrStr("immediately"),
|
||||||
|
NameFormat: model.NewPointer("full_name"),
|
||||||
|
SendOnCtrlEnter: model.NewPointer("true"),
|
||||||
|
CodeBlockCtrlEnter: model.NewPointer("true"),
|
||||||
|
ShowJoinLeave: model.NewPointer("false"),
|
||||||
|
SyncDrafts: model.NewPointer("false"),
|
||||||
|
ShowUnreadScrollPosition: model.NewPointer("start_from_newest"),
|
||||||
|
LimitVisibleDmsGms: model.NewPointer("20"),
|
||||||
}
|
}
|
||||||
appErr = th.App.importUser(th.Context, &data, false)
|
appErr = th.App.importUser(th.Context, &data, false)
|
||||||
assert.Nil(t, appErr)
|
assert.Nil(t, appErr)
|
||||||
@@ -1197,7 +1204,13 @@ func TestImportImportUser(t *testing.T) {
|
|||||||
checkPreference(t, th.App, user.Id, model.PreferenceCategoryAdvancedSettings, "feature_enabled_markdown_preview", *data.UseMarkdownPreview)
|
checkPreference(t, th.App, user.Id, model.PreferenceCategoryAdvancedSettings, "feature_enabled_markdown_preview", *data.UseMarkdownPreview)
|
||||||
checkPreference(t, th.App, user.Id, model.PreferenceCategoryAdvancedSettings, "formatting", *data.UseFormatting)
|
checkPreference(t, th.App, user.Id, model.PreferenceCategoryAdvancedSettings, "formatting", *data.UseFormatting)
|
||||||
checkPreference(t, th.App, user.Id, model.PreferenceCategorySidebarSettings, "show_unread_section", *data.ShowUnreadSection)
|
checkPreference(t, th.App, user.Id, model.PreferenceCategorySidebarSettings, "show_unread_section", *data.ShowUnreadSection)
|
||||||
checkPreference(t, th.App, user.Id, model.PreferenceCategoryNotifications, model.PreferenceNameEmailInterval, "30")
|
checkPreference(t, th.App, user.Id, model.PreferenceCategoryDisplaySettings, model.PreferenceNameNameFormat, "full_name")
|
||||||
|
checkPreference(t, th.App, user.Id, model.PreferenceCategoryAdvancedSettings, "send_on_ctrl_enter", "true")
|
||||||
|
checkPreference(t, th.App, user.Id, model.PreferenceCategoryAdvancedSettings, "code_block_ctrl_enter", "true")
|
||||||
|
checkPreference(t, th.App, user.Id, model.PreferenceCategoryAdvancedSettings, "join_leave", "false")
|
||||||
|
checkPreference(t, th.App, user.Id, model.PreferenceCategoryAdvancedSettings, "sync_drafts", "false")
|
||||||
|
checkPreference(t, th.App, user.Id, model.PreferenceCategoryAdvancedSettings, "unread_scroll_position", "start_from_newest")
|
||||||
|
checkPreference(t, th.App, user.Id, model.PreferenceCategorySidebarSettings, model.PreferenceLimitVisibleDmsGms, "20")
|
||||||
|
|
||||||
// Change those preferences.
|
// Change those preferences.
|
||||||
data = imports.UserImportData{
|
data = imports.UserImportData{
|
||||||
@@ -2556,6 +2569,44 @@ func TestImportimportMultiplePostLines(t *testing.T) {
|
|||||||
AssertAllPostsCount(t, th.App, initialPostCountForTeam2, 1, team2.Id)
|
AssertAllPostsCount(t, th.App, initialPostCountForTeam2, 1, team2.Id)
|
||||||
AssertAllPostsCount(t, th.App, initialPostCount, 15, team.Id)
|
AssertAllPostsCount(t, th.App, initialPostCount, 15, team.Id)
|
||||||
|
|
||||||
|
t.Run("Importing a post with a reply both pinned", func(t *testing.T) {
|
||||||
|
// Create a thread.
|
||||||
|
importCreate := time.Now().Add(-1 * time.Minute).UnixMilli()
|
||||||
|
replyCreate := time.Now().Add(-30 * time.Second).UnixMilli()
|
||||||
|
data = imports.LineImportWorkerData{
|
||||||
|
LineImportData: imports.LineImportData{
|
||||||
|
Post: &imports.PostImportData{
|
||||||
|
Team: &teamName,
|
||||||
|
Channel: &channelName,
|
||||||
|
User: &user.Username,
|
||||||
|
Message: model.NewPointer("Thread Message"),
|
||||||
|
CreateAt: model.NewPointer(importCreate),
|
||||||
|
IsPinned: model.NewPointer(true),
|
||||||
|
Replies: &[]imports.ReplyImportData{{
|
||||||
|
User: &user.Username,
|
||||||
|
Message: model.NewPointer("Reply"),
|
||||||
|
CreateAt: model.NewPointer(replyCreate),
|
||||||
|
IsPinned: model.NewPointer(true),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
LineNumber: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = th.App.importMultiplePostLines(th.Context, []imports.LineImportWorkerData{data}, false, true)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
resultPosts, nErr = th.App.Srv().Store().Post().GetPostsCreatedAt(channel.Id, importCreate)
|
||||||
|
require.NoError(t, nErr)
|
||||||
|
require.Equal(t, 1, len(resultPosts))
|
||||||
|
require.True(t, resultPosts[0].IsPinned)
|
||||||
|
|
||||||
|
resultPosts, nErr = th.App.Srv().Store().Post().GetPostsCreatedAt(channel.Id, replyCreate)
|
||||||
|
require.NoError(t, nErr)
|
||||||
|
require.Equal(t, 1, len(resultPosts))
|
||||||
|
require.True(t, resultPosts[0].IsPinned)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Importing a post with a thread", func(t *testing.T) {
|
t.Run("Importing a post with a thread", func(t *testing.T) {
|
||||||
// Create a thread.
|
// Create a thread.
|
||||||
importCreate := time.Now().Add(-1 * time.Minute).UnixMilli()
|
importCreate := time.Now().Add(-1 * time.Minute).UnixMilli()
|
||||||
@@ -2715,6 +2766,76 @@ func TestImportimportMultiplePostLines(t *testing.T) {
|
|||||||
|
|
||||||
assert.ElementsMatch(t, []string{user.Id}, followers)
|
assert.ElementsMatch(t, []string{user.Id}, followers)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Importing a post that someone flagged", func(t *testing.T) {
|
||||||
|
// Create a thread.
|
||||||
|
importCreate := time.Now().Add(-1 * time.Minute).UnixMilli()
|
||||||
|
data = imports.LineImportWorkerData{
|
||||||
|
LineImportData: imports.LineImportData{
|
||||||
|
Post: &imports.PostImportData{
|
||||||
|
Team: &teamName,
|
||||||
|
Channel: &channelName,
|
||||||
|
User: &user.Username,
|
||||||
|
Message: model.NewPointer("Flagged Message"),
|
||||||
|
CreateAt: model.NewPointer(importCreate),
|
||||||
|
FlaggedBy: &[]string{user.Username},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
LineNumber: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
errLine, err = th.App.importMultiplePostLines(th.Context, []imports.LineImportWorkerData{data}, false, true)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 0, errLine)
|
||||||
|
|
||||||
|
resultPosts, nErr = th.App.Srv().Store().Post().GetPostsCreatedAt(channel.Id, importCreate)
|
||||||
|
require.NoError(t, nErr)
|
||||||
|
require.Equal(t, 1, len(resultPosts))
|
||||||
|
|
||||||
|
pref, err := th.App.ch.srv.Store().Preference().GetCategoryAndName(model.PreferenceCategoryFlaggedPost, resultPosts[0].Id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Len(t, pref, 1)
|
||||||
|
assert.Equal(t, user.Id, pref[0].UserId)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Importing a post that someone flagged its replies", func(t *testing.T) {
|
||||||
|
// Create a thread.
|
||||||
|
importCreate := time.Now().Add(-1 * time.Minute).UnixMilli()
|
||||||
|
replyCreate := time.Now().Add(-30 * time.Second).UnixMilli()
|
||||||
|
data = imports.LineImportWorkerData{
|
||||||
|
LineImportData: imports.LineImportData{
|
||||||
|
Post: &imports.PostImportData{
|
||||||
|
Team: &teamName,
|
||||||
|
Channel: &channelName,
|
||||||
|
User: &user.Username,
|
||||||
|
Message: model.NewPointer("Flagged Message"),
|
||||||
|
CreateAt: model.NewPointer(importCreate),
|
||||||
|
Replies: &[]imports.ReplyImportData{{
|
||||||
|
User: &user.Username,
|
||||||
|
Message: model.NewPointer("Reply"),
|
||||||
|
CreateAt: model.NewPointer(replyCreate),
|
||||||
|
FlaggedBy: &[]string{user2.Username},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
LineNumber: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
errLine, err = th.App.importMultiplePostLines(th.Context, []imports.LineImportWorkerData{data}, false, true)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 0, errLine)
|
||||||
|
|
||||||
|
resultPosts, nErr = th.App.Srv().Store().Post().GetPostsCreatedAt(channel.Id, replyCreate)
|
||||||
|
require.NoError(t, nErr)
|
||||||
|
require.Equal(t, 1, len(resultPosts))
|
||||||
|
|
||||||
|
pref, err := th.App.ch.srv.Store().Preference().GetCategoryAndName(model.PreferenceCategoryFlaggedPost, resultPosts[0].Id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Len(t, pref, 1)
|
||||||
|
assert.Equal(t, user2.Id, pref[0].UserId)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestImportImportPost(t *testing.T) {
|
func TestImportImportPost(t *testing.T) {
|
||||||
|
|||||||
@@ -73,6 +73,14 @@ type UserImportData struct {
|
|||||||
ShowUnreadSection *string `json:"show_unread_section,omitempty"`
|
ShowUnreadSection *string `json:"show_unread_section,omitempty"`
|
||||||
DeleteAt *int64 `json:"delete_at,omitempty"`
|
DeleteAt *int64 `json:"delete_at,omitempty"`
|
||||||
|
|
||||||
|
SendOnCtrlEnter *string `json:"send_on_ctrl_enter,omitempty"`
|
||||||
|
CodeBlockCtrlEnter *string `json:"code_block_ctrl_enter,omitempty"`
|
||||||
|
ShowJoinLeave *string `json:"show_join_leave,omitempty"`
|
||||||
|
ShowUnreadScrollPosition *string `json:"show_unread_scroll_position,omitempty"`
|
||||||
|
SyncDrafts *string `json:"sync_drafts,omitempty"`
|
||||||
|
LimitVisibleDmsGms *string `json:"limit_visible_dms_gms,omitempty"`
|
||||||
|
NameFormat *string `json:"name_format,omitempty"`
|
||||||
|
|
||||||
Teams *[]UserTeamImportData `json:"teams,omitempty"`
|
Teams *[]UserTeamImportData `json:"teams,omitempty"`
|
||||||
|
|
||||||
Theme *string `json:"theme,omitempty"`
|
Theme *string `json:"theme,omitempty"`
|
||||||
@@ -169,6 +177,7 @@ type ReplyImportData struct {
|
|||||||
FlaggedBy *[]string `json:"flagged_by,omitempty"`
|
FlaggedBy *[]string `json:"flagged_by,omitempty"`
|
||||||
Reactions *[]ReactionImportData `json:"reactions,omitempty"`
|
Reactions *[]ReactionImportData `json:"reactions,omitempty"`
|
||||||
Attachments *[]AttachmentImportData `json:"attachments,omitempty"`
|
Attachments *[]AttachmentImportData `json:"attachments,omitempty"`
|
||||||
|
IsPinned *bool `json:"is_pinned,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostImportData struct {
|
type PostImportData struct {
|
||||||
@@ -210,7 +219,7 @@ type DirectPostImportData struct {
|
|||||||
CreateAt *int64 `json:"create_at"`
|
CreateAt *int64 `json:"create_at"`
|
||||||
EditAt *int64 `json:"edit_at"`
|
EditAt *int64 `json:"edit_at"`
|
||||||
|
|
||||||
FlaggedBy *[]string `json:"flagged_by"`
|
FlaggedBy *[]string `json:"flagged_by,omitempty"`
|
||||||
Reactions *[]ReactionImportData `json:"reactions"`
|
Reactions *[]ReactionImportData `json:"reactions"`
|
||||||
Replies *[]ReplyImportData `json:"replies"`
|
Replies *[]ReplyImportData `json:"replies"`
|
||||||
Attachments *[]AttachmentImportData `json:"attachments"`
|
Attachments *[]AttachmentImportData `json:"attachments"`
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ func (a *App) createUserOrGuest(c request.CTX, user *model.User, guest bool) (*m
|
|||||||
a.sendUpdatedUserEvent(*nUser)
|
a.sendUpdatedUserEvent(*nUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
recommendedNextStepsPref := model.Preference{UserId: ruser.Id, Category: model.PreferenceRecommendedNextSteps, Name: "hide", Value: "false"}
|
recommendedNextStepsPref := model.Preference{UserId: ruser.Id, Category: model.PreferenceCategoryRecommendedNextSteps, Name: model.PreferenceNameRecommendedNextStepsHide, Value: "false"}
|
||||||
tutorialStepPref := model.Preference{UserId: ruser.Id, Category: model.PreferenceCategoryTutorialSteps, Name: ruser.Id, Value: "0"}
|
tutorialStepPref := model.Preference{UserId: ruser.Id, Category: model.PreferenceCategoryTutorialSteps, Name: ruser.Id, Value: "0"}
|
||||||
gmASdmPref := model.Preference{UserId: ruser.Id, Category: model.PreferenceCategorySystemNotice, Name: "GMasDM", Value: "true"}
|
gmASdmPref := model.Preference{UserId: ruser.Id, Category: model.PreferenceCategorySystemNotice, Name: "GMasDM", Value: "true"}
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,15 @@ func postSliceColumns() []string {
|
|||||||
return cols
|
return cols
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func postSliceColumnsWithName(name string) []string {
|
||||||
|
colInfos := postSliceColumnsWithTypes()
|
||||||
|
cols := make([]string, len(colInfos))
|
||||||
|
for i, colInfo := range colInfos {
|
||||||
|
cols[i] = name + "." + colInfo.Name
|
||||||
|
}
|
||||||
|
return cols
|
||||||
|
}
|
||||||
|
|
||||||
func postSliceCoalesceQuery() string {
|
func postSliceCoalesceQuery() string {
|
||||||
colInfos := postSliceColumnsWithTypes()
|
colInfos := postSliceColumnsWithTypes()
|
||||||
cols := make([]string, len(colInfos))
|
cols := make([]string, len(colInfos))
|
||||||
@@ -2661,13 +2670,22 @@ func (s *SqlPostStore) GetParentsForExportAfter(limit int, afterId string, inclu
|
|||||||
excludeDeletedCond = append(excludeDeletedCond, sq.Eq{"Channels.DeleteAt": 0})
|
excludeDeletedCond = append(excludeDeletedCond, sq.Eq{"Channels.DeleteAt": 0})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aggFn := "COALESCE(json_agg(u1.username) FILTER (WHERE u1.username IS NOT NULL), '[]')"
|
||||||
|
if s.DriverName() == model.DatabaseDriverMysql {
|
||||||
|
aggFn = "IF (COUNT(u1.Username) = 0, JSON_ARRAY(), JSON_ARRAYAGG(u1.Username))"
|
||||||
|
}
|
||||||
|
result := []*model.PostForExport{}
|
||||||
|
|
||||||
builder := s.getQueryBuilder().
|
builder := s.getQueryBuilder().
|
||||||
Select("p1.*, Users.Username as Username, Teams.Name as TeamName, Channels.Name as ChannelName").
|
Select(fmt.Sprintf("%s, Users.Username as Username, Teams.Name as TeamName, Channels.Name as ChannelName, %s as FlaggedBy", strings.Join(postSliceColumnsWithName("p1"), ", "), aggFn)).
|
||||||
FromSelect(sq.Select("*").From("Posts").Where(sq.Eq{"Posts.Id": rootIds}), "p1").
|
FromSelect(sq.Select("*").From("Posts").Where(sq.Eq{"Posts.Id": rootIds}), "p1").
|
||||||
|
LeftJoin("Preferences ON p1.Id = Preferences.Name").
|
||||||
|
LeftJoin("Users u1 ON Preferences.UserId = u1.Id").
|
||||||
InnerJoin("Channels ON p1.ChannelId = Channels.Id").
|
InnerJoin("Channels ON p1.ChannelId = Channels.Id").
|
||||||
InnerJoin("Teams ON Channels.TeamId = Teams.Id").
|
InnerJoin("Teams ON Channels.TeamId = Teams.Id").
|
||||||
InnerJoin("Users ON p1.UserId = Users.Id").
|
InnerJoin("Users ON p1.UserId = Users.Id").
|
||||||
Where(excludeDeletedCond).
|
Where(excludeDeletedCond).
|
||||||
|
GroupBy(fmt.Sprintf("%s, Users.Username, Teams.Name, Channels.Name", strings.Join(postSliceColumnsWithName("p1"), ", "))).
|
||||||
OrderBy("p1.Id")
|
OrderBy("p1.Id")
|
||||||
|
|
||||||
query, args, err := builder.ToSql()
|
query, args, err := builder.ToSql()
|
||||||
@@ -2675,56 +2693,72 @@ func (s *SqlPostStore) GetParentsForExportAfter(limit int, afterId string, inclu
|
|||||||
return nil, errors.Wrap(err, "postsForExport_toSql")
|
return nil, errors.Wrap(err, "postsForExport_toSql")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.GetSearchReplicaX().Select(&postsForExport, query, args...)
|
err = s.GetSearchReplicaX().Select(&result, query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to find Posts")
|
return nil, errors.Wrap(err, "failed to find Posts")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(postsForExport) == 0 {
|
if len(result) == 0 {
|
||||||
// All of the posts were in channels or teams that were deleted.
|
// All of the posts were in channels or teams that were deleted.
|
||||||
// Update the afterId and try again.
|
// Update the afterId and try again.
|
||||||
afterId = rootIds[len(rootIds)-1]
|
afterId = rootIds[len(rootIds)-1]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return postsForExport, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SqlPostStore) GetRepliesForExport(rootId string) ([]*model.ReplyForExport, error) {
|
func (s *SqlPostStore) GetRepliesForExport(rootId string) ([]*model.ReplyForExport, error) {
|
||||||
posts := []*model.ReplyForExport{}
|
aggFn := "COALESCE(json_agg(u1.username) FILTER (WHERE u1.username IS NOT NULL), '[]')"
|
||||||
err := s.GetSearchReplicaX().Select(&posts, `
|
if s.DriverName() == model.DatabaseDriverMysql {
|
||||||
SELECT
|
aggFn = "IF (COUNT(u1.Username) = 0, JSON_ARRAY(), JSON_ARRAYAGG(u1.Username))"
|
||||||
Posts.*,
|
}
|
||||||
Users.Username as Username
|
result := []*model.ReplyForExport{}
|
||||||
FROM
|
|
||||||
Posts
|
qb := s.getQueryBuilder().Select(fmt.Sprintf("Posts.*, u2.Username as Username, %s as FlaggedBy", aggFn)).
|
||||||
INNER JOIN
|
From("Posts").
|
||||||
Users ON Posts.UserId = Users.Id
|
LeftJoin("Preferences ON Posts.Id = Preferences.Name").
|
||||||
WHERE
|
LeftJoin("Users u1 ON Preferences.UserId = u1.Id").
|
||||||
Posts.RootId = ?
|
InnerJoin("Users u2 ON Posts.UserId = u2.Id").
|
||||||
AND Posts.DeleteAt = 0
|
Where(sq.And{sq.Eq{"Posts.RootId": rootId}, sq.Eq{"Posts.DeleteAt": 0}}).
|
||||||
ORDER BY
|
GroupBy("Posts.Id, u2.Username").
|
||||||
Posts.Id`, rootId)
|
OrderBy("Posts.Id")
|
||||||
|
|
||||||
|
query, args, err := qb.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "postsForExport_toSql")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.GetSearchReplicaX().Select(&result, query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to find Posts")
|
return nil, errors.Wrap(err, "failed to find Posts")
|
||||||
}
|
}
|
||||||
|
|
||||||
return posts, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SqlPostStore) GetDirectPostParentsForExportAfter(limit int, afterId string, includeArchivedChannels bool) ([]*model.DirectPostForExport, error) {
|
func (s *SqlPostStore) GetDirectPostParentsForExportAfter(limit int, afterId string, includeArchivedChannels bool) ([]*model.DirectPostForExport, error) {
|
||||||
|
aggFn := "COALESCE(json_agg(u1.username) FILTER (WHERE u1.username IS NOT NULL), '[]')"
|
||||||
|
if s.DriverName() == model.DatabaseDriverMysql {
|
||||||
|
aggFn = "IF (COUNT(u1.Username) = 0, JSON_ARRAY(), JSON_ARRAYAGG(u1.Username))"
|
||||||
|
}
|
||||||
|
result := []*model.DirectPostForExport{}
|
||||||
|
|
||||||
query := s.getQueryBuilder().
|
query := s.getQueryBuilder().
|
||||||
Select("p.*", "Users.Username as User").
|
Select(fmt.Sprintf("p.*, u2.Username as User, %s as FlaggedBy", aggFn)).
|
||||||
From("Posts p").
|
From("Posts p").
|
||||||
|
LeftJoin("Preferences ON p.Id = Preferences.Name").
|
||||||
|
LeftJoin("Users u1 ON Preferences.UserId = u1.Id").
|
||||||
Join("Channels ON p.ChannelId = Channels.Id").
|
Join("Channels ON p.ChannelId = Channels.Id").
|
||||||
Join("Users ON p.UserId = Users.Id").
|
Join("Users u2 ON p.UserId = u2.Id").
|
||||||
Where(sq.And{
|
Where(sq.And{
|
||||||
sq.Gt{"p.Id": afterId},
|
sq.Gt{"p.Id": afterId},
|
||||||
sq.Eq{"p.RootId": ""},
|
sq.Eq{"p.RootId": ""},
|
||||||
sq.Eq{"p.DeleteAt": 0},
|
sq.Eq{"p.DeleteAt": 0},
|
||||||
sq.Eq{"Channels.Type": []model.ChannelType{model.ChannelTypeDirect, model.ChannelTypeGroup}},
|
sq.Eq{"Channels.Type": []model.ChannelType{model.ChannelTypeDirect, model.ChannelTypeGroup}},
|
||||||
}).
|
}).
|
||||||
|
GroupBy("p.Id, u2.Username").
|
||||||
OrderBy("p.Id").
|
OrderBy("p.Id").
|
||||||
Limit(uint64(limit))
|
Limit(uint64(limit))
|
||||||
|
|
||||||
@@ -2739,13 +2773,12 @@ func (s *SqlPostStore) GetDirectPostParentsForExportAfter(limit int, afterId str
|
|||||||
return nil, errors.Wrap(err, "post_tosql")
|
return nil, errors.Wrap(err, "post_tosql")
|
||||||
}
|
}
|
||||||
|
|
||||||
posts := []*model.DirectPostForExport{}
|
if err2 := s.GetReplicaX().Select(&result, queryString, args...); err2 != nil {
|
||||||
if err2 := s.GetReplicaX().Select(&posts, queryString, args...); err2 != nil {
|
|
||||||
return nil, errors.Wrap(err2, "failed to find Posts")
|
return nil, errors.Wrap(err2, "failed to find Posts")
|
||||||
}
|
}
|
||||||
var channelIds []string
|
var channelIds []string
|
||||||
for _, post := range posts {
|
for _, p := range result {
|
||||||
channelIds = append(channelIds, post.ChannelId)
|
channelIds = append(channelIds, p.ChannelId)
|
||||||
}
|
}
|
||||||
query = s.getQueryBuilder().
|
query = s.getQueryBuilder().
|
||||||
Select("u.Username as Username, ChannelId, UserId, cm.Roles as Roles, LastViewedAt, MsgCount, MentionCount, MentionCountRoot, cm.NotifyProps as NotifyProps, LastUpdateAt, SchemeUser, SchemeAdmin, (SchemeGuest IS NOT NULL AND SchemeGuest) as SchemeGuest").
|
Select("u.Username as Username, ChannelId, UserId, cm.Roles as Roles, LastViewedAt, MsgCount, MentionCount, MentionCountRoot, cm.NotifyProps as NotifyProps, LastUpdateAt, SchemeUser, SchemeAdmin, (SchemeGuest IS NOT NULL AND SchemeGuest) as SchemeGuest").
|
||||||
@@ -2761,13 +2794,13 @@ func (s *SqlPostStore) GetDirectPostParentsForExportAfter(limit int, afterId str
|
|||||||
}
|
}
|
||||||
|
|
||||||
channelMembers := []*model.ChannelMemberForExport{}
|
channelMembers := []*model.ChannelMemberForExport{}
|
||||||
if err := s.GetReplicaX().Select(&channelMembers, queryString, args...); err != nil {
|
if err = s.GetReplicaX().Select(&channelMembers, queryString, args...); err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to find ChannelMembers")
|
return nil, errors.Wrap(err, "failed to find ChannelMembers")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a map of channels and their posts
|
// Build a map of channels and their posts
|
||||||
postsChannelMap := make(map[string][]*model.DirectPostForExport)
|
postsChannelMap := make(map[string][]*model.DirectPostForExport)
|
||||||
for _, post := range posts {
|
for _, post := range result {
|
||||||
post.ChannelMembers = &[]string{}
|
post.ChannelMembers = &[]string{}
|
||||||
postsChannelMap[post.ChannelId] = append(postsChannelMap[post.ChannelId], post)
|
postsChannelMap[post.ChannelId] = append(postsChannelMap[post.ChannelId], post)
|
||||||
}
|
}
|
||||||
@@ -2784,7 +2817,8 @@ func (s *SqlPostStore) GetDirectPostParentsForExportAfter(limit int, afterId str
|
|||||||
*post.ChannelMembers = channelMembersMap[channelId]
|
*post.ChannelMembers = channelMembersMap[channelId]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return posts, nil
|
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:unparam
|
//nolint:unparam
|
||||||
|
|||||||
@@ -4349,6 +4349,28 @@ func testPostStoreGetParentsForExportAfter(t *testing.T, rctx request.CTX, ss st
|
|||||||
}
|
}
|
||||||
assert.True(t, found)
|
assert.True(t, found)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("with flagged post", func(t *testing.T) {
|
||||||
|
err := ss.Preference().Save(model.Preferences([]model.Preference{
|
||||||
|
{
|
||||||
|
UserId: u1.Id,
|
||||||
|
Category: model.PreferenceCategoryFlaggedPost,
|
||||||
|
Name: p1.Id,
|
||||||
|
Value: "true",
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
posts, err := ss.Post().GetParentsForExportAfter(10000, strings.Repeat("0", 26), false)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
for _, p := range posts {
|
||||||
|
if p.Id == p1.Id {
|
||||||
|
require.NotNil(t, p.FlaggedBy)
|
||||||
|
assert.Equal(t, model.StringArray([]string{u1.Username}), p.FlaggedBy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPostStoreGetRepliesForExport(t *testing.T, rctx request.CTX, ss store.Store) {
|
func testPostStoreGetRepliesForExport(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||||
@@ -4394,7 +4416,7 @@ func testPostStoreGetRepliesForExport(t *testing.T, rctx request.CTX, ss store.S
|
|||||||
r1, err := ss.Post().GetRepliesForExport(p1.Id)
|
r1, err := ss.Post().GetRepliesForExport(p1.Id)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Len(t, r1, 1)
|
require.Len(t, r1, 1)
|
||||||
|
|
||||||
reply1 := r1[0]
|
reply1 := r1[0]
|
||||||
assert.Equal(t, reply1.Id, p2.Id)
|
assert.Equal(t, reply1.Id, p2.Id)
|
||||||
@@ -4409,7 +4431,7 @@ func testPostStoreGetRepliesForExport(t *testing.T, rctx request.CTX, ss store.S
|
|||||||
r1, err = ss.Post().GetRepliesForExport(p1.Id)
|
r1, err = ss.Post().GetRepliesForExport(p1.Id)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Len(t, r1, 1)
|
require.Len(t, r1, 1)
|
||||||
|
|
||||||
reply1 = r1[0]
|
reply1 = r1[0]
|
||||||
assert.Equal(t, reply1.Id, p2.Id)
|
assert.Equal(t, reply1.Id, p2.Id)
|
||||||
|
|||||||
@@ -235,17 +235,20 @@ type PostForExport struct {
|
|||||||
ChannelName string
|
ChannelName string
|
||||||
Username string
|
Username string
|
||||||
ReplyCount int
|
ReplyCount int
|
||||||
|
FlaggedBy StringArray
|
||||||
}
|
}
|
||||||
|
|
||||||
type DirectPostForExport struct {
|
type DirectPostForExport struct {
|
||||||
Post
|
Post
|
||||||
User string
|
User string
|
||||||
ChannelMembers *[]string
|
ChannelMembers *[]string
|
||||||
|
FlaggedBy StringArray
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReplyForExport struct {
|
type ReplyForExport struct {
|
||||||
Post
|
Post
|
||||||
Username string
|
Username string
|
||||||
|
FlaggedBy StringArray
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostForIndexing struct {
|
type PostForIndexing struct {
|
||||||
|
|||||||
@@ -13,15 +13,70 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// The primary key for the preference table is the combination of User.Id, Category, and Name.
|
||||||
|
|
||||||
|
// PreferenceCategoryDirectChannelShow and PreferenceCategoryGroupChannelShow
|
||||||
|
// are used to store the user's preferences for which channels to show in the sidebar.
|
||||||
|
// The Name field is the channel ID.
|
||||||
PreferenceCategoryDirectChannelShow = "direct_channel_show"
|
PreferenceCategoryDirectChannelShow = "direct_channel_show"
|
||||||
PreferenceCategoryGroupChannelShow = "group_channel_show"
|
PreferenceCategoryGroupChannelShow = "group_channel_show"
|
||||||
|
// PreferenceCategoryTutorialStep is used to store the user's progress in the tutorial.
|
||||||
|
// The Name field is the user ID again (for whatever reason).
|
||||||
PreferenceCategoryTutorialSteps = "tutorial_step"
|
PreferenceCategoryTutorialSteps = "tutorial_step"
|
||||||
|
// PreferenceCategoryAdvancedSettings has settings for the user's advanced settings.
|
||||||
|
// The Name field is the setting name. Possible values are:
|
||||||
|
// - "formatting"
|
||||||
|
// - "send_on_ctrl_enter"
|
||||||
|
// - "join_leave"
|
||||||
|
// - "unread_scroll_position"
|
||||||
|
// - "sync_drafts"
|
||||||
|
// - "feature_enabled_markdown_preview" <- deprecated in favor of "formatting"
|
||||||
PreferenceCategoryAdvancedSettings = "advanced_settings"
|
PreferenceCategoryAdvancedSettings = "advanced_settings"
|
||||||
|
// PreferenceCategoryFlaggedPost is used to store the user's saved posts.
|
||||||
|
// The Name field is the post ID.
|
||||||
PreferenceCategoryFlaggedPost = "flagged_post"
|
PreferenceCategoryFlaggedPost = "flagged_post"
|
||||||
|
// PreferenceCategoryFavoriteChannel is used to store the user's favorite channels to be
|
||||||
|
// shown in the sidebar. The Name field is the channel ID.
|
||||||
PreferenceCategoryFavoriteChannel = "favorite_channel"
|
PreferenceCategoryFavoriteChannel = "favorite_channel"
|
||||||
|
// PreferenceCategorySidebarSettings is used to store the user's sidebar settings.
|
||||||
|
// The Name field is the setting name. (ie. PreferenceNameShowUnreadSection or PreferenceLimitVisibleDmsGms)
|
||||||
PreferenceCategorySidebarSettings = "sidebar_settings"
|
PreferenceCategorySidebarSettings = "sidebar_settings"
|
||||||
|
// PreferenceCategoryDisplaySettings is used to store the user's various display settings.
|
||||||
|
// The possible Name fields are:
|
||||||
|
// - PreferenceNameUseMilitaryTime
|
||||||
|
// - PreferenceNameCollapseSetting
|
||||||
|
// - PreferenceNameMessageDisplay
|
||||||
|
// - PreferenceNameCollapseConsecutive
|
||||||
|
// - PreferenceNameColorizeUsernames
|
||||||
|
// - PreferenceNameChannelDisplayMode
|
||||||
|
// - PreferenceNameNameFormat
|
||||||
PreferenceCategoryDisplaySettings = "display_settings"
|
PreferenceCategoryDisplaySettings = "display_settings"
|
||||||
|
// PreferenceCategorySystemNotice is used store system admin notices.
|
||||||
|
// Possible Name values are not defined here. It can be anything with the notice name.
|
||||||
|
PreferenceCategorySystemNotice = "system_notice"
|
||||||
|
// Deprecated: PreferenceCategoryLast is not used anymore.
|
||||||
|
PreferenceCategoryLast = "last"
|
||||||
|
// PreferenceCategoryCustomStatus is used to store the user's custom status preferences.
|
||||||
|
// Possible Name values are:
|
||||||
|
// - PreferenceNameRecentCustomStatuses
|
||||||
|
// - PreferenceNameCustomStatusTutorialState
|
||||||
|
// - PreferenceCustomStatusModalViewed
|
||||||
|
PreferenceCategoryCustomStatus = "custom_status"
|
||||||
|
// PreferenceCategoryNotifications is used to store the user's notification settings.
|
||||||
|
// Possible Name values are:
|
||||||
|
// - PreferenceNameEmailInterval
|
||||||
|
PreferenceCategoryNotifications = "notifications"
|
||||||
|
|
||||||
|
// Deprecated: PreferenceRecommendedNextSteps is not used anymore.
|
||||||
|
// Use PreferenceCategoryRecommendedNextSteps instead.
|
||||||
|
// PreferenceRecommendedNextSteps is actually a Category. The only possible
|
||||||
|
// Name vaule is PreferenceRecommendedNextStepsHide for now.
|
||||||
|
PreferenceRecommendedNextSteps = PreferenceCategoryRecommendedNextSteps
|
||||||
|
PreferenceCategoryRecommendedNextSteps = "recommended_next_steps"
|
||||||
|
|
||||||
|
// PreferenceCategoryTheme has the name for the team id where theme is set.
|
||||||
|
PreferenceCategoryTheme = "theme"
|
||||||
|
|
||||||
PreferenceNameCollapsedThreadsEnabled = "collapsed_reply_threads"
|
PreferenceNameCollapsedThreadsEnabled = "collapsed_reply_threads"
|
||||||
PreferenceNameChannelDisplayMode = "channel_display_mode"
|
PreferenceNameChannelDisplayMode = "channel_display_mode"
|
||||||
PreferenceNameCollapseSetting = "collapse_previews"
|
PreferenceNameCollapseSetting = "collapse_previews"
|
||||||
@@ -30,27 +85,25 @@ const (
|
|||||||
PreferenceNameColorizeUsernames = "colorize_usernames"
|
PreferenceNameColorizeUsernames = "colorize_usernames"
|
||||||
PreferenceNameNameFormat = "name_format"
|
PreferenceNameNameFormat = "name_format"
|
||||||
PreferenceNameUseMilitaryTime = "use_military_time"
|
PreferenceNameUseMilitaryTime = "use_military_time"
|
||||||
PreferenceRecommendedNextSteps = "recommended_next_steps"
|
|
||||||
|
|
||||||
PreferenceCategorySystemNotice = "system_notice"
|
PreferenceNameShowUnreadSection = "show_unread_section"
|
||||||
|
PreferenceLimitVisibleDmsGms = "limit_visible_dms_gms"
|
||||||
|
|
||||||
PreferenceCategoryTheme = "theme"
|
PreferenceMaxLimitVisibleDmsGmsValue = 40
|
||||||
// the name for theme props is the team id
|
MaxPreferenceValueLength = 20000
|
||||||
|
|
||||||
PreferenceCategoryAuthorizedOAuthApp = "oauth_app"
|
PreferenceCategoryAuthorizedOAuthApp = "oauth_app"
|
||||||
// the name for oauth_app is the client_id and value is the current scope
|
// the name for oauth_app is the client_id and value is the current scope
|
||||||
|
|
||||||
PreferenceCategoryLast = "last"
|
// Deprecated: PreferenceCategoryLastChannel is not used anymore.
|
||||||
PreferenceNameLastChannel = "channel"
|
PreferenceNameLastChannel = "channel"
|
||||||
|
// Deprecated: PreferenceCategoryLastTeam is not used anymore.
|
||||||
PreferenceNameLastTeam = "team"
|
PreferenceNameLastTeam = "team"
|
||||||
|
|
||||||
PreferenceCategoryCustomStatus = "custom_status"
|
|
||||||
PreferenceNameRecentCustomStatuses = "recent_custom_statuses"
|
PreferenceNameRecentCustomStatuses = "recent_custom_statuses"
|
||||||
PreferenceNameCustomStatusTutorialState = "custom_status_tutorial_state"
|
PreferenceNameCustomStatusTutorialState = "custom_status_tutorial_state"
|
||||||
|
|
||||||
PreferenceCustomStatusModalViewed = "custom_status_modal_viewed"
|
PreferenceCustomStatusModalViewed = "custom_status_modal_viewed"
|
||||||
|
|
||||||
PreferenceCategoryNotifications = "notifications"
|
|
||||||
PreferenceNameEmailInterval = "email_interval"
|
PreferenceNameEmailInterval = "email_interval"
|
||||||
|
|
||||||
PreferenceEmailIntervalNoBatchingSeconds = "30" // the "immediate" setting is actually 30s
|
PreferenceEmailIntervalNoBatchingSeconds = "30" // the "immediate" setting is actually 30s
|
||||||
@@ -62,9 +115,7 @@ const (
|
|||||||
PreferenceEmailIntervalHourAsSeconds = "3600"
|
PreferenceEmailIntervalHourAsSeconds = "3600"
|
||||||
PreferenceCloudUserEphemeralInfo = "cloud_user_ephemeral_info"
|
PreferenceCloudUserEphemeralInfo = "cloud_user_ephemeral_info"
|
||||||
|
|
||||||
PreferenceLimitVisibleDmsGms = "limit_visible_dms_gms"
|
PreferenceNameRecommendedNextStepsHide = "hide"
|
||||||
PreferenceMaxLimitVisibleDmsGmsValue = 40
|
|
||||||
MaxPreferenceValueLength = 20000
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Preference struct {
|
type Preference struct {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user