Merge branch 'master' into advanced-permissions-phase-1
Этот коммит содержится в:
10
app/admin.go
10
app/admin.go
@@ -15,7 +15,6 @@ import (
|
||||
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store/sqlstore"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
@@ -141,10 +140,11 @@ func (a *App) InvalidateAllCachesSkipSend() {
|
||||
l4g.Info(utils.T("api.context.invalidate_all_caches"))
|
||||
a.sessionCache.Purge()
|
||||
ClearStatusCache()
|
||||
sqlstore.ClearChannelCaches()
|
||||
sqlstore.ClearUserCaches()
|
||||
sqlstore.ClearPostCaches()
|
||||
sqlstore.ClearWebhookCaches()
|
||||
a.Srv.Store.Channel().ClearCaches()
|
||||
a.Srv.Store.User().ClearCaches()
|
||||
a.Srv.Store.Post().ClearCaches()
|
||||
a.Srv.Store.FileInfo().ClearCaches()
|
||||
a.Srv.Store.Webhook().ClearCaches()
|
||||
a.LoadLicense()
|
||||
}
|
||||
|
||||
|
||||
18
app/app.go
18
app/app.go
@@ -133,8 +133,24 @@ func New(options ...Option) (outApp *App, outErr error) {
|
||||
|
||||
app.configListenerId = app.AddConfigListener(func(_, _ *model.Config) {
|
||||
app.configOrLicenseListener()
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CONFIG_CHANGED, "", "", "", nil)
|
||||
|
||||
message.Add("config", app.ClientConfigWithNoAccounts())
|
||||
app.Go(func() {
|
||||
app.Publish(message)
|
||||
})
|
||||
})
|
||||
app.licenseListenerId = app.AddLicenseListener(func() {
|
||||
app.configOrLicenseListener()
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_LICENSE_CHANGED, "", "", "", nil)
|
||||
message.Add("license", app.GetSanitizedClientLicense())
|
||||
app.Go(func() {
|
||||
app.Publish(message)
|
||||
})
|
||||
|
||||
})
|
||||
app.licenseListenerId = app.AddLicenseListener(app.configOrLicenseListener)
|
||||
app.regenerateClientConfig()
|
||||
|
||||
l4g.Info(utils.T("api.server.new_server.init.info"))
|
||||
|
||||
@@ -143,10 +143,6 @@ func (me *TestHelper) InitBasic() *TestHelper {
|
||||
return me
|
||||
}
|
||||
|
||||
func (me *TestHelper) MakeUsername() string {
|
||||
return "un_" + model.NewId()
|
||||
}
|
||||
|
||||
func (me *TestHelper) MakeEmail() string {
|
||||
return "success_" + model.NewId() + "@simulator.amazonses.com"
|
||||
}
|
||||
@@ -199,10 +195,6 @@ func (me *TestHelper) CreateChannel(team *model.Team) *model.Channel {
|
||||
return me.createChannel(team, model.CHANNEL_OPEN)
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreatePrivateChannel(team *model.Team) *model.Channel {
|
||||
return me.createChannel(team, model.CHANNEL_PRIVATE)
|
||||
}
|
||||
|
||||
func (me *TestHelper) createChannel(team *model.Team, channelType string) *model.Channel {
|
||||
id := model.NewId()
|
||||
|
||||
@@ -262,6 +254,22 @@ func (me *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
member, err := me.App.AddUserToChannel(user, channel)
|
||||
if err != nil {
|
||||
l4g.Error(err.Error())
|
||||
l4g.Close()
|
||||
time.Sleep(time.Second)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
|
||||
return member
|
||||
}
|
||||
|
||||
func (me *TestHelper) TearDown() {
|
||||
me.App.Shutdown()
|
||||
os.Remove(me.tempConfigPath)
|
||||
|
||||
@@ -9,16 +9,15 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
USER_PASSWORD = "passwd"
|
||||
CHANNEL_TYPE = model.CHANNEL_OPEN
|
||||
FUZZ_USER_EMAIL_PREFIX_LEN = 10
|
||||
BTEST_TEAM_DISPLAY_NAME = "TestTeam"
|
||||
BTEST_TEAM_NAME = "z-z-testdomaina"
|
||||
BTEST_TEAM_EMAIL = "test@nowhere.com"
|
||||
BTEST_TEAM_TYPE = model.TEAM_OPEN
|
||||
BTEST_USER_NAME = "Mr. Testing Tester"
|
||||
BTEST_USER_EMAIL = "success+ttester@simulator.amazonses.com"
|
||||
BTEST_USER_PASSWORD = "passwd"
|
||||
USER_PASSWORD = "passwd"
|
||||
CHANNEL_TYPE = model.CHANNEL_OPEN
|
||||
BTEST_TEAM_DISPLAY_NAME = "TestTeam"
|
||||
BTEST_TEAM_NAME = "z-z-testdomaina"
|
||||
BTEST_TEAM_EMAIL = "test@nowhere.com"
|
||||
BTEST_TEAM_TYPE = model.TEAM_OPEN
|
||||
BTEST_USER_NAME = "Mr. Testing Tester"
|
||||
BTEST_USER_EMAIL = "success+ttester@simulator.amazonses.com"
|
||||
BTEST_USER_PASSWORD = "passwd"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -29,8 +28,5 @@ var (
|
||||
USER_EMAIL_LEN = utils.Range{Begin: 15, End: 30}
|
||||
CHANNEL_DISPLAY_NAME_LEN = utils.Range{Begin: 10, End: 20}
|
||||
CHANNEL_NAME_LEN = utils.Range{Begin: 5, End: 20}
|
||||
POST_MESSAGE_LEN = utils.Range{Begin: 100, End: 400}
|
||||
POST_HASHTAGS_NUM = utils.Range{Begin: 5, End: 10}
|
||||
POST_MENTIONS_NUM = utils.Range{Begin: 0, End: 3}
|
||||
TEST_IMAGE_FILENAMES = []string{"test.png", "testjpg.jpg", "testgif.gif"}
|
||||
)
|
||||
|
||||
@@ -90,18 +90,3 @@ func (cfg *AutoPostCreator) CreateRandomPost() (*model.Post, bool) {
|
||||
}
|
||||
return result.Data.(*model.Post), true
|
||||
}
|
||||
|
||||
func (cfg *AutoPostCreator) CreateTestPosts(rangePosts utils.Range) ([]*model.Post, bool) {
|
||||
numPosts := utils.RandIntFromRange(rangePosts)
|
||||
posts := make([]*model.Post, numPosts)
|
||||
|
||||
for i := 0; i < numPosts; i++ {
|
||||
var err bool
|
||||
posts[i], err = cfg.CreateRandomPost()
|
||||
if !err {
|
||||
return posts, false
|
||||
}
|
||||
}
|
||||
|
||||
return posts, true
|
||||
}
|
||||
|
||||
@@ -225,6 +225,14 @@ func (a *App) createDirectChannel(userId string, otherUserId string) (*model.Cha
|
||||
}
|
||||
} else {
|
||||
channel := result.Data.(*model.Channel)
|
||||
|
||||
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(userId, channel.Id, model.GetMillis()); result.Err != nil {
|
||||
l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
|
||||
}
|
||||
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(otherUserId, channel.Id, model.GetMillis()); result.Err != nil {
|
||||
l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
|
||||
}
|
||||
|
||||
return channel, nil
|
||||
}
|
||||
}
|
||||
@@ -369,7 +377,7 @@ func (a *App) postChannelPrivacyMessage(user *model.User, channel *model.Channel
|
||||
})[channel.Type]
|
||||
post := &model.Post{
|
||||
ChannelId: channel.Id,
|
||||
Message: fmt.Sprintf(utils.T("api.channel.change_channel_privacy." + privacy)),
|
||||
Message: utils.T("api.channel.change_channel_privacy." + privacy),
|
||||
Type: model.POST_CHANGE_CHANNEL_PRIVACY,
|
||||
UserId: user.Id,
|
||||
Props: model.StringInterface{
|
||||
@@ -549,7 +557,6 @@ func (a *App) DeleteChannel(channel *model.Channel, userId string) *model.AppErr
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_DELETED, channel.TeamId, "", "", nil)
|
||||
message.Add("channel_id", channel.Id)
|
||||
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
@@ -1059,7 +1066,7 @@ func (a *App) LeaveChannel(channelId string, userId string) *model.AppError {
|
||||
return err
|
||||
}
|
||||
|
||||
if channel.Name == model.DEFAULT_CHANNEL && *a.Config().ServiceSettings.ExperimentalEnableDefaultChannelLeaveJoinMessages == false {
|
||||
if channel.Name == model.DEFAULT_CHANNEL && !*a.Config().ServiceSettings.ExperimentalEnableDefaultChannelLeaveJoinMessages {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1097,7 +1104,9 @@ func (a *App) PostAddToChannelMessage(user *model.User, addedUser *model.User, c
|
||||
UserId: user.Id,
|
||||
RootId: postRootId,
|
||||
Props: model.StringInterface{
|
||||
"userId": user.Id,
|
||||
"username": user.Username,
|
||||
"addedUserId": addedUser.Id,
|
||||
"addedUsername": addedUser.Username,
|
||||
},
|
||||
}
|
||||
@@ -1117,7 +1126,9 @@ func (a *App) postAddToTeamMessage(user *model.User, addedUser *model.User, chan
|
||||
UserId: user.Id,
|
||||
RootId: postRootId,
|
||||
Props: model.StringInterface{
|
||||
"userId": user.Id,
|
||||
"username": user.Username,
|
||||
"addedUserId": addedUser.Id,
|
||||
"addedUsername": addedUser.Username,
|
||||
},
|
||||
}
|
||||
@@ -1136,6 +1147,7 @@ func (a *App) postRemoveFromChannelMessage(removerUserId string, removedUser *mo
|
||||
Type: model.POST_REMOVE_FROM_CHANNEL,
|
||||
UserId: removerUserId,
|
||||
Props: model.StringInterface{
|
||||
"removedUserId": removedUser.Id,
|
||||
"removedUsername": removedUser.Username,
|
||||
},
|
||||
}
|
||||
@@ -1170,17 +1182,13 @@ func (a *App) removeUserFromChannel(userIdToRemove string, removerUserId string,
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_REMOVED, "", channel.Id, "", nil)
|
||||
message.Add("user_id", userIdToRemove)
|
||||
message.Add("remover_id", removerUserId)
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
|
||||
// because the removed user no longer belongs to the channel we need to send a separate websocket event
|
||||
userMsg := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_REMOVED, "", "", userIdToRemove, nil)
|
||||
userMsg.Add("channel_id", channel.Id)
|
||||
userMsg.Add("remover_id", removerUserId)
|
||||
a.Go(func() {
|
||||
a.Publish(userMsg)
|
||||
})
|
||||
a.Publish(userMsg)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1250,9 +1258,7 @@ func (a *App) UpdateChannelLastViewedAt(channelIds []string, userId string) *mod
|
||||
for _, channelId := range channelIds {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, "", "", userId, nil)
|
||||
message.Add("channel_id", channelId)
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1329,9 +1335,7 @@ func (a *App) ViewChannel(view *model.ChannelView, userId string, clearPushNotif
|
||||
if *a.Config().ServiceSettings.EnableChannelViewedMessages && model.IsValidId(view.ChannelId) {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, "", "", userId, nil)
|
||||
message.Add("channel_id", view.ChannelId)
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
return times, nil
|
||||
@@ -1434,7 +1438,16 @@ func (a *App) GetDirectChannel(userId1, userId2 string) (*model.Channel, *model.
|
||||
}
|
||||
a.InvalidateCacheForUser(userId1)
|
||||
a.InvalidateCacheForUser(userId2)
|
||||
return result.Data.(*model.Channel), nil
|
||||
|
||||
channel := result.Data.(*model.Channel)
|
||||
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(userId1, channel.Id, model.GetMillis()); result.Err != nil {
|
||||
l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
|
||||
}
|
||||
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(userId2, channel.Id, model.GetMillis()); result.Err != nil {
|
||||
l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
|
||||
}
|
||||
|
||||
return channel, nil
|
||||
} else if result.Err != nil {
|
||||
return nil, model.NewAppError("GetOrCreateDMChannel", "web.incoming_webhook.channel.app_error", nil, "err="+result.Err.Message, result.Err.StatusCode)
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ func TestMoveChannel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinDefaultChannelsTownSquare(t *testing.T) {
|
||||
func TestJoinDefaultChannelsCreatesChannelMemberHistoryRecordTownSquare(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -136,7 +136,7 @@ func TestJoinDefaultChannelsTownSquare(t *testing.T) {
|
||||
assert.True(t, found)
|
||||
}
|
||||
|
||||
func TestJoinDefaultChannelsOffTopic(t *testing.T) {
|
||||
func TestJoinDefaultChannelsCreatesChannelMemberHistoryRecordOffTopic(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -162,7 +162,7 @@ func TestJoinDefaultChannelsOffTopic(t *testing.T) {
|
||||
assert.True(t, found)
|
||||
}
|
||||
|
||||
func TestCreateChannelPublic(t *testing.T) {
|
||||
func TestCreateChannelPublicCreatesChannelMemberHistoryRecord(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -176,7 +176,7 @@ func TestCreateChannelPublic(t *testing.T) {
|
||||
assert.Equal(t, publicChannel.Id, histories[0].ChannelId)
|
||||
}
|
||||
|
||||
func TestCreateChannelPrivate(t *testing.T) {
|
||||
func TestCreateChannelPrivateCreatesChannelMemberHistoryRecord(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -205,7 +205,7 @@ func TestUpdateChannelPrivacy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateGroupChannel(t *testing.T) {
|
||||
func TestCreateGroupChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -233,7 +233,62 @@ func TestCreateGroupChannel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddUserToChannel(t *testing.T) {
|
||||
func TestCreateDirectChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
user1 := th.CreateUser()
|
||||
user2 := th.CreateUser()
|
||||
|
||||
if channel, err := th.App.CreateDirectChannel(user1.Id, user2.Id); err != nil {
|
||||
t.Fatal("Failed to create direct channel. Error: " + err.Message)
|
||||
} else {
|
||||
// there should be a ChannelMemberHistory record for both users
|
||||
histories := store.Must(th.App.Srv.Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, channel.Id)).([]*model.ChannelMemberHistoryResult)
|
||||
assert.Len(t, histories, 2)
|
||||
|
||||
historyId0 := histories[0].UserId
|
||||
historyId1 := histories[1].UserId
|
||||
switch historyId0 {
|
||||
case user1.Id:
|
||||
assert.Equal(t, user2.Id, historyId1)
|
||||
case user2.Id:
|
||||
assert.Equal(t, user1.Id, historyId1)
|
||||
default:
|
||||
t.Fatal("Unexpected user id " + historyId0 + " in ChannelMemberHistory table")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDirectChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
user1 := th.CreateUser()
|
||||
user2 := th.CreateUser()
|
||||
|
||||
// this function call implicitly creates a direct channel between the two users if one doesn't already exist
|
||||
if channel, err := th.App.GetDirectChannel(user1.Id, user2.Id); err != nil {
|
||||
t.Fatal("Failed to create direct channel. Error: " + err.Message)
|
||||
} else {
|
||||
// there should be a ChannelMemberHistory record for both users
|
||||
histories := store.Must(th.App.Srv.Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, channel.Id)).([]*model.ChannelMemberHistoryResult)
|
||||
assert.Len(t, histories, 2)
|
||||
|
||||
historyId0 := histories[0].UserId
|
||||
historyId1 := histories[1].UserId
|
||||
switch historyId0 {
|
||||
case user1.Id:
|
||||
assert.Equal(t, user2.Id, historyId1)
|
||||
case user2.Id:
|
||||
assert.Equal(t, user1.Id, historyId1)
|
||||
default:
|
||||
t.Fatal("Unexpected user id " + historyId0 + " in ChannelMemberHistory table")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddUserToChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -263,7 +318,7 @@ func TestAddUserToChannel(t *testing.T) {
|
||||
assert.Equal(t, groupUserIds, channelMemberHistoryUserIds)
|
||||
}
|
||||
|
||||
func TestRemoveUserFromChannel(t *testing.T) {
|
||||
func TestRemoveUserFromChannelUpdatesChannelMemberHistoryRecord(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
|
||||
@@ -74,9 +74,7 @@ func (a *App) setCollapsePreference(args *model.CommandArgs, isCollapse bool) *m
|
||||
|
||||
socketMessage := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PREFERENCE_CHANGED, "", "", args.UserId, nil)
|
||||
socketMessage.Add("preference", pref.ToJson())
|
||||
a.Go(func() {
|
||||
a.Publish(socketMessage)
|
||||
})
|
||||
a.Publish(socketMessage)
|
||||
|
||||
var rmsg string
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
@@ -34,6 +35,7 @@ func (a *App) UpdateConfig(f func(*model.Config)) {
|
||||
updated := old.Clone()
|
||||
f(updated)
|
||||
a.config.Store(updated)
|
||||
|
||||
a.InvokeConfigListeners(old, updated)
|
||||
}
|
||||
|
||||
@@ -269,3 +271,16 @@ func (a *App) GetCookieDomain() string {
|
||||
func (a *App) GetSiteURL() string {
|
||||
return a.siteURL
|
||||
}
|
||||
|
||||
// ClientConfigWithNoAccounts gets the configuration in a format suitable for sending to the client.
|
||||
func (a *App) ClientConfigWithNoAccounts() map[string]string {
|
||||
respCfg := map[string]string{}
|
||||
for k, v := range a.ClientConfig() {
|
||||
respCfg[k] = v
|
||||
}
|
||||
|
||||
// NoAccounts is not actually part of the configuration, but is expected by the client.
|
||||
respCfg["NoAccounts"] = strconv.FormatBool(a.IsFirstUserAccount())
|
||||
|
||||
return respCfg
|
||||
}
|
||||
|
||||
@@ -63,3 +63,13 @@ func TestAsymmetricSigningKey(t *testing.T) {
|
||||
assert.NotNil(t, th.App.AsymmetricSigningKey())
|
||||
assert.NotEmpty(t, th.App.ClientConfig()["AsymmetricSigningPublicKey"])
|
||||
}
|
||||
|
||||
func TestClientConfigWithNoAccounts(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
config := th.App.ClientConfigWithNoAccounts()
|
||||
if _, ok := config["NoAccounts"]; !ok {
|
||||
t.Fatal("expected NoAccounts in returned config")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,11 +502,15 @@ func (a *App) trackConfig() {
|
||||
})
|
||||
|
||||
a.SendDiagnostic(TRACK_CONFIG_MESSAGE_EXPORT, map[string]interface{}{
|
||||
"enable_message_export": *cfg.MessageExportSettings.EnableExport,
|
||||
"export_format": *cfg.MessageExportSettings.ExportFormat,
|
||||
"daily_run_time": *cfg.MessageExportSettings.DailyRunTime,
|
||||
"default_export_from_timestamp": *cfg.MessageExportSettings.ExportFromTimestamp,
|
||||
"batch_size": *cfg.MessageExportSettings.BatchSize,
|
||||
"enable_message_export": *cfg.MessageExportSettings.EnableExport,
|
||||
"export_format": *cfg.MessageExportSettings.ExportFormat,
|
||||
"daily_run_time": *cfg.MessageExportSettings.DailyRunTime,
|
||||
"default_export_from_timestamp": *cfg.MessageExportSettings.ExportFromTimestamp,
|
||||
"batch_size": *cfg.MessageExportSettings.BatchSize,
|
||||
"global_relay_customer_type": *cfg.MessageExportSettings.GlobalRelaySettings.CustomerType,
|
||||
"is_default_global_relay_smtp_username": isDefault(*cfg.MessageExportSettings.GlobalRelaySettings.SmtpUsername, ""),
|
||||
"is_default_global_relay_smtp_password": isDefault(*cfg.MessageExportSettings.GlobalRelaySettings.SmtpPassword, ""),
|
||||
"is_default_global_relay_email_address": isDefault(*cfg.MessageExportSettings.GlobalRelaySettings.EmailAddress, ""),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,6 @@ func (a *App) CreateEmoji(sessionUserId string, emoji *model.Emoji, multiPartIma
|
||||
} else {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_EMOJI_ADDED, "", "", "", nil)
|
||||
message.Add("emoji", emoji.ToJson())
|
||||
|
||||
a.Publish(message)
|
||||
return result.Data.(*model.Emoji), nil
|
||||
}
|
||||
|
||||
14
app/login.go
14
app/login.go
@@ -9,8 +9,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/avct/uasurfer"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mssola/user_agent"
|
||||
)
|
||||
|
||||
func (a *App) AuthenticateUserForLogin(id, loginId, password, mfaToken, deviceId string, ldapOnly bool) (*model.User, *model.AppError) {
|
||||
@@ -71,19 +71,19 @@ func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User,
|
||||
session.SetExpireInDays(*a.Config().ServiceSettings.SessionLengthWebInDays)
|
||||
}
|
||||
|
||||
ua := user_agent.New(r.UserAgent())
|
||||
ua := uasurfer.Parse(r.UserAgent())
|
||||
|
||||
plat := ua.Platform()
|
||||
plat := ua.OS.Platform.String()
|
||||
if plat == "" {
|
||||
plat = "unknown"
|
||||
}
|
||||
|
||||
os := ua.OS()
|
||||
os := ua.OS.Name.String()
|
||||
if os == "" {
|
||||
os = "unknown"
|
||||
}
|
||||
|
||||
bname, bversion := ua.Browser()
|
||||
bname := ua.Browser.Name.String()
|
||||
if bname == "" {
|
||||
bname = "unknown"
|
||||
}
|
||||
@@ -92,9 +92,7 @@ func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User,
|
||||
bname = "Desktop App"
|
||||
}
|
||||
|
||||
if bversion == "" {
|
||||
bversion = "0.0"
|
||||
}
|
||||
bversion := ua.Browser.Version
|
||||
|
||||
session.AddProp(model.SESSION_PROP_PLATFORM, plat)
|
||||
session.AddProp(model.SESSION_PROP_OS, os)
|
||||
|
||||
18
app/post.go
18
app/post.go
@@ -84,9 +84,7 @@ func (a *App) CreatePostAsUser(post *model.Post) (*model.Post, *model.AppError)
|
||||
if *a.Config().ServiceSettings.EnableChannelViewedMessages {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, "", "", post.UserId, nil)
|
||||
message.Add("channel_id", post.ChannelId)
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,10 +312,7 @@ func (a *App) SendEphemeralPost(userId string, post *model.Post) *model.Post {
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_EPHEMERAL_MESSAGE, "", post.ChannelId, userId, nil)
|
||||
message.Add("post", a.PostWithProxyAddedToImageURLs(post).ToJson())
|
||||
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
|
||||
return post
|
||||
}
|
||||
@@ -417,10 +412,7 @@ func (a *App) PatchPost(postId string, patch *model.PostPatch) (*model.Post, *mo
|
||||
func (a *App) sendUpdatedPostEvent(post *model.Post) {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_EDITED, "", post.ChannelId, "", nil)
|
||||
message.Add("post", a.PostWithProxyAddedToImageURLs(post).ToJson())
|
||||
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
func (a *App) GetPostsPage(channelId string, page int, perPage int) (*model.PostList, *model.AppError) {
|
||||
@@ -560,10 +552,8 @@ func (a *App) DeletePost(postId string) (*model.Post, *model.AppError) {
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_DELETED, "", post.ChannelId, "", nil)
|
||||
message.Add("post", a.PostWithProxyAddedToImageURLs(post).ToJson())
|
||||
a.Publish(message)
|
||||
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Go(func() {
|
||||
a.DeletePostFiles(post)
|
||||
})
|
||||
|
||||
@@ -55,9 +55,7 @@ func (a *App) UpdatePreferences(userId string, preferences model.Preferences) *m
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PREFERENCES_CHANGED, "", "", userId, nil)
|
||||
message.Add("preferences", preferences.ToJson())
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -80,9 +78,7 @@ func (a *App) DeletePreferences(userId string, preferences model.Preferences) *m
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PREFERENCES_DELETED, "", "", userId, nil)
|
||||
message.Add("preferences", preferences.ToJson())
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -84,28 +84,6 @@ func (cw *CorsWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
const TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN = time.Second
|
||||
|
||||
type VaryBy struct {
|
||||
useIP bool
|
||||
useAuth bool
|
||||
}
|
||||
|
||||
func (m *VaryBy) Key(r *http.Request) string {
|
||||
key := ""
|
||||
|
||||
if m.useAuth {
|
||||
token, tokenLocation := ParseAuthTokenFromRequest(r)
|
||||
if tokenLocation != TokenLocationNotFound {
|
||||
key += token
|
||||
} else if m.useIP { // If we don't find an authentication token and IP based is enabled, fall back to IP
|
||||
key += utils.GetIpAddress(r)
|
||||
}
|
||||
} else if m.useIP { // Only if Auth based is not enabed do we use a plain IP based
|
||||
key = utils.GetIpAddress(r)
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
func redirectHTTPToHTTPS(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Host == "" {
|
||||
http.Error(w, "Not Found", http.StatusNotFound)
|
||||
@@ -223,31 +201,6 @@ func (a *App) StartServer() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type tcpKeepAliveListener struct {
|
||||
*net.TCPListener
|
||||
}
|
||||
|
||||
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
|
||||
tc, err := ln.AcceptTCP()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tc.SetKeepAlive(true)
|
||||
tc.SetKeepAlivePeriod(3 * time.Minute)
|
||||
return tc, nil
|
||||
}
|
||||
|
||||
func (a *App) Listen(addr string) (net.Listener, error) {
|
||||
if addr == "" {
|
||||
addr = ":http"
|
||||
}
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tcpKeepAliveListener{ln.(*net.TCPListener)}, nil
|
||||
}
|
||||
|
||||
func (a *App) StopServer() {
|
||||
if a.Srv.Server != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN)
|
||||
|
||||
@@ -26,7 +26,7 @@ func TestStartServerRateLimiterCriticalError(t *testing.T) {
|
||||
|
||||
// Attempt to use Rate Limiter with an invalid config
|
||||
a.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.RateLimitSettings.Enable = true
|
||||
*cfg.RateLimitSettings.Enable = true
|
||||
*cfg.RateLimitSettings.MaxBurst = -100
|
||||
})
|
||||
|
||||
|
||||
@@ -138,6 +138,9 @@ func (a *App) ClearSessionCacheForUserSkipClusterSend(userId string) {
|
||||
session := ts.(*model.Session)
|
||||
if session.UserId == userId {
|
||||
a.sessionCache.Remove(key)
|
||||
if a.Metrics != nil {
|
||||
a.Metrics.IncrementMemCacheInvalidationCounterSession()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,13 +109,11 @@ func SlackParseUsers(data io.Reader) ([]SlackUser, error) {
|
||||
decoder := json.NewDecoder(data)
|
||||
|
||||
var users []SlackUser
|
||||
if err := decoder.Decode(&users); err != nil {
|
||||
// This actually returns errors that are ignored.
|
||||
// In this case it is erroring because of a null that Slack
|
||||
// introduced. So we just return the users here.
|
||||
return users, err
|
||||
}
|
||||
return users, nil
|
||||
err := decoder.Decode(&users)
|
||||
// This actually returns errors that are ignored.
|
||||
// In this case it is erroring because of a null that Slack
|
||||
// introduced. So we just return the users here.
|
||||
return users, err
|
||||
}
|
||||
|
||||
func SlackParsePosts(data io.Reader) ([]SlackPost, error) {
|
||||
|
||||
@@ -221,9 +221,7 @@ func (a *App) BroadcastStatus(status *model.Status) {
|
||||
event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
|
||||
event.Add("status", status.Status)
|
||||
event.Add("user_id", status.UserId)
|
||||
a.Go(func() {
|
||||
a.Publish(event)
|
||||
})
|
||||
a.Publish(event)
|
||||
}
|
||||
|
||||
func (a *App) SetStatusOffline(userId string, manual bool) {
|
||||
@@ -238,18 +236,7 @@ func (a *App) SetStatusOffline(userId string, manual bool) {
|
||||
|
||||
status = &model.Status{UserId: userId, Status: model.STATUS_OFFLINE, Manual: manual, LastActivityAt: model.GetMillis(), ActiveChannel: ""}
|
||||
|
||||
a.AddStatusCache(status)
|
||||
|
||||
if result := <-a.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
|
||||
l4g.Error(utils.T("api.status.save_status.error"), userId, result.Err)
|
||||
}
|
||||
|
||||
event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
|
||||
event.Add("status", model.STATUS_OFFLINE)
|
||||
event.Add("user_id", status.UserId)
|
||||
a.Go(func() {
|
||||
a.Publish(event)
|
||||
})
|
||||
a.SaveAndBroadcastStatus(status)
|
||||
}
|
||||
|
||||
func (a *App) SetStatusAwayIfNeeded(userId string, manual bool) {
|
||||
@@ -281,18 +268,7 @@ func (a *App) SetStatusAwayIfNeeded(userId string, manual bool) {
|
||||
status.Manual = manual
|
||||
status.ActiveChannel = ""
|
||||
|
||||
a.AddStatusCache(status)
|
||||
|
||||
if result := <-a.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
|
||||
l4g.Error(utils.T("api.status.save_status.error"), userId, result.Err)
|
||||
}
|
||||
|
||||
event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
|
||||
event.Add("status", model.STATUS_AWAY)
|
||||
event.Add("user_id", status.UserId)
|
||||
a.Go(func() {
|
||||
a.Publish(event)
|
||||
})
|
||||
a.SaveAndBroadcastStatus(status)
|
||||
}
|
||||
|
||||
func (a *App) SetStatusDoNotDisturb(userId string) {
|
||||
@@ -309,18 +285,22 @@ func (a *App) SetStatusDoNotDisturb(userId string) {
|
||||
status.Status = model.STATUS_DND
|
||||
status.Manual = true
|
||||
|
||||
a.SaveAndBroadcastStatus(status)
|
||||
}
|
||||
|
||||
func (a *App) SaveAndBroadcastStatus(status *model.Status) *model.AppError {
|
||||
a.AddStatusCache(status)
|
||||
|
||||
if result := <-a.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
|
||||
l4g.Error(utils.T("api.status.save_status.error"), userId, result.Err)
|
||||
l4g.Error(utils.T("api.status.save_status.error"), status.UserId, result.Err)
|
||||
}
|
||||
|
||||
event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
|
||||
event.Add("status", model.STATUS_DND)
|
||||
event.Add("status", status.Status)
|
||||
event.Add("user_id", status.UserId)
|
||||
a.Go(func() {
|
||||
a.Publish(event)
|
||||
})
|
||||
a.Publish(event)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetStatusFromCache(userId string) *model.Status {
|
||||
|
||||
40
app/status_test.go
Обычный файл
40
app/status_test.go
Обычный файл
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestSaveStatus(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
user := th.BasicUser
|
||||
|
||||
for _, statusString := range []string{
|
||||
model.STATUS_ONLINE,
|
||||
model.STATUS_AWAY,
|
||||
model.STATUS_DND,
|
||||
model.STATUS_OFFLINE,
|
||||
} {
|
||||
t.Run(statusString, func(t *testing.T) {
|
||||
status := &model.Status{
|
||||
UserId: user.Id,
|
||||
Status: statusString,
|
||||
}
|
||||
|
||||
th.App.SaveAndBroadcastStatus(status)
|
||||
|
||||
after, err := th.App.GetStatus(user.Id)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get status after save: %v", err)
|
||||
} else if after.Status != statusString {
|
||||
t.Fatalf("failed to save status, got %v, expected %v", after.Status, statusString)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -139,9 +139,7 @@ func (a *App) sendTeamEvent(team *model.Team, event string) {
|
||||
|
||||
message := model.NewWebSocketEvent(event, "", "", "", nil)
|
||||
message.Add("team", sanitizedTeam.ToJson())
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
func (a *App) UpdateTeamMemberRoles(teamId string, userId string, newRoles string) (*model.TeamMember, *model.AppError) {
|
||||
@@ -182,10 +180,7 @@ func (a *App) UpdateTeamMemberRoles(teamId string, userId string, newRoles strin
|
||||
func (a *App) sendUpdatedMemberRoleEvent(userId string, member *model.TeamMember) {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_MEMBERROLE_UPDATED, "", "", userId, nil)
|
||||
message.Add("member", member.ToJson())
|
||||
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
func (a *App) AddUserToTeam(teamId string, userId string, userRequestorId string) (*model.Team, *model.AppError) {
|
||||
|
||||
31
app/user.go
31
app/user.go
@@ -34,7 +34,6 @@ const (
|
||||
TOKEN_TYPE_PASSWORD_RECOVERY = "password_recovery"
|
||||
TOKEN_TYPE_VERIFY_EMAIL = "verify_email"
|
||||
PASSWORD_RECOVER_EXPIRY_TIME = 1000 * 60 * 60 // 1 hour
|
||||
VERIFY_EMAIL_EXPIRY_TIME = 1000 * 60 * 60 // 1 hour
|
||||
IMAGE_PROFILE_PIXEL_DIMENSION = 128
|
||||
)
|
||||
|
||||
@@ -202,9 +201,7 @@ func (a *App) CreateUser(user *model.User) (*model.User, *model.AppError) {
|
||||
// This message goes to everyone, so the teamId, channelId and userId are irrelevant
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_NEW_USER, "", "", "", nil)
|
||||
message.Add("user_id", ruser.Id)
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
|
||||
return ruser, nil
|
||||
}
|
||||
@@ -508,6 +505,14 @@ func (a *App) GetUsersInChannel(channelId string, offset int, limit int) ([]*mod
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) GetUsersInChannelByStatus(channelId string, offset int, limit int) ([]*model.User, *model.AppError) {
|
||||
if result := <-a.Srv.Store.User().GetProfilesInChannelByStatus(channelId, offset, limit); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
return result.Data.([]*model.User), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) GetUsersInChannelMap(channelId string, offset int, limit int, asAdmin bool) (map[string]*model.User, *model.AppError) {
|
||||
users, err := a.GetUsersInChannel(channelId, offset, limit)
|
||||
if err != nil {
|
||||
@@ -533,6 +538,15 @@ func (a *App) GetUsersInChannelPage(channelId string, page int, perPage int, asA
|
||||
return a.sanitizeProfiles(users, asAdmin), nil
|
||||
}
|
||||
|
||||
func (a *App) GetUsersInChannelPageByStatus(channelId string, page int, perPage int, asAdmin bool) ([]*model.User, *model.AppError) {
|
||||
users, err := a.GetUsersInChannelByStatus(channelId, page*perPage, perPage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a.sanitizeProfiles(users, asAdmin), nil
|
||||
}
|
||||
|
||||
func (a *App) GetUsersNotInChannel(teamId string, channelId string, offset int, limit int) ([]*model.User, *model.AppError) {
|
||||
if result := <-a.Srv.Store.User().GetProfilesNotInChannel(teamId, channelId, offset, limit); result.Err != nil {
|
||||
return nil, result.Err
|
||||
@@ -832,7 +846,6 @@ func (a *App) SetProfileImageFromFile(userId string, file multipart.File) *model
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_UPDATED, "", "", "", nil)
|
||||
message.Add("user", user)
|
||||
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
@@ -901,10 +914,6 @@ func (a *App) UpdateActive(user *model.User, active bool) (*model.User, *model.A
|
||||
}
|
||||
}
|
||||
|
||||
if extra := <-a.Srv.Store.Channel().ExtraUpdateByUser(user.Id, model.GetMillis()); extra.Err != nil {
|
||||
return nil, extra.Err
|
||||
}
|
||||
|
||||
ruser := result.Data.([2]*model.User)[0]
|
||||
options := a.Config().GetSanitizeOptions()
|
||||
options["passwordupdate"] = false
|
||||
@@ -1002,9 +1011,7 @@ func (a *App) sendUpdatedUserEvent(user model.User, asAdmin bool) {
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_UPDATED, "", "", "", nil)
|
||||
message.Add("user", user)
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
func (a *App) UpdateUser(user *model.User, sendNotifications bool) (*model.User, *model.AppError) {
|
||||
|
||||
129
app/user_test.go
129
app/user_test.go
@@ -299,3 +299,132 @@ func createGitlabUser(t *testing.T, a *App, email string, username string) (*mod
|
||||
|
||||
return user, gitlabUserObj
|
||||
}
|
||||
|
||||
func TestGetUsersByStatus(t *testing.T) {
|
||||
th := Setup()
|
||||
defer th.TearDown()
|
||||
|
||||
team := th.CreateTeam()
|
||||
channel, err := th.App.CreateChannel(&model.Channel{
|
||||
DisplayName: "dn_" + model.NewId(),
|
||||
Name: "name_" + model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
TeamId: team.Id,
|
||||
CreatorId: model.NewId(),
|
||||
}, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create channel: %v", err)
|
||||
}
|
||||
|
||||
createUserWithStatus := func(username string, status string) *model.User {
|
||||
id := model.NewId()
|
||||
|
||||
user, err := th.App.CreateUser(&model.User{
|
||||
Email: "success+" + id + "@simulator.amazonses.com",
|
||||
Username: "un_" + username + "_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
Password: "Password1",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create user: %v", err)
|
||||
}
|
||||
|
||||
th.LinkUserToTeam(user, team)
|
||||
th.AddUserToChannel(user, channel)
|
||||
|
||||
th.App.SaveAndBroadcastStatus(&model.Status{
|
||||
UserId: user.Id,
|
||||
Status: status,
|
||||
Manual: true,
|
||||
})
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
// Creating these out of order in case that affects results
|
||||
awayUser1 := createUserWithStatus("away1", model.STATUS_AWAY)
|
||||
awayUser2 := createUserWithStatus("away2", model.STATUS_AWAY)
|
||||
dndUser1 := createUserWithStatus("dnd1", model.STATUS_DND)
|
||||
dndUser2 := createUserWithStatus("dnd2", model.STATUS_DND)
|
||||
offlineUser1 := createUserWithStatus("offline1", model.STATUS_OFFLINE)
|
||||
offlineUser2 := createUserWithStatus("offline2", model.STATUS_OFFLINE)
|
||||
onlineUser1 := createUserWithStatus("online1", model.STATUS_ONLINE)
|
||||
onlineUser2 := createUserWithStatus("online2", model.STATUS_ONLINE)
|
||||
|
||||
t.Run("sorting by status then alphabetical", func(t *testing.T) {
|
||||
usersByStatus, err := th.App.GetUsersInChannelPageByStatus(channel.Id, 0, 8, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedUsersByStatus := []*model.User{
|
||||
onlineUser1,
|
||||
onlineUser2,
|
||||
awayUser1,
|
||||
awayUser2,
|
||||
dndUser1,
|
||||
dndUser2,
|
||||
offlineUser1,
|
||||
offlineUser2,
|
||||
}
|
||||
|
||||
if len(usersByStatus) != len(expectedUsersByStatus) {
|
||||
t.Fatalf("received only %v users, expected %v", len(usersByStatus), len(expectedUsersByStatus))
|
||||
}
|
||||
|
||||
for i := range usersByStatus {
|
||||
if usersByStatus[i].Id != expectedUsersByStatus[i].Id {
|
||||
t.Fatalf("received user %v at index %v, expected %v", usersByStatus[i].Username, i, expectedUsersByStatus[i].Username)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("paging", func(t *testing.T) {
|
||||
usersByStatus, err := th.App.GetUsersInChannelPageByStatus(channel.Id, 0, 3, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(usersByStatus) != 3 {
|
||||
t.Fatal("received too many users")
|
||||
}
|
||||
|
||||
if usersByStatus[0].Id != onlineUser1.Id && usersByStatus[1].Id != onlineUser2.Id {
|
||||
t.Fatal("expected to receive online users first")
|
||||
}
|
||||
|
||||
if usersByStatus[2].Id != awayUser1.Id {
|
||||
t.Fatal("expected to receive away users second")
|
||||
}
|
||||
|
||||
usersByStatus, err = th.App.GetUsersInChannelPageByStatus(channel.Id, 1, 3, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if usersByStatus[0].Id != awayUser2.Id {
|
||||
t.Fatal("expected to receive away users second")
|
||||
}
|
||||
|
||||
if usersByStatus[1].Id != dndUser1.Id && usersByStatus[2].Id != dndUser2.Id {
|
||||
t.Fatal("expected to receive dnd users third")
|
||||
}
|
||||
|
||||
usersByStatus, err = th.App.GetUsersInChannelPageByStatus(channel.Id, 1, 4, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(usersByStatus) != 4 {
|
||||
t.Fatal("received too many users")
|
||||
}
|
||||
|
||||
if usersByStatus[0].Id != dndUser1.Id && usersByStatus[1].Id != dndUser2.Id {
|
||||
t.Fatal("expected to receive dnd users third")
|
||||
}
|
||||
|
||||
if usersByStatus[2].Id != offlineUser1.Id && usersByStatus[3].Id != offlineUser2.Id {
|
||||
t.Fatal("expected to receive offline users last")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ func SplitWebhookPost(post *model.Post) ([]*model.Post, *model.AppError) {
|
||||
|
||||
func (a *App) CreateWebhookPost(userId string, channel *model.Channel, text, overrideUsername, overrideIconUrl string, props model.StringInterface, postType string, postRootId string) (*model.Post, *model.AppError) {
|
||||
// parse links into Markdown format
|
||||
linkWithTextRegex := regexp.MustCompile(`<([^<\|]+)\|([^>]+)>`)
|
||||
linkWithTextRegex := regexp.MustCompile(`<([^\n<\|>]+)\|([^\n>]+)>`)
|
||||
text = linkWithTextRegex.ReplaceAllString(text, "[${2}](${1})")
|
||||
|
||||
post := &model.Post{UserId: userId, ChannelId: channel.Id, Message: text, Type: postType, RootId: postRootId}
|
||||
|
||||
@@ -317,6 +317,64 @@ func TestCreateWebhookPost(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatal("should have failed - bad post type")
|
||||
}
|
||||
|
||||
expectedText := "`<>|<>|`"
|
||||
post, err = th.App.CreateWebhookPost(hook.UserId, th.BasicChannel, expectedText, "user", "http://iconurl", model.StringInterface{
|
||||
"attachments": []*model.SlackAttachment{
|
||||
{
|
||||
Text: "text",
|
||||
},
|
||||
},
|
||||
"webhook_display_name": hook.DisplayName,
|
||||
}, model.POST_SLACK_ATTACHMENT, "")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
assert.Equal(t, expectedText, post.Message)
|
||||
|
||||
expectedText = "< | \n|\n>"
|
||||
post, err = th.App.CreateWebhookPost(hook.UserId, th.BasicChannel, expectedText, "user", "http://iconurl", model.StringInterface{
|
||||
"attachments": []*model.SlackAttachment{
|
||||
{
|
||||
Text: "text",
|
||||
},
|
||||
},
|
||||
"webhook_display_name": hook.DisplayName,
|
||||
}, model.POST_SLACK_ATTACHMENT, "")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
assert.Equal(t, expectedText, post.Message)
|
||||
|
||||
expectedText = `commit bc95839e4a430ace453e8b209a3723c000c1729a
|
||||
Author: foo <foo@example.org>
|
||||
Date: Thu Mar 1 19:46:54 2018 +0300
|
||||
|
||||
commit message 2
|
||||
|
||||
test | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
commit 5df78b7139b543997838071cd912e375d8bd69b2
|
||||
Author: foo <foo@example.org>
|
||||
Date: Thu Mar 1 19:46:48 2018 +0300
|
||||
|
||||
commit message 1
|
||||
|
||||
test | 3 +++
|
||||
1 file changed, 3 insertions(+)`
|
||||
post, err = th.App.CreateWebhookPost(hook.UserId, th.BasicChannel, expectedText, "user", "http://iconurl", model.StringInterface{
|
||||
"attachments": []*model.SlackAttachment{
|
||||
{
|
||||
Text: "text",
|
||||
},
|
||||
},
|
||||
"webhook_display_name": hook.DisplayName,
|
||||
}, model.POST_SLACK_ATTACHMENT, "")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
assert.Equal(t, expectedText, post.Message)
|
||||
}
|
||||
|
||||
func TestSplitWebhookPost(t *testing.T) {
|
||||
|
||||
Ссылка в новой задаче
Block a user