diff --git a/server/channels/app/post_permission_utils.go b/server/channels/app/post_permission_utils.go index d6768e114ee..c059ebc994f 100644 --- a/server/channels/app/post_permission_utils.go +++ b/server/channels/app/post_permission_utils.go @@ -142,6 +142,10 @@ func PostBurnOnReadCheckWithApp(where string, a *App, rctx request.CTX, userId, channel = ch } + if channel.IsShared() { + return model.NewAppError(where, "api.post.fill_in_post_props.burn_on_read.shared_channel.app_error", nil, "", http.StatusBadRequest) + } + // Burn-on-read is not allowed in self-DMs or DMs with bots (including AI agents, plugins) if channel.Type == model.ChannelTypeDirect { // Check if it's a self-DM by comparing the channel name with the expected self-DM name diff --git a/server/channels/app/post_test.go b/server/channels/app/post_test.go index 5dc8a366e57..70d4be3b7dd 100644 --- a/server/channels/app/post_test.go +++ b/server/channels/app/post_test.go @@ -1371,6 +1371,70 @@ func TestCreatePost(t *testing.T) { require.Nil(t, appErr) require.Empty(t, createdPost.FileIds) }) + + t.Run("should reject burn-on-read posts in shared channels", func(t *testing.T) { + os.Setenv("MM_FEATUREFLAGS_BURNONREAD", "true") + t.Cleanup(func() { + os.Unsetenv("MM_FEATUREFLAGS_BURNONREAD") + }) + th := setupSharedChannels(t).InitBasic(t) + enableBoRFeature(th) + + channel := th.CreateChannel(t, th.BasicTeam) + + sc := &model.SharedChannel{ + ChannelId: channel.Id, + TeamId: th.BasicTeam.Id, + Type: channel.Type, + Home: true, + ShareName: "shared-bor-test", + CreatorId: th.BasicUser.Id, + RemoteId: model.NewId(), + } + _, scErr := th.Server.Store().SharedChannel().Save(sc) + require.NoError(t, scErr) + + channel.Shared = model.NewPointer(true) + _, err := th.Server.Store().Channel().Update(th.Context, channel) + require.NoError(t, err) + + post := &model.Post{ + ChannelId: channel.Id, + UserId: th.BasicUser.Id, + Message: "burn-on-read in shared channel", + Type: model.PostTypeBurnOnRead, + } + + createdPost, _, appErr := th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{SetOnline: true}) + require.NotNil(t, appErr) + require.Nil(t, createdPost) + require.Equal(t, "api.post.fill_in_post_props.burn_on_read.shared_channel.app_error", appErr.Id) + require.Equal(t, http.StatusBadRequest, appErr.StatusCode) + }) + + t.Run("should allow burn-on-read posts in non-shared channels", func(t *testing.T) { + os.Setenv("MM_FEATUREFLAGS_BURNONREAD", "true") + t.Cleanup(func() { + os.Unsetenv("MM_FEATUREFLAGS_BURNONREAD") + }) + th := Setup(t).InitBasic(t) + enableBoRFeature(th) + + channel := th.CreateChannel(t, th.BasicTeam) + require.False(t, channel.IsShared()) + + post := &model.Post{ + ChannelId: channel.Id, + UserId: th.BasicUser.Id, + Message: "burn-on-read in non-shared channel", + Type: model.PostTypeBurnOnRead, + } + + createdPost, _, appErr := th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{SetOnline: true}) + require.Nil(t, appErr) + require.NotNil(t, createdPost) + require.Equal(t, model.PostTypeBurnOnRead, createdPost.Type) + }) } func TestPatchPost(t *testing.T) { diff --git a/server/i18n/en.json b/server/i18n/en.json index 352f62da23e..01f61b38690 100644 --- a/server/i18n/en.json +++ b/server/i18n/en.json @@ -2868,6 +2868,10 @@ "id": "api.post.fill_in_post_props.burn_on_read.self_dm.app_error", "translation": "Burn-on-read posts are not allowed when messaging yourself." }, + { + "id": "api.post.fill_in_post_props.burn_on_read.shared_channel.app_error", + "translation": "Burn-on-read posts are not allowed in shared channels." + }, { "id": "api.post.fill_in_post_props.burn_on_read.user.app_error", "translation": "An error occurred while validating the user for burn-on-read post." diff --git a/webapp/channels/src/components/advanced_text_editor/use_burn_on_read.test.tsx b/webapp/channels/src/components/advanced_text_editor/use_burn_on_read.test.tsx index 9ea9f200297..21daef18c87 100644 --- a/webapp/channels/src/components/advanced_text_editor/use_burn_on_read.test.tsx +++ b/webapp/channels/src/components/advanced_text_editor/use_burn_on_read.test.tsx @@ -215,6 +215,42 @@ describe('useBurnOnRead', () => { expect(result.current.additionalControl).toBeDefined(); }); + + it('should hide burn-on-read button in shared channels', () => { + const sharedChannel = {...createMockChannel('O'), shared: true}; + (getChannel as jest.Mock).mockReturnValue(sharedChannel); + + const {result} = renderHook( + () => useBurnOnRead( + createMockDraft(), + mockHandleDraftChange, + mockFocusTextbox, + false, + true, + ), + {wrapper}, + ); + + expect(result.current.additionalControl).toBeUndefined(); + }); + + it('should show burn-on-read button in non-shared channels', () => { + const nonSharedChannel = {...createMockChannel('O'), shared: false}; + (getChannel as jest.Mock).mockReturnValue(nonSharedChannel); + + const {result} = renderHook( + () => useBurnOnRead( + createMockDraft(), + mockHandleDraftChange, + mockFocusTextbox, + false, + true, + ), + {wrapper}, + ); + + expect(result.current.additionalControl).toBeDefined(); + }); }); describe('button visibility with feature flags', () => { diff --git a/webapp/channels/src/components/advanced_text_editor/use_burn_on_read.tsx b/webapp/channels/src/components/advanced_text_editor/use_burn_on_read.tsx index d751c93caf5..9e0aa26b82b 100644 --- a/webapp/channels/src/components/advanced_text_editor/use_burn_on_read.tsx +++ b/webapp/channels/src/components/advanced_text_editor/use_burn_on_read.tsx @@ -79,6 +79,10 @@ const useBurnOnRead = ( } } + if (channel.shared) { + return false; + } + return true; // Allow all other channel types }, [channel, currentUser, otherUser]);