diff --git a/bigbluebutton-html5/imports/ui/components/chat/message-form/component.jsx b/bigbluebutton-html5/imports/ui/components/chat/message-form/component.jsx index fdc90d50b2f93d47186026289c934832f51cd8ce..5669e0a37fde67aa17145211fdc03fde27938ccb 100755 --- a/bigbluebutton-html5/imports/ui/components/chat/message-form/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/chat/message-form/component.jsx @@ -1,15 +1,29 @@ import React, { PureComponent } from 'react'; -import { defineMessages, injectIntl } from 'react-intl'; +import { defineMessages, injectIntl, intlShape } from 'react-intl'; import cx from 'classnames'; import TextareaAutosize from 'react-autosize-textarea'; import browser from 'browser-detect'; +import PropTypes from 'prop-types'; import { styles } from './styles.scss'; import Button from '../../button/component'; + const propTypes = { + intl: intlShape.isRequired, + chatId: PropTypes.string.isRequired, + disabled: PropTypes.bool.isRequired, + minMessageLength: PropTypes.number.isRequired, + maxMessageLength: PropTypes.number.isRequired, + chatTitle: PropTypes.string.isRequired, + chatName: PropTypes.string.isRequired, + className: PropTypes.string, + chatAreaId: PropTypes.string.isRequired, + handleSendMessage: PropTypes.func.isRequired, + UnsentMessagesCollection: PropTypes.object.isRequired, }; const defaultProps = { + className: '', }; const messages = defineMessages({ @@ -36,6 +50,8 @@ const messages = defineMessages({ }, }); +const CHAT_ENABLED = Meteor.settings.public.chat.enabled; + class MessageForm extends PureComponent { constructor(props) { super(props); @@ -60,7 +76,7 @@ class MessageForm extends PureComponent { this.setMessageState(); if (!mobile) { - this.textarea.focus(); + if (this.textarea) this.textarea.focus(); } } @@ -70,7 +86,7 @@ class MessageForm extends PureComponent { const { mobile } = this.BROWSER_RESULTS; if (prevProps.chatId !== chatId && !mobile) { - this.textarea.focus(); + if (this.textarea) this.textarea.focus(); } if (prevProps.chatId !== chatId) { @@ -199,7 +215,7 @@ class MessageForm extends PureComponent { const { hasErrors, error, message } = this.state; - return ( + return CHAT_ENABLED ? ( <form ref={(ref) => { this.form = ref; }} className={cx(className, styles.form)} @@ -240,7 +256,7 @@ class MessageForm extends PureComponent { {hasErrors ? <span id="message-input-error">{error}</span> : null} </div> </form> - ); + ) : null; } } diff --git a/bigbluebutton-html5/imports/ui/components/join-handler/component.jsx b/bigbluebutton-html5/imports/ui/components/join-handler/component.jsx index 6e898e881691b2a1290cdf7ca2349a7f21e4e2e6..fcf16895b33ada608121689ecc091c6773ef780e 100644 --- a/bigbluebutton-html5/imports/ui/components/join-handler/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/join-handler/component.jsx @@ -14,6 +14,7 @@ const propTypes = { const APP_CONFIG = Meteor.settings.public.app; const { showParticipantsOnLogin } = APP_CONFIG; +const CHAT_ENABLED = Meteor.settings.public.chat.enabled; class JoinHandler extends Component { static setError(codeError) { @@ -149,8 +150,11 @@ class JoinHandler extends Component { logUserInfo(); if (showParticipantsOnLogin && !deviceInfo.type().isPhone) { - Session.set('openPanel', 'chat'); - Session.set('idChatOpen', ''); + Session.set('openPanel', 'userlist'); + if (CHAT_ENABLED) { + Session.set('openPanel', 'chat'); + Session.set('idChatOpen', ''); + } } else { Session.set('openPanel', ''); } diff --git a/bigbluebutton-html5/imports/ui/components/lock-viewers/component.jsx b/bigbluebutton-html5/imports/ui/components/lock-viewers/component.jsx index c716e5f966ad4ec710d6067ebe3da58c765123c5..617421ee696f1a30b150d494794ac703763fa78d 100755 --- a/bigbluebutton-html5/imports/ui/components/lock-viewers/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/lock-viewers/component.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Fragment } from 'react'; import { defineMessages, injectIntl } from 'react-intl'; import Toggle from '/imports/ui/components/switch/component'; import cx from 'classnames'; @@ -61,6 +61,8 @@ const intlMessages = defineMessages({ }, }); +const CHAT_ENABLED = Meteor.settings.public.chat.enabled; + class LockViewersComponent extends React.PureComponent { render() { const { @@ -159,73 +161,82 @@ class LockViewersComponent extends React.PureComponent { </div> </div> </div> - <div className={styles.row}> - <div className={styles.col} aria-hidden="true"> - <div className={styles.formElement}> - <div className={styles.label}> - {intl.formatMessage(intlMessages.publicChatLabel)} + + {CHAT_ENABLED ? ( + <Fragment> + <div className={styles.row}> + <div className={styles.col} aria-hidden="true"> + <div className={styles.formElement}> + <div className={styles.label}> + {intl.formatMessage(intlMessages.publicChatLabel)} + </div> + </div> </div> - </div> - </div> - <div className={styles.col}> - <div className={cx(styles.formElement, styles.pullContentRight)}> - <Toggle - icons={false} - defaultChecked={meeting.lockSettingsProps.disablePublicChat} - onChange={() => { - meeting.lockSettingsProps.disablePublicChat = !meeting.lockSettingsProps.disablePublicChat; - toggleLockSettings(meeting); - }} - ariaLabel={intl.formatMessage(intlMessages.publicChatLabel)} - /> - </div> - </div> - </div> - <div className={styles.row}> - <div className={styles.col} aria-hidden="true"> - <div className={styles.formElement}> - <div className={styles.label}> - {intl.formatMessage(intlMessages.privateChatLable)} + <div className={styles.col}> + <div className={cx(styles.formElement, styles.pullContentRight)}> + <Toggle + icons={false} + defaultChecked={meeting.lockSettingsProps.disablePublicChat} + onChange={() => { + meeting.lockSettingsProps.disablePublicChat = !meeting.lockSettingsProps.disablePublicChat; + toggleLockSettings(meeting); + }} + ariaLabel={intl.formatMessage(intlMessages.publicChatLabel)} + /> + </div> </div> </div> - </div> - <div className={styles.col}> - <div className={cx(styles.formElement, styles.pullContentRight)}> - <Toggle - icons={false} - defaultChecked={meeting.lockSettingsProps.disablePrivateChat} - onChange={() => { - meeting.lockSettingsProps.disablePrivateChat = !meeting.lockSettingsProps.disablePrivateChat; - toggleLockSettings(meeting); - }} - ariaLabel={intl.formatMessage(intlMessages.privateChatLable)} - /> - </div> - </div> - </div> - { NoteService.isEnabled() ? - (<div className={styles.row}> - <div className={styles.col} aria-hidden="true"> - <div className={styles.formElement}> - <div className={styles.label}> - {intl.formatMessage(intlMessages.notesLabel)} + <div className={styles.row}> + <div className={styles.col} aria-hidden="true"> + <div className={styles.formElement}> + <div className={styles.label}> + {intl.formatMessage(intlMessages.privateChatLable)} + </div> + </div> + </div> + <div className={styles.col}> + <div className={cx(styles.formElement, styles.pullContentRight)}> + <Toggle + icons={false} + defaultChecked={meeting.lockSettingsProps.disablePrivateChat} + onChange={() => { + meeting.lockSettingsProps.disablePrivateChat = !meeting.lockSettingsProps.disablePrivateChat; + toggleLockSettings(meeting); + }} + ariaLabel={intl.formatMessage(intlMessages.privateChatLable)} + /> </div> </div> </div> - <div className={styles.col}> - <div className={cx(styles.formElement, styles.pullContentRight)}> - <Toggle - icons={false} - defaultChecked={meeting.lockSettingsProps.disableNote} - onChange={() => { - meeting.lockSettingsProps.disableNote = !meeting.lockSettingsProps.disableNote; - toggleLockSettings(meeting); - }} - ariaLabel={intl.formatMessage(intlMessages.notesLabel)} - /> + </Fragment> + ) : null + } + + { NoteService.isEnabled() + ? ( + <div className={styles.row}> + <div className={styles.col} aria-hidden="true"> + <div className={styles.formElement}> + <div className={styles.label}> + {intl.formatMessage(intlMessages.notesLabel)} + </div> + </div> + </div> + <div className={styles.col}> + <div className={cx(styles.formElement, styles.pullContentRight)}> + <Toggle + icons={false} + defaultChecked={meeting.lockSettingsProps.disableNote} + onChange={() => { + meeting.lockSettingsProps.disableNote = !meeting.lockSettingsProps.disableNote; + toggleLockSettings(meeting); + }} + ariaLabel={intl.formatMessage(intlMessages.notesLabel)} + /> + </div> </div> </div> - </div>) + ) : null } </div> diff --git a/bigbluebutton-html5/imports/ui/components/settings/submenus/application/component.jsx b/bigbluebutton-html5/imports/ui/components/settings/submenus/application/component.jsx index 4afcbe085567ebb0741eac61e6fcfbb0e966b28d..a92d20bdfcfe92b758b8b26c852e8b2ccb614ca4 100644 --- a/bigbluebutton-html5/imports/ui/components/settings/submenus/application/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/settings/submenus/application/component.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Fragment } from 'react'; import cx from 'classnames'; import Button from '/imports/ui/components/button/component'; import Toggle from '/imports/ui/components/switch/component'; @@ -7,6 +7,7 @@ import BaseMenu from '../base/component'; import { styles } from '../styles'; const MIN_FONTSIZE = 0; +const CHAT_ENABLED = Meteor.settings.public.chat.enabled; const intlMessages = defineMessages({ applicationSectionTitle: { @@ -195,45 +196,50 @@ class ApplicationMenu extends BaseMenu { </div> </div> - <div className={styles.row}> - <div className={styles.col} aria-hidden="true"> - <div className={styles.formElement}> - <label className={styles.label}> - {intl.formatMessage(intlMessages.audioAlertLabel)} - </label> + {CHAT_ENABLED + ? (<Fragment> + <div className={styles.row}> + <div className={styles.col} aria-hidden="true"> + <div className={styles.formElement}> + <label className={styles.label}> + {intl.formatMessage(intlMessages.audioAlertLabel)} + </label> + </div> + </div> + <div className={styles.col}> + <div className={cx(styles.formElement, styles.pullContentRight)}> + <Toggle + icons={false} + defaultChecked={this.state.settings.chatAudioAlerts} + onChange={() => this.handleToggle('chatAudioAlerts')} + ariaLabel={intl.formatMessage(intlMessages.audioAlertLabel)} + /> + </div> + </div> </div> - </div> - <div className={styles.col}> - <div className={cx(styles.formElement, styles.pullContentRight)}> - <Toggle - icons={false} - defaultChecked={this.state.settings.chatAudioAlerts} - onChange={() => this.handleToggle('chatAudioAlerts')} - ariaLabel={intl.formatMessage(intlMessages.audioAlertLabel)} - /> + <div className={styles.row}> + <div className={styles.col} aria-hidden="true"> + <div className={styles.formElement}> + <label className={styles.label}> + {intl.formatMessage(intlMessages.pushAlertLabel)} + </label> + </div> + </div> + <div className={styles.col}> + <div className={cx(styles.formElement, styles.pullContentRight)}> + <Toggle + icons={false} + defaultChecked={this.state.settings.chatPushAlerts} + onChange={() => this.handleToggle('chatPushAlerts')} + ariaLabel={intl.formatMessage(intlMessages.pushAlertLabel)} + /> + </div> + </div> </div> - </div> - </div> + </Fragment> + ) : null + } - <div className={styles.row}> - <div className={styles.col} aria-hidden="true"> - <div className={styles.formElement}> - <label className={styles.label}> - {intl.formatMessage(intlMessages.pushAlertLabel)} - </label> - </div> - </div> - <div className={styles.col}> - <div className={cx(styles.formElement, styles.pullContentRight)}> - <Toggle - icons={false} - defaultChecked={this.state.settings.chatPushAlerts} - onChange={() => this.handleToggle('chatPushAlerts')} - ariaLabel={intl.formatMessage(intlMessages.pushAlertLabel)} - /> - </div> - </div> - </div> <div className={styles.row}> <div className={styles.col} aria-hidden="true"> diff --git a/bigbluebutton-html5/imports/ui/components/shortcut-help/component.jsx b/bigbluebutton-html5/imports/ui/components/shortcut-help/component.jsx index b5624cdc0e54a92774836f99cf5779c4f8a5db65..47adb49a9232a8723784955d8445ea63c78ba4ba 100644 --- a/bigbluebutton-html5/imports/ui/components/shortcut-help/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/shortcut-help/component.jsx @@ -86,6 +86,9 @@ const intlMessages = defineMessages({ }, }); +const CHAT_CONFIG = Meteor.settings.public.chat; +const CHAT_ENABLED = CHAT_CONFIG.enabled; + const ShortcutHelpComponent = (props) => { const { intl, shortcuts } = props; const { name } = browser(); @@ -109,12 +112,16 @@ const ShortcutHelpComponent = (props) => { break; } - const shortcutItems = shortcuts.map(shortcut => ( - <tr key={_.uniqueId('hotkey-item-')}> - <td className={styles.keyCell}>{`${accessMod} + ${shortcut.accesskey}`}</td> - <td className={styles.descCell}>{intl.formatMessage(intlMessages[`${shortcut.descId}`])}</td> - </tr> - )); + const shortcutItems = shortcuts.map((shortcut) => { + if (!CHAT_ENABLED && shortcut.descId.indexOf('Chat') !== -1) return null; + + return ( + <tr key={_.uniqueId('hotkey-item-')}> + <td className={styles.keyCell}>{`${accessMod} + ${shortcut.accesskey}`}</td> + <td className={styles.descCell}>{intl.formatMessage(intlMessages[`${shortcut.descId}`])}</td> + </tr> + ); + }); shortcutItems.push(( <tr key={_.uniqueId('hotkey-item-')}> diff --git a/bigbluebutton-html5/imports/ui/components/subscriptions/component.jsx b/bigbluebutton-html5/imports/ui/components/subscriptions/component.jsx index dc08ed494b970c8718021451e3e9de8ffd075828..3b2206686b2a3960ae12e23150de32f1d33b539f 100644 --- a/bigbluebutton-html5/imports/ui/components/subscriptions/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/subscriptions/component.jsx @@ -10,6 +10,7 @@ import AnnotationsLocal from '/imports/ui/components/whiteboard/service'; import mapUser from '/imports/ui/services/user/mapUser'; const CHAT_CONFIG = Meteor.settings.public.chat; +const CHAT_ENABLED = CHAT_CONFIG.enabled; const PUBLIC_GROUP_CHAT_ID = CHAT_CONFIG.public_group_id; const PUBLIC_CHAT_TYPE = CHAT_CONFIG.type_public; const SUBSCRIPTIONS = [ @@ -52,29 +53,36 @@ export default withTracker(() => { }, }; - const subscriptionsHandlers = SUBSCRIPTIONS.map(name => Meteor.subscribe( - name, - credentials, - subscriptionErrorHandler, - )); + let subscriptionsHandlers = SUBSCRIPTIONS.map((name) => { + if (!CHAT_ENABLED && name.indexOf('chat') !== -1) return; + return Meteor.subscribe( + name, + credentials, + subscriptionErrorHandler, + ); + }); let groupChatMessageHandler = {}; // let annotationsHandler = {}; - const chats = GroupChat.find({ - $or: [ - { - meetingId, - access: PUBLIC_CHAT_TYPE, - chatId: { $ne: PUBLIC_GROUP_CHAT_ID }, - }, - { meetingId, users: { $all: [requesterUserId] } }, - ], - }).fetch(); + if (CHAT_ENABLED) { + const chats = GroupChat.find({ + $or: [ + { + meetingId, + access: PUBLIC_CHAT_TYPE, + chatId: { $ne: PUBLIC_GROUP_CHAT_ID }, + }, + { meetingId, users: { $all: [requesterUserId] } }, + ], + }).fetch(); + + const chatIds = chats.map(chat => chat.chatId); + + groupChatMessageHandler = Meteor.subscribe('group-chat-msg', credentials, chatIds, subscriptionErrorHandler); + subscriptionsHandlers.push(groupChatMessageHandler); + } - const chatIds = chats.map(chat => chat.chatId); - groupChatMessageHandler = Meteor.subscribe('group-chat-msg', credentials, chatIds, subscriptionErrorHandler); - subscriptionsHandlers.push(groupChatMessageHandler); const User = Users.findOne({ intId: requesterUserId }); if (User) { @@ -100,6 +108,7 @@ export default withTracker(() => { ...subscriptionErrorHandler, }); + subscriptionsHandlers = subscriptionsHandlers.filter(obj => obj); const ready = subscriptionsHandlers.every(handler => handler.ready()); return { diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/component.jsx index 91481bef91b024acab6d5b968138d82004e63fbd..62aeae1e77cfc60131b1fbfe39ac110d2e168418 100644 --- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/component.jsx @@ -43,6 +43,8 @@ const defaultProps = { isBreakoutRoom: false, }; +const CHAT_ENABLED = Meteor.settings.public.chat.enabled; + class UserContent extends PureComponent { render() { const { @@ -83,15 +85,18 @@ class UserContent extends PureComponent { className={styles.content} role="complementary" > - <UserMessages - {...{ - isPublicChat, - activeChats, - compact, - intl, - roving, - }} - /> + {CHAT_ENABLED + ? (<UserMessages + {...{ + isPublicChat, + activeChats, + compact, + intl, + roving, + }} + /> + ) : null + } {currentUser.isModerator ? ( <UserCaptionsContainer diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx index c75f72576dd809292f3abf83dd4389b82c1c4b28..4e7bda7c74c7afb5acf7f2c1a42d3e759de47b40 100755 --- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-dropdown/component.jsx @@ -112,6 +112,8 @@ const propTypes = { toggleUserLock: PropTypes.func.isRequired, }; +const CHAT_ENABLED = Meteor.settings.public.chat.enabled; + class UserDropdown extends PureComponent { /** * Return true if the content fit on the screen, false otherwise. @@ -276,7 +278,7 @@ class UserDropdown extends PureComponent { )); } - if (enablePrivateChat && isMeteorConnected) { + if (CHAT_ENABLED && enablePrivateChat && isMeteorConnected) { actions.push(this.makeDropdownItem( 'activeChat', intl.formatMessage(messages.ChatLabel), diff --git a/bigbluebutton-html5/private/config/settings.yml b/bigbluebutton-html5/private/config/settings.yml index bbc56af57cd37eef5775c94a54b0fed913063783..ae65b9064c8debfad0d92d3f7eb9d646a8de40ab 100755 --- a/bigbluebutton-html5/private/config/settings.yml +++ b/bigbluebutton-html5/private/config/settings.yml @@ -156,6 +156,7 @@ public: lines: 2 time: 5000 chat: + enabled: true min_message_length: 1 max_message_length: 5000 grouping_messages_window: 10000