Ensure users status is set to offline when deactivated (#30900)

Этот коммит содержится в:
Daniel Espino García
2025-05-29 10:41:14 +02:00
коммит произвёл GitHub
родитель 79bf1c34db
Коммит 293f38ad0b
11 изменённых файлов: 141 добавлений и 16 удалений

Просмотреть файл

@@ -120,7 +120,7 @@ func updateUserStatus(c *Context, w http.ResponseWriter, r *http.Request) {
case "online":
c.App.SetStatusOnline(c.Params.UserId, true)
case "offline":
c.App.SetStatusOffline(c.Params.UserId, true)
c.App.SetStatusOffline(c.Params.UserId, true, false)
case "away":
c.App.SetStatusAwayIfNeeded(c.Params.UserId, true)
case "dnd":

Просмотреть файл

@@ -72,7 +72,7 @@ func TestGetUserStatus(t *testing.T) {
})
t.Run("back to offline status", func(t *testing.T) {
th.App.SetStatusOffline(th.BasicUser.Id, true)
th.App.SetStatusOffline(th.BasicUser.Id, true, false)
userStatus, _, err := client.GetUserStatus(context.Background(), th.BasicUser.Id, "")
require.NoError(t, err)
assert.Equal(t, "offline", userStatus.Status)

Просмотреть файл

@@ -2440,7 +2440,7 @@ func TestUserAllowsEmail(t *testing.T) {
t.Run("should return true", func(t *testing.T) {
user := th.CreateUser()
th.App.SetStatusOffline(user.Id, true)
th.App.SetStatusOffline(user.Id, true, false)
channelMemberNotificationProps := model.StringMap{
model.EmailNotifyProp: model.ChannelNotifyDefault,
@@ -2466,7 +2466,7 @@ func TestUserAllowsEmail(t *testing.T) {
t.Run("should return false in case the EMAIL_NOTIFY_PROP is false", func(t *testing.T) {
user := th.CreateUser()
th.App.SetStatusOffline(user.Id, true)
th.App.SetStatusOffline(user.Id, true, false)
channelMemberNotificationProps := model.StringMap{
model.EmailNotifyProp: "false",
@@ -2479,7 +2479,7 @@ func TestUserAllowsEmail(t *testing.T) {
t.Run("should return false in case the MARK_UNREAD_NOTIFY_PROP is CHANNEL_MARK_UNREAD_MENTION", func(t *testing.T) {
user := th.CreateUser()
th.App.SetStatusOffline(user.Id, true)
th.App.SetStatusOffline(user.Id, true, false)
channelMemberNotificationProps := model.StringMap{
model.EmailNotifyProp: model.ChannelNotifyDefault,
@@ -2492,7 +2492,7 @@ func TestUserAllowsEmail(t *testing.T) {
t.Run("should return false in case the Post type is POST_AUTO_RESPONDER", func(t *testing.T) {
user := th.CreateUser()
th.App.SetStatusOffline(user.Id, true)
th.App.SetStatusOffline(user.Id, true, false)
channelMemberNotificationProps := model.StringMap{
model.EmailNotifyProp: model.ChannelNotifyDefault,

Просмотреть файл

@@ -46,7 +46,7 @@ type mockSuite struct {
}
func (ms *mockSuite) SetStatusLastActivityAt(userID string, activityAt int64) {}
func (ms *mockSuite) SetStatusOffline(userID string, manual bool) {}
func (ms *mockSuite) SetStatusOffline(userID string, manual bool, force bool) {}
func (ms *mockSuite) IsUserAway(lastActivityAt int64) bool { return false }
func (ms *mockSuite) SetStatusOnline(userID string, manual bool) {}
func (ms *mockSuite) UpdateLastActivityAtIfNeeded(session model.Session) {}

Просмотреть файл

@@ -352,13 +352,13 @@ func (ps *PlatformService) SetStatusOnline(userID string, manual bool) {
}
}
func (ps *PlatformService) SetStatusOffline(userID string, manual bool) {
func (ps *PlatformService) SetStatusOffline(userID string, manual bool, force bool) {
if !*ps.Config().ServiceSettings.EnableUserStatuses {
return
}
status, err := ps.GetStatus(userID)
if err == nil && status.Manual && !manual {
if !force && err == nil && status.Manual && !manual {
return // manually set status always overrides non-manual one
}

Просмотреть файл

@@ -52,3 +52,128 @@ func TestTruncateDNDEndTime(t *testing.T) {
// 2025-Jan-20 at 00:00:10 GMT remains unchanged
assert.Equal(t, int64(1737331200), truncateDNDEndTime(1737331200))
}
func TestSetStatusOffline(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
user := th.BasicUser
t.Run("when user statuses are disabled", func(t *testing.T) {
th.Service.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.EnableUserStatuses = false
})
// Set initial status to online
status := &model.Status{
UserId: user.Id,
Status: model.StatusOnline,
}
th.Service.SaveAndBroadcastStatus(status)
// Try to set offline
th.Service.SetStatusOffline(user.Id, false, false)
// Enable user statuses to see what is really in the database
th.Service.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.EnableUserStatuses = true
})
// Status should remain unchanged
after, err := th.Service.GetStatus(user.Id)
require.Nil(t, err)
assert.Equal(t, model.StatusOnline, after.Status)
})
t.Run("when setting status manually over manually set status", func(t *testing.T) {
th.Service.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.EnableUserStatuses = true
})
// Set initial status to online manually
status := &model.Status{
UserId: user.Id,
Status: model.StatusOnline,
Manual: true,
}
th.Service.SaveAndBroadcastStatus(status)
// Try to set offline non-manually
th.Service.SetStatusOffline(user.Id, false, false)
// Status should remain unchanged because manual status takes precedence
after, err := th.Service.GetStatus(user.Id)
require.Nil(t, err)
assert.Equal(t, model.StatusOnline, after.Status)
assert.True(t, after.Manual)
})
t.Run("when force flag is true over manually set status", func(t *testing.T) {
th.Service.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.EnableUserStatuses = true
})
// Set initial status to online manually
status := &model.Status{
UserId: user.Id,
Status: model.StatusOnline,
Manual: true,
}
th.Service.SaveAndBroadcastStatus(status)
// Try to set offline with force flag
th.Service.SetStatusOffline(user.Id, false, true)
// Status should change despite being manual
after, err := th.Service.GetStatus(user.Id)
require.Nil(t, err)
assert.Equal(t, model.StatusOffline, after.Status)
assert.False(t, after.Manual)
})
t.Run("when setting status normally", func(t *testing.T) {
th.Service.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.EnableUserStatuses = true
})
// Set initial status to online
status := &model.Status{
UserId: user.Id,
Status: model.StatusOnline,
Manual: false,
}
th.Service.SaveAndBroadcastStatus(status)
// Set offline
th.Service.SetStatusOffline(user.Id, false, false)
// Status should change
after, err := th.Service.GetStatus(user.Id)
require.Nil(t, err)
assert.Equal(t, model.StatusOffline, after.Status)
assert.False(t, after.Manual)
})
t.Run("when setting status manually over normal status", func(t *testing.T) {
th.Service.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.EnableUserStatuses = true
})
// Set initial status to online
status := &model.Status{
UserId: user.Id,
Status: model.StatusOnline,
Manual: false,
}
th.Service.SaveAndBroadcastStatus(status)
// Set offline manually
th.Service.SetStatusOffline(user.Id, true, false)
// Status should change and be marked as manual
after, err := th.Service.GetStatus(user.Id)
require.Nil(t, err)
assert.Equal(t, model.StatusOffline, after.Status)
assert.True(t, after.Manual)
})
}

Просмотреть файл

@@ -580,7 +580,7 @@ func (h *Hub) Start() {
// Only set to offline if there are no
// active connections in other nodes as well.
if clusterCnt == 0 {
h.platform.SetStatusOffline(userID, false)
h.platform.SetStatusOffline(userID, false, false)
}
})
continue
@@ -704,7 +704,7 @@ func (h *Hub) Start() {
case <-h.stop:
for webConn := range connIndex.All() {
webConn.Close()
h.platform.SetStatusOffline(webConn.UserId, false)
h.platform.SetStatusOffline(webConn.UserId, false, false)
}
h.explicitStop = true

Просмотреть файл

@@ -367,7 +367,7 @@ func (api *PluginAPI) UpdateUserStatus(userID, status string) (*model.Status, *m
case model.StatusOnline:
api.app.SetStatusOnline(userID, true)
case model.StatusOffline:
api.app.SetStatusOffline(userID, true)
api.app.SetStatusOffline(userID, true, false)
case model.StatusAway:
api.app.SetStatusAwayIfNeeded(userID, true)
case model.StatusDnd:

Просмотреть файл

@@ -35,7 +35,7 @@ func (*OfflineProvider) GetCommand(a *app.App, T i18n.TranslateFunc) *model.Comm
}
func (*OfflineProvider) DoCommand(a *app.App, c request.CTX, args *model.CommandArgs, message string) *model.CommandResponse {
a.SetStatusOffline(args.UserId, true)
a.SetStatusOffline(args.UserId, true, false)
return &model.CommandResponse{ResponseType: model.CommandResponseTypeEphemeral, Text: args.T("api.command_offline.success")}
}

Просмотреть файл

@@ -28,8 +28,8 @@ func (a *App) SetStatusOnline(userID string, manual bool) {
a.Srv().Platform().SetStatusOnline(userID, manual)
}
func (a *App) SetStatusOffline(userID string, manual bool) {
a.Srv().Platform().SetStatusOffline(userID, manual)
func (a *App) SetStatusOffline(userID string, manual bool, force bool) {
a.Srv().Platform().SetStatusOffline(userID, manual, force)
}
func (a *App) SetStatusAwayIfNeeded(userID string, manual bool) {

Просмотреть файл

@@ -959,7 +959,7 @@ func (a *App) UpdatePasswordAsUser(c request.CTX, userID, currentPassword, newPa
}
func (a *App) userDeactivated(c request.CTX, userID string) *model.AppError {
a.SetStatusOffline(userID, false)
a.SetStatusOffline(userID, false, true)
user, err := a.GetUser(userID)
if err != nil {