update(chat): add option to disable private chat (#21459)

* update(chat): add option to disable private chat

- Adds `public.chat.private` to settings.yml, defaults to true
- Adds `privateChat` to disabledFeatures

* backend validation for disabledFeatures=privateChat

* refactor(settings): remove chat.private from settings.yml

Will be kept only in disabledFeatures

---------

Co-authored-by: Gustavo Trott <gustavo@trott.com.br>
This commit is contained in:
germanocaumo 2024-10-25 17:40:10 +00:00 committed by GitHub
parent 82774b9a08
commit e992862e40
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 76 additions and 48 deletions

View File

@ -20,8 +20,13 @@ trait CreateGroupChatReqMsgHdlr extends SystemConfiguration {
liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
log.debug("RECEIVED CREATE CHAT REQ MESSAGE")
var privateChatDisabled: Boolean = false
var chatLocked: Boolean = false
if (msg.body.access == GroupChatAccess.PRIVATE) {
privateChatDisabled = liveMeeting.props.meetingProp.disabledFeatures.contains("privateChat")
}
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
} yield {
@ -45,7 +50,12 @@ trait CreateGroupChatReqMsgHdlr extends SystemConfiguration {
// Check if this message was sent while the lock settings was being changed.
val isDelayedMessage = System.currentTimeMillis() - MeetingStatus2x.getPermissionsChangedOn(liveMeeting.status) < 5000
if (applyPermissionCheck && chatLocked && !isDelayedMessage) {
if (privateChatDisabled ||
(
applyPermissionCheck &&
chatLocked &&
!isDelayedMessage
)) {
val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to create a new group chat."
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)

View File

@ -25,6 +25,7 @@ trait SendGroupChatMessageMsgHdlr extends HandlerHelpers {
}
val chatDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("chat")
var privateChatDisabled: Boolean = false
val replyChatMessageDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("replyChatMessage")
var chatLocked: Boolean = false
var chatLockedForUser: Boolean = false
@ -33,6 +34,10 @@ trait SendGroupChatMessageMsgHdlr extends HandlerHelpers {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
groupChat <- state.groupChats.find(msg.body.chatId)
} yield {
if (groupChat.access == GroupChatAccess.PRIVATE) {
privateChatDisabled = liveMeeting.props.meetingProp.disabledFeatures.contains("privateChat")
}
if (groupChat.access == GroupChatAccess.PUBLIC && user.userLockSettings.disablePublicChat && user.role != Roles.MODERATOR_ROLE) {
chatLockedForUser = true
}
@ -54,7 +59,10 @@ trait SendGroupChatMessageMsgHdlr extends HandlerHelpers {
}
}
if (!chatDisabled && !(applyPermissionCheck && chatLocked) && !chatLockedForUser) {
if (!chatDisabled &&
!privateChatDisabled &&
!(applyPermissionCheck && chatLocked) &&
!chatLockedForUser) {
val newState = for {
sender <- GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x)
chat <- state.groupChats.find(msg.body.chatId)

View File

@ -74,13 +74,13 @@ const intlMessages = defineMessages({
description: 'locked element label',
},
hideCursorsLabel: {
id: "app.lock-viewers.hideViewersCursor",
id: 'app.lock-viewers.hideViewersCursor',
description: 'label for other viewers cursor',
},
hideAnnotationsLabel: {
id: "app.lock-viewers.hideAnnotationsLabel",
id: 'app.lock-viewers.hideAnnotationsLabel',
description: 'label for other viewers annotation',
}
},
});
const propTypes = {
@ -88,7 +88,7 @@ const propTypes = {
intl: PropTypes.shape({
formatMessage: PropTypes.func.isRequired,
}).isRequired,
meeting: PropTypes.object.isRequired,
meeting: PropTypes.shape({}).isRequired,
showToggleLabel: PropTypes.bool.isRequired,
updateLockSettings: PropTypes.func.isRequired,
updateWebcamsOnlyForModerator: PropTypes.func.isRequired,
@ -106,6 +106,12 @@ class LockViewersComponent extends Component {
};
}
componentWillUnmount() {
const { closeModal } = this.props;
closeModal();
}
toggleLockSettings(property) {
const { lockSettingsProps } = this.state;
@ -129,18 +135,14 @@ class LockViewersComponent extends Component {
displayLockStatus(status) {
const { intl } = this.props;
return (
status && <Styled.ToggleLabel>
status && (
<Styled.ToggleLabel>
{intl.formatMessage(intlMessages.lockedLabel)}
</Styled.ToggleLabel>
)
);
}
componentWillUnmount() {
const { closeModal } = this.props;
closeModal();
}
render() {
const {
closeModal,
@ -152,6 +154,7 @@ class LockViewersComponent extends Component {
onRequestClose,
priority,
isChatEnabled,
isPrivateChatEnabled,
isSharedNotesEnabled,
} = this.props;
@ -257,7 +260,7 @@ class LockViewersComponent extends Component {
</Styled.Row>
{isChatEnabled ? (
<Fragment>
<>
<Styled.Row data-test="lockPublicChatItem">
<Styled.Col aria-hidden="true">
<Styled.FormElement>
@ -283,34 +286,35 @@ class LockViewersComponent extends Component {
</Styled.FormElementRight>
</Styled.Col>
</Styled.Row>
<Styled.Row data-test="lockPrivateChatItem">
<Styled.Col aria-hidden="true">
<Styled.FormElement>
<Styled.Label>
{intl.formatMessage(intlMessages.privateChatLable)}
</Styled.Label>
</Styled.FormElement>
</Styled.Col>
<Styled.Col>
<Styled.FormElementRight>
{this.displayLockStatus(lockSettingsProps.disablePrivateChat)}
<Toggle
icons={false}
defaultChecked={lockSettingsProps.disablePrivateChat}
onChange={() => {
this.toggleLockSettings('disablePrivateChat');
}}
ariaLabel={intl.formatMessage(intlMessages.privateChatLable)}
showToggleLabel={showToggleLabel}
invertColors={invertColors}
data-test="lockPrivateChat"
/>
</Styled.FormElementRight>
</Styled.Col>
</Styled.Row>
</Fragment>
) : null
}
{isPrivateChatEnabled ? (
<Styled.Row data-test="lockPrivateChatItem">
<Styled.Col aria-hidden="true">
<Styled.FormElement>
<Styled.Label>
{intl.formatMessage(intlMessages.privateChatLable)}
</Styled.Label>
</Styled.FormElement>
</Styled.Col>
<Styled.Col>
<Styled.FormElementRight>
{this.displayLockStatus(lockSettingsProps.disablePrivateChat)}
<Toggle
icons={false}
defaultChecked={lockSettingsProps.disablePrivateChat}
onChange={() => {
this.toggleLockSettings('disablePrivateChat');
}}
ariaLabel={intl.formatMessage(intlMessages.privateChatLable)}
showToggleLabel={showToggleLabel}
invertColors={invertColors}
data-test="lockPrivateChat"
/>
</Styled.FormElementRight>
</Styled.Col>
</Styled.Row>
) : null}
</>
) : null}
{isSharedNotesEnabled
? (
<Styled.Row data-test="lockEditSharedNotesItem">
@ -339,8 +343,7 @@ class LockViewersComponent extends Component {
</Styled.Col>
</Styled.Row>
)
: null
}
: null}
<Styled.Row data-test="lockUserListItem">
<Styled.Col aria-hidden="true">
<Styled.FormElement>

View File

@ -4,7 +4,7 @@ import LockViewersComponent from './component';
import useCurrentUser from '/imports/ui/core/hooks/useCurrentUser';
import { SET_LOCK_SETTINGS_PROPS, SET_WEBCAM_ONLY_FOR_MODERATOR } from './mutations';
import useMeeting from '../../core/hooks/useMeeting';
import { useIsChatEnabled, useIsSharedNotesEnabled } from '../../services/features';
import { useIsChatEnabled, useIsPrivateChatEnabled, useIsSharedNotesEnabled } from '../../services/features';
const LockViewersContainer = (props) => {
const { data: currentUserData } = useCurrentUser((user) => ({
@ -47,6 +47,7 @@ const LockViewersContainer = (props) => {
usersPolicies: m.usersPolicies,
}));
const isChatEnabled = useIsChatEnabled();
const isPrivateChatEnabled = useIsPrivateChatEnabled();
const isSharedNotesEnabled = useIsSharedNotesEnabled();
return amIModerator && meeting && (
@ -57,6 +58,7 @@ const LockViewersContainer = (props) => {
showToggleLabel={false}
meeting={meeting}
isChatEnabled={isChatEnabled}
isPrivateChatEnabled={isPrivateChatEnabled}
isSharedNotesEnabled={isSharedNotesEnabled}
{...props}
/>

View File

@ -6,6 +6,7 @@ import {
import Auth from '/imports/ui/services/auth';
import logger from '/imports/startup/client/logger';
import { toggleMuteMicrophone } from '/imports/ui/components/audio/audio-graphql/audio-controls/input-stream-live-selector/service';
import { useIsPrivateChatEnabled } from '/imports/ui/services/features';
import getFromUserSettings from '/imports/ui/services/users-settings';
export const isVoiceOnlyUser = (userId: string) => userId.toString().startsWith('v_');
@ -31,7 +32,7 @@ export const generateActionsPermissions = (
const parentRoomModerator = getFromUserSettings('bbb_parent_room_moderator', false);
const isSubjectUserGuest = subjectUser.guest;
const hasAuthority = currentUser.isModerator || amISubjectUser;
const allowedToChatPrivately = !amISubjectUser && !isDialInUser;
const allowedToChatPrivately = !amISubjectUser && !isDialInUser && useIsPrivateChatEnabled();
const allowedToMuteAudio = hasAuthority
&& subjectUserVoice?.joined
&& !isMuted

View File

@ -131,3 +131,7 @@ export function useIsChatMessageReactionsEnabled() {
&& window.meetingClientSettings.public.chat.toolbar.includes('reactions')
);
}
export function useIsPrivateChatEnabled() {
return useDisabledFeatures().indexOf('privateChat') === -1;
}

View File

@ -479,7 +479,7 @@ endWhenNoModeratorDelayInMinutes=1
# List of features to disable (comma-separated)
# https://docs.bigbluebutton.org/3.0/development/api/#create
# Available options:
# chat, sharedNotes, polls, screenshare, externalVideos, layouts, captions, liveTranscription,
# chat, privateChat, sharedNotes, polls, screenshare, externalVideos, layouts, captions, liveTranscription,
# breakoutRooms, importSharedNotesFromBreakoutRooms, importPresentationWithAnnotationsFromBreakoutRooms,
# presentation, downloadPresentationWithAnnotations, downloadPresentationOriginalFile, downloadPresentationConvertedToPdf,
# learningDashboard, learningDashboardDownloadSessionData,

View File

@ -350,7 +350,7 @@ const createEndpointTableData = [
"name": "disabledFeatures",
"required": false,
"type": "String",
"description": (<>List (comma-separated) of features to disable in a particular meeting. (added 2.5)<br /><br />Available options to disable:<br /><ul><li><code className="language-plaintext highlighter-rouge">breakoutRooms</code>- <b>Breakout Rooms</b> </li><li><code className="language-plaintext highlighter-rouge">captions</code>- <b>Closed Caption</b> </li><li><code className="language-plaintext highlighter-rouge">chat</code>- <b>Chat</b></li><li><code className="language-plaintext highlighter-rouge">downloadPresentationWithAnnotations</code>- <b>Annotated presentation download</b></li><li><code className="language-plaintext highlighter-rouge">snapshotOfCurrentSlide</code>- <b>Allow snapshot of the current slide</b></li><li><code className="language-plaintext highlighter-rouge">externalVideos</code>- <b>Share an external video</b> </li><li><code className="language-plaintext highlighter-rouge">importPresentationWithAnnotationsFromBreakoutRooms</code>- <b>Capture breakout presentation</b></li><li><code className="language-plaintext highlighter-rouge">importSharedNotesFromBreakoutRooms</code>- <b>Capture breakout shared notes</b></li><li><code className="language-plaintext highlighter-rouge">layouts</code>- <b>Layouts</b> (allow only default layout)</li><li><code className="language-plaintext highlighter-rouge">learningDashboard</code>- <b>Learning Analytics Dashboard</b></li><li><code className="language-plaintext highlighter-rouge">learningDashboardDownloadSessionData</code>- <b>Learning Analytics Dashboard Download Session Data (prevents the option to download)</b></li><li><code className="language-plaintext highlighter-rouge">polls</code>- <b>Polls</b> </li><li><code className="language-plaintext highlighter-rouge">screenshare</code>- <b>Screen Sharing</b></li><li><code className="language-plaintext highlighter-rouge">sharedNotes</code>- <b>Shared Notes</b></li><li><code className="language-plaintext highlighter-rouge">virtualBackgrounds</code>- <b>Virtual Backgrounds</b></li><li><code className="language-plaintext highlighter-rouge">customVirtualBackgrounds</code>- <b>Virtual Backgrounds Upload</b></li><li><code className="language-plaintext highlighter-rouge">liveTranscription</code>- <b>Live Transcription</b></li><li><code className="language-plaintext highlighter-rouge">presentation</code>- <b>Presentation</b></li><li><code className="language-plaintext highlighter-rouge">cameraAsContent</code>-<b>Enables/Disables camera as a content</b></li><li><code className="language-plaintext highlighter-rouge">timer</code>- <b>disables timer</b></li><li><code className="language-plaintext highlighter-rouge">infiniteWhiteboard</code>- <b>Infinite Whiteboard (added in BigBlueButton 3.0)</b></li></ul></>)
"description": (<>List (comma-separated) of features to disable in a particular meeting. (added 2.5)<br /><br />Available options to disable:<br /><ul><li><code className="language-plaintext highlighter-rouge">breakoutRooms</code>- <b>Breakout Rooms</b> </li><li><code className="language-plaintext highlighter-rouge">captions</code>- <b>Closed Caption</b> </li><li><code className="language-plaintext highlighter-rouge">chat</code>- <b>Chat</b></li><li><code className="language-plaintext highlighter-rouge">privateChat</code>- <b>Private Chat</b></li><li><code className="language-plaintext highlighter-rouge">downloadPresentationWithAnnotations</code>- <b>Annotated presentation download</b></li><li><code className="language-plaintext highlighter-rouge">snapshotOfCurrentSlide</code>- <b>Allow snapshot of the current slide</b></li><li><code className="language-plaintext highlighter-rouge">externalVideos</code>- <b>Share an external video</b> </li><li><code className="language-plaintext highlighter-rouge">importPresentationWithAnnotationsFromBreakoutRooms</code>- <b>Capture breakout presentation</b></li><li><code className="language-plaintext highlighter-rouge">importSharedNotesFromBreakoutRooms</code>- <b>Capture breakout shared notes</b></li><li><code className="language-plaintext highlighter-rouge">layouts</code>- <b>Layouts</b> (allow only default layout)</li><li><code className="language-plaintext highlighter-rouge">learningDashboard</code>- <b>Learning Analytics Dashboard</b></li><li><code className="language-plaintext highlighter-rouge">learningDashboardDownloadSessionData</code>- <b>Learning Analytics Dashboard Download Session Data (prevents the option to download)</b></li><li><code className="language-plaintext highlighter-rouge">polls</code>- <b>Polls</b> </li><li><code className="language-plaintext highlighter-rouge">screenshare</code>- <b>Screen Sharing</b></li><li><code className="language-plaintext highlighter-rouge">sharedNotes</code>- <b>Shared Notes</b></li><li><code className="language-plaintext highlighter-rouge">virtualBackgrounds</code>- <b>Virtual Backgrounds</b></li><li><code className="language-plaintext highlighter-rouge">customVirtualBackgrounds</code>- <b>Virtual Backgrounds Upload</b></li><li><code className="language-plaintext highlighter-rouge">liveTranscription</code>- <b>Live Transcription</b></li><li><code className="language-plaintext highlighter-rouge">presentation</code>- <b>Presentation</b></li><li><code className="language-plaintext highlighter-rouge">cameraAsContent</code>-<b>Enables/Disables camera as a content</b></li><li><code className="language-plaintext highlighter-rouge">timer</code>- <b>disables timer</b></li><li><code className="language-plaintext highlighter-rouge">infiniteWhiteboard</code>- <b>Infinite Whiteboard (added in BigBlueButton 3.0)</b></li></ul></>)
},
{
"name": "disabledFeaturesExclude",