diff --git a/bbb-api-demo/src/main/webapp/demo_iframe.jsp b/bbb-api-demo/src/main/webapp/demo_iframe.jsp
index 3cd02f6fda..e8aafb61d2 100644
--- a/bbb-api-demo/src/main/webapp/demo_iframe.jsp
+++ b/bbb-api-demo/src/main/webapp/demo_iframe.jsp
@@ -133,10 +133,11 @@ muteButton.id = 'muteButton';
function getInitialState() {
document.getElementById('client-content').contentWindow.postMessage('c_recording_status', '*');
- document.getElementById('client-content').contentWindow.postMessage('c_mute_status', '*');
+ document.getElementById('client-content').contentWindow.postMessage('get_audio_joined_status', '*');
}
function handleMessage(e) {
+ let neverJoinedAudio = true;
switch (e) {
case 'readyToConnect': {
// get initial state
@@ -160,11 +161,18 @@ function handleMessage(e) {
case 'notInAudio': {
muteButton.innerHTML = 'Not in audio';
document.getElementById('muteButton').disabled = true;
+ if (neverJoinedAudio) {
+ // poll every 1 sec to check if we joined audio
+ setTimeout(function(){
+ document.getElementById('client-content').contentWindow.postMessage('get_audio_joined_status', '*');
+ }, 1000);}
break;
}
case 'joinedAudio': {
+ neverJoinedAudio = false;
muteButton.innerHTML = '';
- document.getElementById('muteButton').disabled = false; getInitialState();
+ document.getElementById('muteButton').disabled = false;
+ document.getElementById('client-content').contentWindow.postMessage('c_mute_status', '*');
break;
}
default: console.log('neither', { e });
diff --git a/bigbluebutton-html5/imports/api/meetings/server/methods.js b/bigbluebutton-html5/imports/api/meetings/server/methods.js
old mode 100644
new mode 100755
index 74401c1b15..960ef8082e
--- a/bigbluebutton-html5/imports/api/meetings/server/methods.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/methods.js
@@ -2,9 +2,13 @@ import { Meteor } from 'meteor/meteor';
import endMeeting from './methods/endMeeting';
import toggleRecording from './methods/toggleRecording';
import transferUser from './methods/transferUser';
+import toggleLockSettings from './methods/toggleLockSettings';
+import toggleWebcamsOnlyForModerator from './methods/toggleWebcamsOnlyForModerator';
Meteor.methods({
endMeeting,
toggleRecording,
+ toggleLockSettings,
transferUser,
+ toggleWebcamsOnlyForModerator,
});
diff --git a/bigbluebutton-html5/imports/api/meetings/server/methods/toggleLockSettings.js b/bigbluebutton-html5/imports/api/meetings/server/methods/toggleLockSettings.js
new file mode 100755
index 0000000000..ee59cf102e
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/meetings/server/methods/toggleLockSettings.js
@@ -0,0 +1,37 @@
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+import RedisPubSub from '/imports/startup/server/redis';
+
+export default function toggleLockSettings(credentials, meeting) {
+ const REDIS_CONFIG = Meteor.settings.private.redis;
+ const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
+ const EVENT_NAME = 'ChangeLockSettingsInMeetingCmdMsg';
+
+ const { meetingId, requesterUserId } = credentials;
+
+ check(meetingId, String);
+ check(requesterUserId, String);
+ check(meeting.lockSettingsProp, {
+ disableCam: Boolean,
+ disableMic: Boolean,
+ disablePrivChat: Boolean,
+ disablePubChat: Boolean,
+ lockedLayout: Boolean,
+ lockOnJoin: Boolean,
+ lockOnJoinConfigurable: Boolean,
+ setBy: String,
+ });
+
+ const payload = {
+ disableCam: meeting.lockSettingsProp.disableCam,
+ disableMic: meeting.lockSettingsProp.disableMic,
+ disablePrivChat: meeting.lockSettingsProp.disablePrivChat,
+ disablePubChat: meeting.lockSettingsProp.disablePubChat,
+ lockedLayout: meeting.lockSettingsProp.lockedLayout,
+ lockOnJoin: meeting.lockSettingsProp.lockOnJoin,
+ lockOnJoinConfigurable: meeting.lockSettingsProp.lockOnJoinConfigurable,
+ setBy: requesterUserId,
+ };
+
+ RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
+}
diff --git a/bigbluebutton-html5/imports/api/meetings/server/methods/toggleWebcamsOnlyForModerator.js b/bigbluebutton-html5/imports/api/meetings/server/methods/toggleWebcamsOnlyForModerator.js
new file mode 100755
index 0000000000..75f052de76
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/meetings/server/methods/toggleWebcamsOnlyForModerator.js
@@ -0,0 +1,22 @@
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+import RedisPubSub from '/imports/startup/server/redis';
+
+export default function toggleWebcamsOnlyForModerator(credentials, meeting) {
+ const REDIS_CONFIG = Meteor.settings.private.redis;
+ const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
+ const EVENT_NAME = 'UpdateWebcamsOnlyForModeratorCmdMsg';
+
+ const { meetingId, requesterUserId } = credentials;
+
+ check(meetingId, String);
+ check(requesterUserId, String);
+ check(meeting.usersProp.webcamsOnlyForModerator, Boolean);
+
+ const payload = {
+ webcamsOnlyForModerator: meeting.usersProp.webcamsOnlyForModerator,
+ setBy: requesterUserId,
+ };
+
+ RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
+}
diff --git a/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js b/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js
index a8023da362..f8011fd0d3 100644
--- a/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js
@@ -69,10 +69,22 @@ export default function addMeeting(meeting) {
meetingId,
};
+ const lockSettingsProp = {
+ disableCam: false,
+ disableMic: false,
+ disablePrivChat: false,
+ disablePubChat: false,
+ lockOnJoin: true,
+ lockOnJoinConfigurable: false,
+ lockedLayout: false,
+ setBy: 'temp',
+ };
+
const modifier = {
$set: Object.assign(
{ meetingId },
flat(meeting, { safe: true }),
+ { lockSettingsProp },
),
};
diff --git a/bigbluebutton-html5/imports/api/voice-users/server/methods/muteToggle.js b/bigbluebutton-html5/imports/api/voice-users/server/methods/muteToggle.js
index a5377f1773..be28a30199 100644
--- a/bigbluebutton-html5/imports/api/voice-users/server/methods/muteToggle.js
+++ b/bigbluebutton-html5/imports/api/voice-users/server/methods/muteToggle.js
@@ -8,10 +8,6 @@ export default function muteToggle(credentials, userId) {
const REDIS_CONFIG = Meteor.settings.private.redis;
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const EVENT_NAME = 'MuteUserCmdMsg';
- const APP_CONFIG = Meteor.settings.public.app;
- const ALLOW_MODERATOR_TO_UNMUTE_AUDIO = APP_CONFIG.allowModeratorToUnmuteAudio;
- const USER_CONFIG = Meteor.settings.public.user;
- const ROLE_MODERATOR = USER_CONFIG.role_moderator;
const { meetingId, requesterUserId } = credentials;
@@ -32,15 +28,6 @@ export default function muteToggle(credentials, userId) {
const { listenOnly, muted } = voiceUser;
if (listenOnly) return;
- const isModerator = requester.roles.includes(ROLE_MODERATOR.toLowerCase());
- const isNotHimself = requesterUserId !== userId;
-
- // the ability for a moderator to unmute other users is configurable (on/off)
- if (!ALLOW_MODERATOR_TO_UNMUTE_AUDIO &&
- isModerator &&
- muted &&
- isNotHimself) return;
-
const payload = {
userId,
mutedBy: requesterUserId,
diff --git a/bigbluebutton-html5/imports/startup/server/logger.js b/bigbluebutton-html5/imports/startup/server/logger.js
old mode 100755
new mode 100644
diff --git a/bigbluebutton-html5/imports/ui/components/app/component.jsx b/bigbluebutton-html5/imports/ui/components/app/component.jsx
index 81ac621100..526ec18f65 100755
--- a/bigbluebutton-html5/imports/ui/components/app/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/app/component.jsx
@@ -2,8 +2,10 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { throttle } from 'lodash';
import { defineMessages, injectIntl, intlShape } from 'react-intl';
+import { Session } from 'meteor/session';
import Modal from 'react-modal';
import browser from 'browser-detect';
+import PanelManager from '/imports/ui/components/panel-manager/component';
import PollingContainer from '/imports/ui/components/polling/container';
import ToastContainer from '../toast/container';
import ModalContainer from '../modal/container';
@@ -11,8 +13,6 @@ import NotificationsBarContainer from '../notifications-bar/container';
import AudioContainer from '../audio/container';
import ChatAlertContainer from '../chat/alert/container';
import { styles } from './styles';
-import PanelManager from '/imports/ui/components/panel-manager/component';
-import { Session } from 'meteor/session';
const MOBILE_MEDIA = 'only screen and (max-width: 40em)';
@@ -188,7 +188,7 @@ class App extends Component {
render() {
const {
- customStyle, customStyleUrl,
+ customStyle, customStyleUrl, micsLocked,
} = this.props;
return (
@@ -205,11 +205,11 @@ class App extends Component {
-
+ {micsLocked ? null : }
- { customStyleUrl ? : null }
- { customStyle ? : null }
+ {customStyleUrl ? : null}
+ {customStyle ? : null}
);
}
diff --git a/bigbluebutton-html5/imports/ui/components/app/container.jsx b/bigbluebutton-html5/imports/ui/components/app/container.jsx
index 964df9be3d..ef095ec724 100755
--- a/bigbluebutton-html5/imports/ui/components/app/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/app/container.jsx
@@ -69,6 +69,7 @@ const AppContainer = (props) => {
export default injectIntl(withModalMounter(withTracker(({ intl, baseControls }) => {
const currentUser = Users.findOne({ userId: Auth.userID });
+ const meeting = Meetings.findOne({ meetingId: Auth.meetingID });
const isMeetingBreakout = meetingIsBreakout();
if (!currentUser.approved) {
@@ -116,6 +117,7 @@ export default injectIntl(withModalMounter(withTracker(({ intl, baseControls })
chatIsOpen: Session.equals('openPanel', 'chat'),
openPanel: Session.get('openPanel'),
userListIsOpen: !Session.equals('openPanel', ''),
+ micsLocked: (currentUser.locked && meeting.lockSettingsProp.disableMic),
};
})(AppContainer)));
diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx
index b2334ede83..6211484d91 100644
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx
@@ -3,6 +3,9 @@ import { withTracker } from 'meteor/react-meteor-data';
import { withModalMounter } from '/imports/ui/components/modal/service';
import AudioManager from '/imports/ui/services/audio-manager';
import { makeCall } from '/imports/ui/services/api';
+import Users from '/imports/api/users/';
+import Meetings from '/imports/api/meetings';
+import Auth from '/imports/ui/services/auth';
import AudioControls from './component';
import AudioModalContainer from '../audio-modal/container';
import Service from '../service';
@@ -15,11 +18,12 @@ const processToggleMuteFromOutside = (e) => {
makeCall('toggleSelfVoice');
break;
}
+ case 'get_audio_joined_status': {
+ const audioJoinedState = AudioManager.isConnected ? 'joinedAudio' : 'notInAudio';
+ this.window.parent.postMessage({ response: audioJoinedState }, '*');
+ break;
+ }
case 'c_mute_status': {
- if (!AudioManager.isUsingAudio()) {
- this.window.parent.postMessage({ response: 'notInAudio' }, '*');
- return;
- }
const muteState = AudioManager.isMuted ? 'selfMuted' : 'selfUnmuted';
this.window.parent.postMessage({ response: muteState }, '*');
break;
@@ -39,6 +43,12 @@ export default withModalMounter(withTracker(({ mountModal }) =>
disable: Service.isConnecting() || Service.isHangingUp(),
glow: Service.isTalking() && !Service.isMuted(),
handleToggleMuteMicrophone: () => Service.toggleMuteMicrophone(),
- handleJoinAudio: () => mountModal(),
+ handleJoinAudio: () => {
+ const meeting = Meetings.findOne({ meetingId: Auth.meetingID });
+ const currentUser = Users.findOne({ userId: Auth.userID });
+ const micsLocked = (currentUser.locked && meeting.lockSettingsProp.disableMic);
+
+ return micsLocked ? Service.joinListenOnly() : mountModal();
+ },
handleLeaveAudio: () => Service.exitAudio(),
}))(AudioControlsContainer));
diff --git a/bigbluebutton-html5/imports/ui/components/lock-viewers/component.jsx b/bigbluebutton-html5/imports/ui/components/lock-viewers/component.jsx
new file mode 100755
index 0000000000..7c89ec1f1b
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/lock-viewers/component.jsx
@@ -0,0 +1,251 @@
+import React, { Component } from 'react';
+import { defineMessages, injectIntl } from 'react-intl';
+import Button from '/imports/ui/components/button/component';
+import Toggle from '/imports/ui/components/switch/component';
+import cx from 'classnames';
+import ModalBase from '/imports/ui/components/modal/base/component';
+import { styles } from './styles';
+
+const intlMessages = defineMessages({
+ lockViewersTitle: {
+ id: 'app.lock-viewers.title',
+ description: 'lock-viewers title',
+ },
+ closeLabel: {
+ id: 'app.shortcut-help.closeLabel',
+ description: 'label for close button',
+ },
+ closeDesc: {
+ id: 'app.shortcut-help.closeDesc',
+ description: 'description for close button',
+ },
+ lockViewersDescription: {
+ id: 'app.lock-viewers.description',
+ description: 'description for lock viewers feature',
+ },
+ featuresLable: {
+ id: 'app.lock-viewers.featuresLable',
+ description: 'features label',
+ },
+ lockStatusLabel: {
+ id: 'app.lock-viewers.lockStatusLabel',
+ description: 'description for close button',
+ },
+ webcamLabel: {
+ id: 'app.lock-viewers.webcamLabel',
+ description: 'description for close button',
+ },
+ otherViewersWebcamLabel: {
+ id: 'app.lock-viewers.otherViewersWebcamLabel',
+ description: 'description for close button',
+ },
+ microphoneLable: {
+ id: 'app.lock-viewers.microphoneLable',
+ description: 'description for close button',
+ },
+ publicChatLabel: {
+ id: 'app.lock-viewers.PublicChatLabel',
+ description: 'description for close button',
+ },
+ privateChatLable: {
+ id: 'app.lock-viewers.PrivateChatLable',
+ description: 'description for close button',
+ },
+ layoutLable: {
+ id: 'app.lock-viewers.Layout',
+ description: 'description for close button',
+ },
+});
+
+class LockViewersComponent extends Component {
+ constructor(props) {
+ super(props);
+ const {
+ closeModal,
+ toggleLockSettings,
+ toggleWebcamsOnlyForModerator,
+ } = props;
+
+ this.closeModal = closeModal;
+ this.toggleLockSettings = toggleLockSettings;
+ this.toggleWebcamsOnlyForModerator = toggleWebcamsOnlyForModerator;
+ }
+
+ render() {
+ const { intl, meeting } = this.props;
+
+ return (
+
+
+
+
+
{intl.formatMessage(intlMessages.lockViewersTitle)}
+
+
+
+ {`${intl.formatMessage(intlMessages.lockViewersDescription)}`}
+
+
+
+
+
+
+
+
+ {intl.formatMessage(intlMessages.webcamLabel)}
+
+
+
+
+
+ {
+ meeting.lockSettingsProp.disableCam =
+ !meeting.lockSettingsProp.disableCam;
+ this.toggleLockSettings(meeting);
+ }}
+ ariaLabel={intl.formatMessage(intlMessages.webcamLabel)}
+ />
+
+
+
+
+
+
+
+ {intl.formatMessage(intlMessages.otherViewersWebcamLabel)}
+
+
+
+
+
+ {
+ meeting.usersProp.webcamsOnlyForModerator =
+ !meeting.usersProp.webcamsOnlyForModerator;
+ this.toggleWebcamsOnlyForModerator(meeting);
+ }}
+ ariaLabel={intl.formatMessage(intlMessages.otherViewersWebcamLabel)}
+ />
+
+
+
+
+
+
+
+ {intl.formatMessage(intlMessages.microphoneLable)}
+
+
+
+
+
+ {
+ meeting.lockSettingsProp.disableMic =
+ !meeting.lockSettingsProp.disableMic;
+ this.toggleLockSettings(meeting);
+ }}
+ ariaLabel={intl.formatMessage(intlMessages.microphoneLable)}
+ />
+
+
+
+
+
+
+
+ {intl.formatMessage(intlMessages.publicChatLabel)}
+
+
+
+
+
+ {
+ meeting.lockSettingsProp.disablePubChat =
+ !meeting.lockSettingsProp.disablePubChat;
+ this.toggleLockSettings(meeting);
+ }}
+ ariaLabel={intl.formatMessage(intlMessages.publicChatLabel)}
+ />
+
+
+
+
+
+
+
+ {intl.formatMessage(intlMessages.privateChatLable)}
+
+
+
+
+
+ {
+ meeting.lockSettingsProp.disablePrivChat =
+ !meeting.lockSettingsProp.disablePrivChat;
+ this.toggleLockSettings(meeting);
+ }}
+ ariaLabel={intl.formatMessage(intlMessages.privateChatLable)}
+ />
+
+
+
+
+
+
+
+ {intl.formatMessage(intlMessages.layoutLable)}
+
+
+
+
+
+ {
+ meeting.lockSettingsProp.lockedLayout =
+ !meeting.lockSettingsProp.lockedLayout;
+ this.toggleLockSettings(meeting);
+ }}
+ ariaLabel={intl.formatMessage(intlMessages.layoutLable)}
+ />
+
+
+
+
+
+
+ );
+ }
+}
+
+export default injectIntl(LockViewersComponent);
diff --git a/bigbluebutton-html5/imports/ui/components/lock-viewers/container.jsx b/bigbluebutton-html5/imports/ui/components/lock-viewers/container.jsx
new file mode 100755
index 0000000000..2ce4edcc89
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/lock-viewers/container.jsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import { withTracker } from 'meteor/react-meteor-data';
+import { withModalMounter } from '/imports/ui/components/modal/service';
+import { makeCall } from '/imports/ui/services/api';
+import Meetings from '/imports/api/meetings';
+import Auth from '/imports/ui/services/auth';
+import LockViewersComponent from './component';
+
+const LockViewersContainer = props => ;
+
+export default withModalMounter(withTracker(({ mountModal }) => ({
+ closeModal() {
+ mountModal(null);
+ },
+
+ toggleLockSettings(meeting) {
+ makeCall('toggleLockSettings', meeting);
+ },
+
+ toggleWebcamsOnlyForModerator(meeting) {
+ makeCall('toggleWebcamsOnlyForModerator', meeting);
+ },
+ meeting: (Meetings.findOne({ meetingId: Auth.meetingID })),
+}))(LockViewersContainer));
diff --git a/bigbluebutton-html5/imports/ui/components/lock-viewers/styles.scss b/bigbluebutton-html5/imports/ui/components/lock-viewers/styles.scss
new file mode 100755
index 0000000000..078b36e6da
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/lock-viewers/styles.scss
@@ -0,0 +1,118 @@
+@import '/imports/ui/stylesheets/mixins/focus';
+@import '/imports/ui/stylesheets/variables/_all';
+@import "/imports/ui/components/modal/simple/styles";
+
+:root {
+ --modal-margin: 3rem;
+ --title-position-left: 2.2rem;
+ --closeBtn-position-left: 2.5rem;
+}
+
+.title {
+ position: relative;
+ left: var(--title-position-left);
+ color: var(--color-gray-dark);
+ font-weight: bold;
+ font-size: var(--font-size-large);
+ text-align: center;
+}
+
+.form {
+ display: flex;
+ flex-flow: column;
+}
+
+.container {
+ margin: var(--modal-margin);
+ margin-bottom: var(--lg-padding-x);
+}
+
+.subHeader {
+ display: flex;
+ flex-flow: row;
+ flex-grow: 1;
+ justify-content: space-between;
+ color: var(--color-gray-label);
+ font-size: var(--font-size-base);
+ margin-bottom: var(--title-position-left);
+}
+
+.modal {
+ @extend .modal;
+ padding: var(--jumbo-padding-y);
+}
+
+.overlay {
+ @extend .overlay;
+}
+
+.description {
+ text-align: center;
+ color: var(--color-gray);
+ margin-bottom: var(--jumbo-padding-y)
+}
+
+.row {
+ display: flex;
+ flex-flow: row;
+ flex-grow: 1;
+ justify-content: space-between;
+ margin-bottom: var(--md-padding-x);
+}
+
+.col {
+ display: flex;
+ flex-grow: 1;
+ flex-basis: 0;
+ margin-right: var(--md-padding-x);
+}
+
+.label {
+ color: var(--color-gray-label);
+ font-size: var(--font-size-small);
+ margin-bottom: var(--lg-padding-y);
+}
+
+.formElement {
+ position: relative;
+ display: flex;
+ flex-flow: column;
+ flex-grow: 1;
+}
+
+.pullContentRight {
+ display: flex;
+ justify-content: flex-end;
+ flex-flow: row;
+}
+
+.bold {
+ font-weight: bold;
+}
+
+.closeBtn {
+ position: relative;
+ background-color: var(--color-white);
+ left: var(--closeBtn-position-left);
+ bottom: var(--closeBtn-position-left);
+
+ i {
+ color: var(--color-gray-light);
+ }
+
+ &:focus,
+ &:hover {
+ background-color: var(--color-gray-lighter);
+ i {
+ color: var(--color-gray);
+ }
+ }
+}
+
+.header {
+ margin: 0;
+ padding: 0;
+ border: none;
+ line-height: var(--title-position-left);
+ margin-bottom: var(--lg-padding-y);
+}
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/service.js b/bigbluebutton-html5/imports/ui/components/user-list/service.js
index 5ee0883dcf..3678f7d61d 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/service.js
+++ b/bigbluebutton-html5/imports/ui/components/user-list/service.js
@@ -12,9 +12,6 @@ import { makeCall } from '/imports/ui/services/api';
import _ from 'lodash';
import KEY_CODES from '/imports/utils/keyCodes';
-const APP_CONFIG = Meteor.settings.public.app;
-const ALLOW_MODERATOR_TO_UNMUTE_AUDIO = APP_CONFIG.allowModeratorToUnmuteAudio;
-
const CHAT_CONFIG = Meteor.settings.public.chat;
const PUBLIC_GROUP_CHAT_ID = CHAT_CONFIG.public_group_id;
@@ -277,7 +274,7 @@ const getAvailableActions = (currentUser, user, isBreakoutRoom) => {
&& user.isVoiceUser
&& !user.isListenOnly
&& user.isMuted
- && (ALLOW_MODERATOR_TO_UNMUTE_AUDIO || user.isCurrent);
+ && user.isCurrent;
const allowedToResetStatus = hasAuthority
&& user.emoji.status !== EMOJI_STATUSES.none
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/styles.scss b/bigbluebutton-html5/imports/ui/components/user-list/styles.scss
index d8de801266..50e54ffaf1 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/user-list/styles.scss
@@ -121,6 +121,11 @@
color: var(--color-gray-light);
}
+.userListColumn {
+ @extend %flex-column;
+ min-height: 0;
+}
+
.enter,
.appear {
opacity: 0.01;
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/styles.scss b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/styles.scss
index 6ab7b7e20f..6c671c9f3a 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/styles.scss
@@ -12,7 +12,8 @@
.container{
display: flex;
align-items: center;
- margin-bottom: .3rem;
+ margin-bottom: var(--lg-padding-y);
+ margin-top: var(--sm-padding-x);
}
.scrollableList {
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx
index b3907e9329..2009826c84 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx
@@ -175,7 +175,7 @@ class UserParticipants extends Component {
} = this.props;
return (
-
+
{
!compact ?
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx
index 3e689940ef..69c2551259 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx
@@ -9,6 +9,7 @@ import DropdownTrigger from '/imports/ui/components/dropdown/trigger/component';
import DropdownContent from '/imports/ui/components/dropdown/content/component';
import DropdownList from '/imports/ui/components/dropdown/list/component';
import DropdownListItem from '/imports/ui/components/dropdown/list/item/component';
+import LockViewersContainer from '/imports/ui/components/lock-viewers/container';
import { styles } from './styles';
const propTypes = {
@@ -19,7 +20,6 @@ const propTypes = {
toggleMuteAllUsers: PropTypes.func.isRequired,
toggleMuteAllUsersExceptPresenter: PropTypes.func.isRequired,
toggleStatus: PropTypes.func.isRequired,
- toggleLockView: PropTypes.func.isRequired,
};
const intlMessages = defineMessages({
@@ -83,7 +83,8 @@ class UserOptions extends Component {
}
componentWillMount() {
- const { intl, isMeetingMuted } = this.props;
+ const { intl, isMeetingMuted, mountModal } = this.props;
+
this.menuItems = _.compact([
( mountModal()}
/>),
]);
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx
index 59561dab0d..cf5af07ef7 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx
@@ -1,6 +1,5 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
-import logger from '/imports/startup/client/logger';
import Auth from '/imports/ui/services/auth';
import mapUser from '/imports/ui/services/user/mapUser';
import Users from '/imports/api/users/';
@@ -26,7 +25,6 @@ export default class UserOptionsContainer extends Component {
this.muteMeeting = this.muteMeeting.bind(this);
this.muteAllUsersExceptPresenter = this.muteAllUsersExceptPresenter.bind(this);
- this.handleLockView = this.handleLockView.bind(this);
this.handleClearStatus = this.handleClearStatus.bind(this);
}
@@ -44,11 +42,6 @@ export default class UserOptionsContainer extends Component {
muteAllExceptPresenter(currentUser.userId);
}
- handleLockView() {
- // Temporary lock method, will be changed in future PR
- logger.info('handleLockView function');
- }
-
handleClearStatus() {
const { users, setEmojiStatus } = this.props;
@@ -71,7 +64,6 @@ export default class UserOptionsContainer extends Component {
: null
diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx
index 1c7fe511e3..24095302e5 100755
--- a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx
@@ -388,7 +388,7 @@ class VideoProvider extends Component {
}
async createWebRTCPeer(id, shareWebcam) {
- const { meetingId, sessionToken } = this.props;
+ const { meetingId, sessionToken, voiceBridge } = this.props;
let iceServers = [];
try {
@@ -448,6 +448,7 @@ class VideoProvider extends Component {
sdpOffer: offerSdp,
cameraId: id,
meetingId,
+ voiceBridge,
};
this.sendMessage(message);
diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/container.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/container.jsx
index 12bab47d13..eb6bbd51d1 100755
--- a/bigbluebutton-html5/imports/ui/components/video-provider/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/video-provider/container.jsx
@@ -14,4 +14,5 @@ export default withTracker(() => ({
sessionToken: VideoService.sessionToken(),
userName: VideoService.userName(),
enableVideoStats: getFromUserSettings('enableVideoStats', Meteor.settings.public.kurento.enableVideoStats),
+ voiceBridge: VideoService.voiceBridge(),
}))(VideoProviderContainer);
diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/service.js b/bigbluebutton-html5/imports/ui/components/video-provider/service.js
index 5c672116d7..0637da6234 100755
--- a/bigbluebutton-html5/imports/ui/components/video-provider/service.js
+++ b/bigbluebutton-html5/imports/ui/components/video-provider/service.js
@@ -126,6 +126,11 @@ class VideoService {
return Auth.sessionToken;
}
+ voiceBridge() {
+ const voiceBridge = Meetings.findOne({ meetingId: Auth.meetingID }).voiceProp.voiceConf;
+ return voiceBridge;
+ }
+
isConnected() {
return this.isConnected;
}
@@ -157,4 +162,5 @@ export default {
meetingId: () => videoService.meetingId(),
getAllUsersVideo: () => videoService.getAllUsersVideo(),
sessionToken: () => videoService.sessionToken(),
+ voiceBridge: () => videoService.voiceBridge(),
};
diff --git a/bigbluebutton-html5/private/config/settings.yml b/bigbluebutton-html5/private/config/settings.yml
index 195bbb60d4..e5d6ce1ded 100755
--- a/bigbluebutton-html5/private/config/settings.yml
+++ b/bigbluebutton-html5/private/config/settings.yml
@@ -75,7 +75,6 @@ public:
branding:
displayBrandingArea: false
allowHTML5Moderator: true
- allowModeratorToUnmuteAudio: true
httpsConnection: false
connectionTimeout: 60000
showHelpButton: true
diff --git a/bigbluebutton-html5/private/locales/en.json b/bigbluebutton-html5/private/locales/en.json
index 7e39055685..abc96fb5fd 100755
--- a/bigbluebutton-html5/private/locales/en.json
+++ b/bigbluebutton-html5/private/locales/en.json
@@ -359,6 +359,16 @@
"app.shortcut-help.closePrivateChat": "Close Private Chat",
"app.shortcut-help.openActions": "Open Actions Menu",
"app.shortcut-help.openStatus": "Open Status Menu",
+ "app.lock-viewers.title": "Lock Viewers",
+ "app.lock-viewers.description": "These options enable you to restrict certain features available to viewers, such as locking out their ability to use private chat. (These restrictions do no apply to moderators)",
+ "app.lock-viewers.featuresLable": "Feature",
+ "app.lock-viewers.lockStatusLabel": "Locked Status",
+ "app.lock-viewers.webcamLabel": "Webcam",
+ "app.lock-viewers.otherViewersWebcamLabel": "See other viewsers webcams",
+ "app.lock-viewers.microphoneLable": "Microphone",
+ "app.lock-viewers.PublicChatLabel": "Public Chat",
+ "app.lock-viewers.PrivateChatLable": "Private Chat",
+ "app.lock-viewers.Layout": "Layout",
"app.videoPreview.cameraLabel": "Camera",
"app.videoPreview.cancelLabel": "Cancel",
"app.videoPreview.closeLabel": "Close",
diff --git a/build_script.sh b/build_script.sh
index 42265a3dba..a83ccde4ea 100755
--- a/build_script.sh
+++ b/build_script.sh
@@ -8,8 +8,17 @@ if [[ $files = *"bigbluebutton-html5"* ]]; then
meteor npm install
if [ $1 = linter ]
then
+ html5_files=""
+ list=$(echo $files | tr " " "\n")
+ for file in $list
+ do
+ if [[ $file = bigbluebutton-html5* ]] ; then
+ html5_files+=" $file"
+ fi
+ done
+
cd ..
- bigbluebutton-html5/node_modules/.bin/eslint --ext .jsx,.js $files
+ bigbluebutton-html5/node_modules/.bin/eslint --ext .jsx,.js $html5_files
elif [ $1 = acceptance_tests ]
then
{