Merge remote-tracking branch 'upstream/master' into nav-bar-performance-issue

This commit is contained in:
Tainan Felipe 2019-08-20 15:24:48 -03:00
commit cffe9e34be
63 changed files with 365 additions and 174 deletions

View File

@ -25,6 +25,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
disablePrivChat = msg.body.disablePrivChat,
disablePubChat = msg.body.disablePubChat,
disableNote = msg.body.disableNote,
hideUserList = msg.body.hideUserList,
lockedLayout = msg.body.lockedLayout,
lockOnJoin = msg.body.lockOnJoin,
lockOnJoinConfigurable = msg.body.lockOnJoinConfigurable
@ -50,6 +51,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
disablePrivChat = settings.disablePrivChat,
disablePubChat = settings.disablePubChat,
disableNote = settings.disableNote,
hideUserList = settings.hideUserList,
lockedLayout = settings.lockedLayout,
lockOnJoin = settings.lockOnJoin,
lockOnJoinConfigurable = settings.lockOnJoinConfigurable,

View File

@ -17,9 +17,14 @@ trait GetLockSettingsReqMsgHdlr {
val envelope = BbbCoreEnvelope(GetLockSettingsRespMsg.NAME, routing)
val body = GetLockSettingsRespMsgBody(
disableCam = settings.disableCam,
disableMic = settings.disableMic, disablePrivChat = settings.disablePrivChat,
disablePubChat = settings.disablePubChat, disableNote = settings.disableNote, lockedLayout = settings.lockedLayout,
lockOnJoin = settings.lockOnJoin, lockOnJoinConfigurable = settings.lockOnJoinConfigurable
disableMic = settings.disableMic,
disablePrivChat = settings.disablePrivChat,
disablePubChat = settings.disablePubChat,
disableNote = settings.disableNote,
hideUserList = settings.hideUserList,
lockedLayout = settings.lockedLayout,
lockOnJoin = settings.lockOnJoin,
lockOnJoinConfigurable = settings.lockOnJoinConfigurable
)
val header = BbbClientMsgHeader(GetLockSettingsRespMsg.NAME, meetingId, requestedBy)
val event = GetLockSettingsRespMsg(header, body)

View File

@ -234,6 +234,7 @@ class MeetingActor(
disablePrivChat = lockSettingsProp.disablePrivateChat,
disablePubChat = lockSettingsProp.disablePublicChat,
disableNote = lockSettingsProp.disableNote,
hideUserList = lockSettingsProp.hideUserList,
lockedLayout = lockSettingsProp.lockedLayout,
lockOnJoin = lockSettingsProp.lockOnJoin,
lockOnJoinConfigurable = lockSettingsProp.lockOnJoinConfigurable

View File

@ -10,6 +10,7 @@ case class Permissions(
disablePrivChat: Boolean = false,
disablePubChat: Boolean = false,
disableNote: Boolean = false,
hideUserList: Boolean = false,
lockedLayout: Boolean = false,
lockOnJoin: Boolean = true,
lockOnJoinConfigurable: Boolean = false

View File

@ -39,6 +39,7 @@ case class LockSettingsProps(
disablePrivateChat: Boolean,
disablePublicChat: Boolean,
disableNote: Boolean,
hideUserList: Boolean,
lockedLayout: Boolean,
lockOnJoin: Boolean,
lockOnJoinConfigurable: Boolean

View File

@ -253,7 +253,7 @@ case class ChangeLockSettingsInMeetingCmdMsg(
body: ChangeLockSettingsInMeetingCmdMsgBody
) extends StandardMsg
case class ChangeLockSettingsInMeetingCmdMsgBody(disableCam: Boolean, disableMic: Boolean, disablePrivChat: Boolean,
disablePubChat: Boolean, disableNote: Boolean, lockedLayout: Boolean, lockOnJoin: Boolean,
disablePubChat: Boolean, disableNote: Boolean, hideUserList: Boolean, lockedLayout: Boolean, lockOnJoin: Boolean,
lockOnJoinConfigurable: Boolean, setBy: String)
object LockSettingsInMeetingChangedEvtMsg { val NAME = "LockSettingsInMeetingChangedEvtMsg" }
@ -262,7 +262,7 @@ case class LockSettingsInMeetingChangedEvtMsg(
body: LockSettingsInMeetingChangedEvtMsgBody
) extends BbbCoreMsg
case class LockSettingsInMeetingChangedEvtMsgBody(disableCam: Boolean, disableMic: Boolean, disablePrivChat: Boolean,
disablePubChat: Boolean, disableNote: Boolean, lockedLayout: Boolean, lockOnJoin: Boolean,
disablePubChat: Boolean, disableNote: Boolean, hideUserList: Boolean, lockedLayout: Boolean, lockOnJoin: Boolean,
lockOnJoinConfigurable: Boolean, setBy: String)
/**
@ -278,7 +278,7 @@ case class GetLockSettingsReqMsgBody(requesterId: String)
object GetLockSettingsRespMsg { val NAME = "GetLockSettingsRespMsg" }
case class GetLockSettingsRespMsg(header: BbbClientMsgHeader, body: GetLockSettingsRespMsgBody) extends BbbCoreMsg
case class GetLockSettingsRespMsgBody(disableCam: Boolean, disableMic: Boolean, disablePrivChat: Boolean,
disablePubChat: Boolean, disableNote: Boolean, lockedLayout: Boolean, lockOnJoin: Boolean,
disablePubChat: Boolean, disableNote: Boolean, hideUserList: Boolean, lockedLayout: Boolean, lockOnJoin: Boolean,
lockOnJoinConfigurable: Boolean)
object LockSettingsNotInitializedRespMsg { val NAME = "LockSettingsNotInitializedRespMsg" }

View File

@ -65,6 +65,7 @@ public class ApiParams {
public static final String LOCK_SETTINGS_DISABLE_PRIVATE_CHAT = "lockSettingsDisablePrivateChat";
public static final String LOCK_SETTINGS_DISABLE_PUBLIC_CHAT = "lockSettingsDisablePublicChat";
public static final String LOCK_SETTINGS_DISABLE_NOTE = "lockSettingsDisableNote";
public static final String LOCK_SETTINGS_HIDE_USER_LIST = "lockSettingsHideUserList";
public static final String LOCK_SETTINGS_LOCKED_LAYOUT = "lockSettingsLockedLayout";
public static final String LOCK_SETTINGS_LOCK_ON_JOIN = "lockSettingsLockOnJoin";
public static final String LOCK_SETTINGS_LOCK_ON_JOIN_CONFIGURABLE = "lockSettingsLockOnJoinConfigurable";

View File

@ -103,6 +103,7 @@ public class ParamsProcessorUtil {
private boolean defaultLockSettingsDisablePrivateChat;
private boolean defaultLockSettingsDisablePublicChat;
private boolean defaultLockSettingsDisableNote;
private boolean defaultLockSettingsHideUserList;
private boolean defaultLockSettingsLockedLayout;
private boolean defaultLockSettingsLockOnJoin;
private boolean defaultLockSettingsLockOnJoinConfigurable;
@ -279,6 +280,12 @@ public class ParamsProcessorUtil {
lockSettingsDisableNote = Boolean.parseBoolean(lockSettingsDisableNoteParam);
}
Boolean lockSettingsHideUserList = defaultLockSettingsHideUserList;
String lockSettingsHideUserListParam = params.get(ApiParams.LOCK_SETTINGS_HIDE_USER_LIST);
if (!StringUtils.isEmpty(lockSettingsHideUserListParam)) {
lockSettingsHideUserList = Boolean.parseBoolean(lockSettingsHideUserListParam);
}
Boolean lockSettingsLockedLayout = defaultLockSettingsLockedLayout;
String lockSettingsLockedLayoutParam = params.get(ApiParams.LOCK_SETTINGS_LOCKED_LAYOUT);
if (!StringUtils.isEmpty(lockSettingsLockedLayoutParam)) {
@ -302,6 +309,7 @@ public class ParamsProcessorUtil {
lockSettingsDisablePrivateChat,
lockSettingsDisablePublicChat,
lockSettingsDisableNote,
lockSettingsHideUserList,
lockSettingsLockedLayout,
lockSettingsLockOnJoin,
lockSettingsLockOnJoinConfigurable);
@ -1097,6 +1105,10 @@ public class ParamsProcessorUtil {
this.defaultLockSettingsDisableNote = lockSettingsDisableNote;
}
public void setLockSettingsHideUserList(Boolean lockSettingsHideUserList) {
this.defaultLockSettingsHideUserList = lockSettingsHideUserList;
}
public void setLockSettingsLockedLayout(Boolean lockSettingsLockedLayout) {
this.defaultLockSettingsLockedLayout = lockSettingsLockedLayout;
}

View File

@ -6,6 +6,7 @@ public class LockSettingsParams {
public final Boolean disablePrivateChat;
public final Boolean disablePublicChat;
public final Boolean disableNote;
public final Boolean hideUserList;
public final Boolean lockedLayout;
public final Boolean lockOnJoin;
public final Boolean lockOnJoinConfigurable;
@ -15,6 +16,7 @@ public class LockSettingsParams {
Boolean disablePrivateChat,
Boolean disablePublicChat,
Boolean disableNote,
Boolean hideUserList,
Boolean lockedLayout,
Boolean lockOnJoin,
Boolean lockOnJoinConfigurable) {
@ -23,6 +25,7 @@ public class LockSettingsParams {
this.disablePrivateChat = disablePrivateChat;
this.disablePublicChat = disablePublicChat;
this.disableNote = disableNote;
this.hideUserList = hideUserList;
this.lockedLayout = lockedLayout;
this.lockOnJoin = lockOnJoin;
this.lockOnJoinConfigurable = lockOnJoinConfigurable;

View File

@ -188,6 +188,7 @@ class BbbWebApiGWApp(
disablePrivateChat = lockSettingsParams.disablePrivateChat.booleanValue(),
disablePublicChat = lockSettingsParams.disablePublicChat.booleanValue(),
disableNote = lockSettingsParams.disableNote.booleanValue(),
hideUserList = lockSettingsParams.hideUserList.booleanValue(),
lockedLayout = lockSettingsParams.lockedLayout.booleanValue(),
lockOnJoin = lockSettingsParams.lockOnJoin.booleanValue(),
lockOnJoinConfigurable = lockSettingsParams.lockOnJoinConfigurable.booleanValue()

View File

@ -344,8 +344,9 @@ package org.bigbluebutton.core
var lockOptions:LockOptions = Options.getOptions(LockOptions) as LockOptions;
var lockSettings:LockSettingsVO = new LockSettingsVO(lockOptions.disableCam, lockOptions.disableMic,
lockOptions.disablePrivateChat, lockOptions.disablePublicChat, lockOptions.disableNote,
lockOptions.lockedLayout, lockOptions.lockOnJoin,
lockOptions.lockOnJoinConfigurable);
lockOptions.lockedLayout,
false, // stubbed out hideUserList lock setting that is only used in the HTML5 client
lockOptions.lockOnJoin, lockOptions.lockOnJoinConfigurable);
var event:LockControlEvent = new LockControlEvent(LockControlEvent.SAVE_LOCK_SETTINGS);
event.payload = lockSettings.toMap();

View File

@ -27,6 +27,7 @@ package org.bigbluebutton.core.vo
private var disablePublicChat:Boolean;
private var disableNote:Boolean;
private var lockedLayout:Boolean;
private var hideUserList:Boolean;
private var lockOnJoin:Boolean;
public function LockSettingsVO(pDisableCam:Boolean=false,
@ -35,6 +36,7 @@ package org.bigbluebutton.core.vo
pDisablePublicChat:Boolean=false,
pDisableNote:Boolean=false,
pLockLayout: Boolean=false,
pHideUserList:Boolean=false,
pLockOnJoin:Boolean=false,
pLockOnJoinConfigurable:Boolean=false)
{
@ -44,6 +46,7 @@ package org.bigbluebutton.core.vo
this.disablePublicChat = pDisablePublicChat;
this.disableNote = pDisableNote;
this.lockedLayout = pLockLayout;
this.hideUserList = pHideUserList;
this.lockOnJoin = pLockOnJoin;
this.lockOnJoinConfigurable = pLockOnJoinConfigurable;
}
@ -56,6 +59,7 @@ package org.bigbluebutton.core.vo
disablePublicChat: this.disablePublicChat,
disableNote: this.disableNote,
lockedLayout: this.lockedLayout,
hideUserList: this.hideUserList,
lockOnJoin: this.lockOnJoin,
lockOnJoinConfigurable: this.lockOnJoinConfigurable
};
@ -87,6 +91,10 @@ package org.bigbluebutton.core.vo
return lockedLayout;
}
public function getHideUserList():Boolean {
return hideUserList;
}
public function getLockOnJoin():Boolean {
return lockOnJoin;
}

View File

@ -43,6 +43,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
[Bindable] public var disablePrivChat:Boolean = false;
[Bindable] public var disableNote:Boolean = false;
[Bindable] public var lockedLayout:Boolean = false;
[Bindable] public var hideUserList:Boolean = false;
[Bindable] public var lockOnJoin:Boolean = false;
[Bindable] public var lockOnJoinConfigurable:Boolean = false;
@ -54,7 +55,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
private function onSaveClicked():void {
var event:LockControlEvent = new LockControlEvent(LockControlEvent.SAVE_LOCK_SETTINGS);
var lockSettings:LockSettingsVO = new LockSettingsVO(chkDisableWebcam.selected, chkDisableMicrophone.selected, chkDisablePrivateChat.selected, chkDisablePublicChat.selected, chkDisableNote.selected, chkDisableLayout.selected, chkLockOnJoin.selected, lockOnJoinConfigurable);
var lockSettings:LockSettingsVO = new LockSettingsVO(chkDisableWebcam.selected, chkDisableMicrophone.selected, chkDisablePrivateChat.selected, chkDisablePublicChat.selected, chkDisableNote.selected, chkDisableLayout.selected, hideUserList, chkLockOnJoin.selected, lockOnJoinConfigurable);
event.payload = lockSettings.toMap();
dispatchEvent(event);

View File

@ -762,6 +762,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
ls.disablePrivChat = lsv.getDisablePrivateChat();
ls.disablePubChat = lsv.getDisablePublicChat();
ls.disableNote = lsv.getDisableNote();
ls.hideUserList = lsv.getHideUserList();
ls.lockedLayout = lsv.getLockedLayout();
ls.lockOnJoin = lsv.getLockOnJoin();
ls.lockOnJoinConfigurable = lsv.getLockOnJoinConfigurable();

View File

@ -22,9 +22,9 @@ package org.bigbluebutton.modules.users.services
import flash.utils.setTimeout;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.utils.ObjectUtil;
import mx.collections.ArrayCollection;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
@ -695,6 +695,7 @@ package org.bigbluebutton.modules.users.services
body.disablePubChat as Boolean,
body.disableNote as Boolean,
body.lockedLayout as Boolean,
body.hideUserList as Boolean,
body.lockOnJoin as Boolean,
body.lockOnJoinConfigurable as Boolean);
UsersUtil.setLockSettings(lockSettings);
@ -711,6 +712,7 @@ package org.bigbluebutton.modules.users.services
body.disablePubChat as Boolean,
body.disableNote as Boolean,
body.lockedLayout as Boolean,
body.hideUserList as Boolean,
body.lockOnJoin as Boolean,
body.lockOnJoinConfigurable as Boolean);
UsersUtil.setLockSettings(lockSettings);

View File

@ -653,7 +653,8 @@ package org.bigbluebutton.modules.users.services
disablePrivChat: newLockSettings.disablePrivateChat,
disablePubChat: newLockSettings.disablePublicChat,
disableNote: newLockSettings.disableNote,
lockedLayout: newLockSettings.lockedLayout,
lockedLayout: newLockSettings.lockedLayout,
hideUserList: newLockSettings.hideUserList,
lockOnJoin: newLockSettings.lockOnJoin,
lockOnJoinConfigurable: newLockSettings.lockOnJoinConfigurable,
setBy: UsersUtil.getMyUserID()}

View File

@ -17,6 +17,7 @@ export default function toggleLockSettings(credentials, meeting) {
disablePrivateChat: Boolean,
disablePublicChat: Boolean,
disableNote: Boolean,
hideUserList: Boolean,
lockedLayout: Boolean,
lockOnJoin: Boolean,
lockOnJoinConfigurable: Boolean,
@ -29,6 +30,7 @@ export default function toggleLockSettings(credentials, meeting) {
disablePrivChat: meeting.lockSettingsProps.disablePrivateChat,
disablePubChat: meeting.lockSettingsProps.disablePublicChat,
disableNote: meeting.lockSettingsProps.disableNote,
hideUserList: meeting.lockSettingsProps.hideUserList,
lockedLayout: meeting.lockSettingsProps.lockedLayout,
lockOnJoin: meeting.lockSettingsProps.lockOnJoin,
lockOnJoinConfigurable: meeting.lockSettingsProps.lockOnJoinConfigurable,

View File

@ -79,6 +79,7 @@ export default function addMeeting(meeting) {
disablePrivateChat: Boolean,
disablePublicChat: Boolean,
disableNote: Boolean,
hideUserList: Boolean,
lockOnJoin: Boolean,
lockOnJoinConfigurable: Boolean,
lockedLayout: Boolean,

View File

@ -10,6 +10,7 @@ export default function changeLockSettings(meetingId, payload) {
disablePrivChat: Boolean,
disablePubChat: Boolean,
disableNote: Boolean,
hideUserList: Boolean,
lockedLayout: Boolean,
lockOnJoin: Boolean,
lockOnJoinConfigurable: Boolean,
@ -22,6 +23,7 @@ export default function changeLockSettings(meetingId, payload) {
disablePrivChat,
disablePubChat,
disableNote,
hideUserList,
lockedLayout,
lockOnJoin,
lockOnJoinConfigurable,
@ -40,6 +42,7 @@ export default function changeLockSettings(meetingId, payload) {
disablePrivateChat: disablePrivChat,
disablePublicChat: disablePubChat,
disableNote,
hideUserList,
lockedLayout,
lockOnJoin,
lockOnJoinConfigurable,

View File

@ -27,9 +27,7 @@ export default function handleEmojiStatus({ body }, meetingId) {
}
if (numChanged) {
Logger.info(`Assigned user emoji status${
emoji} id=${userId} meeting=${meetingId}`,
);
Logger.info(`Assigned user emoji status ${emoji} id=${userId} meeting=${meetingId}`);
}
};

View File

@ -81,7 +81,10 @@ const DesktopShare = ({
},
}, 'Default error handler for screenshare');
}
screenShareEndAlert();
// Don't trigger the screen share end alert if presenter click to cancel on screen share dialog
if (error && error.name !== 'NotAllowedError') {
screenShareEndAlert();
}
};
const screenshareLocked = screenshareDataSavingSetting

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { findDOMNode } from 'react-dom';
import cx from 'classnames';
import { defineMessages, injectIntl } from 'react-intl';
import { defineMessages, injectIntl, intlShape } from 'react-intl';
import Button from '/imports/ui/components/button/component';
import screenreaderTrap from 'makeup-screenreader-trap';
import { styles } from './styles';
@ -26,37 +26,40 @@ const propTypes = {
const children = props[propName];
if (!children || children.length < 2) {
return new Error(`Invalid prop \`${propName}\` supplied to` +
` \`${componentName}\`. Validation failed.`);
return new Error(`Invalid prop \`${propName}\` supplied to`
+ ` \`${componentName}\`. Validation failed.`);
}
const trigger = children.find(x => x.type === DropdownTrigger);
const content = children.find(x => x.type === DropdownContent);
if (!trigger) {
return new Error(`Invalid prop \`${propName}\` supplied to` +
` \`${componentName}\`. Missing \`DropdownTrigger\`. Validation failed.`);
return new Error(`Invalid prop \`${propName}\` supplied to`
+ ` \`${componentName}\`. Missing \`DropdownTrigger\`. Validation failed.`);
}
if (!content) {
return new Error(`Invalid prop \`${propName}\` supplied to` +
` \`${componentName}\`. Missing \`DropdownContent\`. Validation failed.`);
return new Error(`Invalid prop \`${propName}\` supplied to`
+ ` \`${componentName}\`. Missing \`DropdownContent\`. Validation failed.`);
}
return null;
},
isOpen: PropTypes.bool,
keepOpen: PropTypes.bool,
onHide: PropTypes.func,
onShow: PropTypes.func,
autoFocus: PropTypes.bool,
intl: intlShape.isRequired,
};
const defaultProps = {
children: null,
keepOpen: null,
onShow: noop,
onHide: noop,
autoFocus: false,
isOpen: false,
keepOpen: false,
};
class Dropdown extends Component {
@ -82,6 +85,7 @@ class Dropdown extends Component {
const { isOpen } = this.state;
if (isOpen && !prevState.isOpen) { onShow(); }
if (!isOpen && prevState.isOpen) { onHide(); }
@ -90,6 +94,7 @@ class Dropdown extends Component {
}
handleShow() {
Session.set('dropdownOpen', true);
const {
onShow,
} = this.props;
@ -101,6 +106,7 @@ class Dropdown extends Component {
}
handleHide() {
Session.set('dropdownOpen', false);
const { onHide } = this.props;
this.setState({ isOpen: false }, () => {
const { removeEventListener } = window;
@ -115,28 +121,33 @@ class Dropdown extends Component {
const triggerElement = findDOMNode(this.trigger);
const contentElement = findDOMNode(this.content);
if (!(triggerElement && contentElement)) return;
if (keepOpen === null) {
if (triggerElement.contains(event.target)) {
if (triggerElement && triggerElement.contains(event.target)) {
if (keepOpen) {
onHide();
return;
}
if (isOpen) {
this.handleHide();
return;
}
}
if (triggerElement && triggerElement.contains(event.target)) {
if (keepOpen) return onHide();
if (isOpen) return this.handleHide();
}
if (keepOpen && isOpen && !contentElement.contains(event.target)) {
if (triggerElement) {
const { parentElement } = triggerElement;
if (parentElement) parentElement.focus();
}
onHide();
this.handleHide();
return;
}
if (keepOpen !== null) {
return;
if (keepOpen && triggerElement) {
const { parentElement } = triggerElement;
if (parentElement) parentElement.focus();
}
this.handleHide();
if (!keepOpen) this.handleHide();
}
handleToggle() {
@ -148,12 +159,11 @@ class Dropdown extends Component {
const {
children,
className,
style,
intl,
keepOpen,
...otherProps
} = this.props;
const { isOpen } = this.state;
let trigger = children.find(x => x.type === DropdownTrigger);
@ -175,31 +185,31 @@ class Dropdown extends Component {
dropdownShow: this.handleShow,
dropdownHide: this.handleHide,
});
const showCloseBtn = (isOpen && keepOpen) || (isOpen && keepOpen === null);
return (
<div
style={style}
className={cx(styles.dropdown, className)}
aria-live={otherProps['aria-live']}
aria-relevant={otherProps['aria-relevant']}
aria-haspopup={otherProps['aria-haspopup']}
aria-label={otherProps['aria-label']}
data-isopen={this.state.isOpen}
ref={(node) => { this.dropdown = node; }}
tabIndex={-1}
>
{trigger}
{content}
{showCloseBtn ?
<Button
className={styles.close}
label={intl.formatMessage(intlMessages.close)}
size="lg"
color="default"
onClick={this.handleHide}
/> : null}
{showCloseBtn
? (
<Button
className={styles.close}
label={intl.formatMessage(intlMessages.close)}
size="lg"
color="default"
onClick={this.handleHide}
/>
) : null}
</div>
);
}

View File

@ -25,7 +25,6 @@ class JoinHandler extends Component {
constructor(props) {
super(props);
this.fetchToken = this.fetchToken.bind(this);
this.numFetchTokenRetries = 0;
this.state = {
joined: false,
@ -34,7 +33,51 @@ class JoinHandler extends Component {
componentDidMount() {
this._isMounted = true;
this.fetchToken();
if (!this.firstJoinTime) {
this.firstJoinTime = new Date();
}
Tracker.autorun((c) => {
const {
connected,
status,
} = Meteor.status();
logger.debug(`Initial connection status change. status: ${status}, connected: ${connected}`);
if (connected) {
c.stop();
const msToConnect = (new Date() - this.firstJoinTime) / 1000;
const secondsToConnect = parseFloat(msToConnect).toFixed(2);
logger.info({
logCode: 'joinhandler_component_initial_connection_time',
extraInfo: {
attemptForUserInfo: Auth.fullInfo,
timeToConnect: secondsToConnect,
},
}, `Connection to Meteor took ${secondsToConnect}s`);
this.firstJoinTime = undefined;
this.fetchToken();
} else if (status === 'failed') {
c.stop();
const msToConnect = (new Date() - this.firstJoinTime) / 1000;
const secondsToConnect = parseFloat(msToConnect).toFixed(2);
logger.info({
logCode: 'joinhandler_component_initial_connection_failed',
extraInfo: {
attemptForUserInfo: Auth.fullInfo,
timeToConnect: secondsToConnect,
},
}, `Connection to Meteor failed, took ${secondsToConnect}s`);
JoinHandler.setError('400');
Session.set('errorMessageDescription', 'Failed to connect to server');
this.firstJoinTime = undefined;
}
});
}
componentWillUnmount() {
@ -44,21 +87,6 @@ class JoinHandler extends Component {
async fetchToken() {
if (!this._isMounted) return;
if (!Meteor.status().connected) {
if (this.numFetchTokenRetries % 9) {
logger.error({
logCode: 'joinhandler_component_fetchToken_not_connected',
extraInfo: {
attemptForUserInfo: Auth.fullInfo,
numFetchTokenRetries: this.numFetchTokenRetries,
},
}, 'Meteor was not connected, retry in a few moments');
}
this.numFetchTokenRetries += 1;
setTimeout(() => this.fetchToken(), 200);
return;
}
const urlParams = new URLSearchParams(window.location.search);
const sessionToken = urlParams.get('sessionToken');

View File

@ -33,27 +33,31 @@ const intlMessages = defineMessages({
},
webcamLabel: {
id: 'app.lock-viewers.webcamLabel',
description: 'description for close button',
description: 'label for webcam toggle',
},
otherViewersWebcamLabel: {
id: 'app.lock-viewers.otherViewersWebcamLabel',
description: 'description for close button',
description: 'label for other viewers webcam toggle',
},
microphoneLable: {
id: 'app.lock-viewers.microphoneLable',
description: 'description for close button',
description: 'label for microphone toggle',
},
publicChatLabel: {
id: 'app.lock-viewers.PublicChatLabel',
description: 'description for close button',
description: 'label for public chat toggle',
},
privateChatLable: {
id: 'app.lock-viewers.PrivateChatLable',
description: 'description for close button',
description: 'label for private chat toggle',
},
notesLabel: {
id: 'app.lock-viewers.notesLabel',
description: 'description for close button',
description: 'label for shared notes toggle',
},
userListLabel: {
id: 'app.lock-viewers.userListLabel',
description: 'label for user list toggle',
},
ariaModalTitle: {
id: 'app.lock-viewers.ariaTitle',
@ -239,6 +243,29 @@ class LockViewersComponent extends React.PureComponent {
)
: null
}
<div className={styles.row}>
<div className={styles.col} aria-hidden="true">
<div className={styles.formElement}>
<div className={styles.label}>
{intl.formatMessage(intlMessages.userListLabel)}
</div>
</div>
</div>
<div className={styles.col}>
<div className={cx(styles.formElement, styles.pullContentRight)}>
<Toggle
icons={false}
defaultChecked={meeting.lockSettingsProps.hideUserList}
onChange={() => {
meeting.lockSettingsProps.hideUserList = !meeting.lockSettingsProps.hideUserList;
toggleLockSettings(meeting);
}}
ariaLabel={intl.formatMessage(intlMessages.userListLabel)}
/>
</div>
</div>
</div>
</div>
</div>
</Modal>

View File

@ -24,6 +24,10 @@ const intlDisableMessages = defineMessages({
id: 'app.userList.userOptions.disableNote',
description: 'label to disable note notification',
},
hideUserList: {
id: 'app.userList.userOptions.hideUserList',
description: 'label to hide user list notification',
},
onlyModeratorWebcam: {
id: 'app.userList.userOptions.webcamsOnlyForModerator',
description: 'label to disable all webcams except for the moderators cam',
@ -51,6 +55,10 @@ const intlEnableMessages = defineMessages({
id: 'app.userList.userOptions.enableNote',
description: 'label to enable note notification',
},
hideUserList: {
id: 'app.userList.userOptions.showUserList',
description: 'label to show user list notification',
},
onlyModeratorWebcam: {
id: 'app.userList.userOptions.enableOnlyModeratorWebcam',
description: 'label to enable all webcams except for the moderators cam',

View File

@ -17,6 +17,7 @@ import logger from '/imports/startup/client/logger';
const CHAT_CONFIG = Meteor.settings.public.chat;
const PUBLIC_GROUP_CHAT_ID = CHAT_CONFIG.public_group_id;
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
const ROLE_VIEWER = Meteor.settings.public.user.role_viewer;
const DIAL_IN_CLIENT_TYPE = 'dial-in-user';
@ -181,13 +182,22 @@ const userFindSorting = {
};
const getUsers = () => {
const users = Users
let users = Users
.find({
meetingId: Auth.meetingID,
connectionStatus: 'online',
}, userFindSorting)
.fetch();
const currentUser = Users.findOne({ userId: Auth.userID });
if (currentUser && currentUser.role === ROLE_VIEWER && currentUser.locked) {
const meeting = Meetings.findOne({ meetingId: Auth.meetingID });
if (meeting && meeting.lockSettingsProps && meeting.lockSettingsProps.hideUserList) {
const moderatorOrCurrentUser = u => u.role === ROLE_MODERATOR || u.userId === Auth.userID;
users = users.filter(moderatorOrCurrentUser);
}
}
return users.sort(sortUsers);
};
@ -358,12 +368,12 @@ const normalizeEmojiName = emoji => (
emoji in EMOJI_STATUSES ? EMOJI_STATUSES[emoji] : emoji
);
const setEmojiStatus = (data) => {
const statusAvailable = (Object.keys(EMOJI_STATUSES).includes(data));
const setEmojiStatus = (userId, emoji) => {
const statusAvailable = (Object.keys(EMOJI_STATUSES).includes(emoji));
return statusAvailable
? makeCall('setEmojiStatus', Auth.userID, data)
: makeCall('setEmojiStatus', data, 'none');
? makeCall('setEmojiStatus', Auth.userID, emoji)
: makeCall('setEmojiStatus', userId, 'none');
};
const assignPresenter = (userId) => { makeCall('assignPresenter', userId); };
@ -394,8 +404,11 @@ const muteAllExceptPresenter = (userId) => { makeCall('muteAllExceptPresenter',
const changeRole = (userId, role) => { makeCall('changeRole', userId, role); };
const roving = (event, itemCount, changeState) => {
if (document.activeElement.getAttribute('data-isopen') === 'true') {
const roving = (event, changeState, elementsList, element) => {
this.selectedElement = element;
const menuOpen = Session.get('dropdownOpen') || false;
if (menuOpen) {
const menuChildren = document.activeElement.getElementsByTagName('li');
if ([KEY_CODES.ESCAPE, KEY_CODES.ARROW_LEFT].includes(event.keyCode)) {
@ -418,34 +431,23 @@ const roving = (event, itemCount, changeState) => {
return;
}
if (this.selectedIndex === undefined) {
this.selectedIndex = -1;
}
if ([KEY_CODES.ESCAPE, KEY_CODES.TAB].includes(event.keyCode)) {
document.activeElement.blur();
this.selectedIndex = -1;
changeState(this.selectedIndex);
changeState(null);
}
if (event.keyCode === KEY_CODES.ARROW_DOWN) {
this.selectedIndex += 1;
if (this.selectedIndex === itemCount) {
this.selectedIndex = 0;
}
changeState(this.selectedIndex);
const firstElement = elementsList.firstChild;
let elRef = element ? element.nextSibling : firstElement;
elRef = elRef || firstElement;
changeState(elRef);
}
if (event.keyCode === KEY_CODES.ARROW_UP) {
this.selectedIndex -= 1;
if (this.selectedIndex < 0) {
this.selectedIndex = itemCount - 1;
}
changeState(this.selectedIndex);
const lastElement = elementsList.lastChild;
let elRef = element ? element.previousSibling : lastElement;
elRef = elRef || lastElement;
changeState(elRef);
}
if ([KEY_CODES.ARROW_RIGHT, KEY_CODES.SPACE, KEY_CODES.ENTER].includes(event.keyCode)) {

View File

@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import { defineMessages } from 'react-intl';
import cx from 'classnames';
import { styles } from '/imports/ui/components/user-list/user-list-content/styles';
import { findDOMNode } from 'react-dom';
import ChatListItemContainer from '../../chat-list-item/container';
const propTypes = {
@ -41,38 +42,31 @@ class UserMessages extends PureComponent {
super();
this.state = {
index: -1,
selectedChat: null,
};
this.activeChatRefs = [];
this.selectedIndex = -1;
this.focusActiveChatItem = this.focusActiveChatItem.bind(this);
this.changeState = this.changeState.bind(this);
this.rove = this.rove.bind(this);
}
componentDidMount() {
const { compact, roving, activeChats } = this.props;
const { compact } = this.props;
if (!compact) {
this._msgsList.addEventListener(
'keydown',
event => roving(
event,
activeChats.length,
this.changeState,
),
this.rove,
);
}
}
componentDidUpdate(prevProps, prevState) {
const { index } = this.state;
if (index === -1) {
return;
}
const { selectedChat } = this.state;
if (index !== prevState.index) {
this.focusActiveChatItem(index);
if (selectedChat && selectedChat !== prevState.selectedChat) {
const { firstChild } = selectedChat;
if (firstChild) firstChild.focus();
}
}
@ -108,16 +102,15 @@ class UserMessages extends PureComponent {
));
}
changeState(newIndex) {
this.setState({ index: newIndex });
changeState(ref) {
this.setState({ selectedChat: ref });
}
focusActiveChatItem(index) {
if (!this.activeChatRefs[index]) {
return;
}
this.activeChatRefs[index].firstChild.focus();
rove(event) {
const { roving } = this.props;
const { selectedChat } = this.state;
const msgItemsRef = findDOMNode(this._msgItems);
roving(event, this.changeState, msgItemsRef, selectedChat);
}
render() {

View File

@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
import cx from 'classnames';
import { styles } from '/imports/ui/components/user-list/user-list-content/styles';
import _ from 'lodash';
import { findDOMNode } from 'react-dom';
import UserListItemContainer from './user-list-item/container';
import UserOptionsContainer from './user-options/container';
@ -62,46 +63,43 @@ class UserParticipants extends Component {
super();
this.state = {
index: -1,
selectedUser: null,
};
this.userRefs = [];
this.selectedIndex = -1;
this.getScrollContainerRef = this.getScrollContainerRef.bind(this);
this.focusUserItem = this.focusUserItem.bind(this);
this.rove = this.rove.bind(this);
this.changeState = this.changeState.bind(this);
this.getUsers = this.getUsers.bind(this);
}
componentDidMount() {
const { compact, roving, users } = this.props;
const { compact } = this.props;
if (!compact) {
this.refScrollContainer.addEventListener(
'keydown',
event => roving(
event,
users.length,
this.changeState,
),
this.rove,
);
}
}
componentWillUnmount() {
this.refScrollContainer.removeEventListener('keydown', this.rove);
}
shouldComponentUpdate(nextProps, nextState) {
const isPropsEqual = _.isEqual(this.props, nextProps);
const isStateEqual = _.isEqual(this.state, nextState);
return !isPropsEqual || !isStateEqual;
}
componentDidUpdate(prevProps, prevState) {
const { index } = this.state;
if (index === -1) {
return;
}
componentDidUpdate() {
const { selectedUser } = this.state;
if (index !== prevState.index) {
this.focusUserItem(index);
if (selectedUser) {
const { firstChild } = selectedUser;
if (firstChild) firstChild.focus();
}
}
@ -177,14 +175,15 @@ class UserParticipants extends Component {
));
}
focusUserItem(index) {
if (!this.userRefs[index]) return;
this.userRefs[index].firstChild.focus();
rove(event) {
const { roving } = this.props;
const { selectedUser } = this.state;
const usersItemsRef = findDOMNode(this.refScrollItems);
roving(event, this.changeState, usersItemsRef, selectedUser);
}
changeState(newIndex) {
this.setState({ index: newIndex });
changeState(ref) {
this.setState({ selectedUser: ref });
}
render() {
@ -230,7 +229,6 @@ class UserParticipants extends Component {
}
<div
className={styles.scrollableList}
role="list"
tabIndex={0}
ref={(ref) => { this.refScrollContainer = ref; }}
>

View File

@ -153,10 +153,13 @@ class UserDropdown extends PureComponent {
}
componentDidUpdate() {
const { dropdownVisible } = this.props;
if (!dropdownVisible) document.activeElement.blur();
this.checkDropdownDirection();
}
onActionsShow() {
Session.set('dropdownOpen', true);
const { getScrollContainerRef } = this.props;
const dropdown = this.getDropdownMenuParent();
const scrollContainer = getScrollContainerRef();
@ -193,6 +196,8 @@ class UserDropdown extends PureComponent {
if (callback) {
return callback;
}
return Session.set('dropdownOpen', false);
}
getUsersActions() {
@ -251,7 +256,12 @@ class UserDropdown extends PureComponent {
actions.push(this.makeDropdownItem(
'back',
intl.formatMessage(messages.backTriggerLabel),
() => this.setState({ showNestedOptions: false, isActionsOpen: true }),
() => this.setState(
{
showNestedOptions: false,
isActionsOpen: true,
}, Session.set('dropdownOpen', true),
),
'left_arrow',
));
}
@ -262,7 +272,7 @@ class UserDropdown extends PureComponent {
statuses.map(status => actions.push(this.makeDropdownItem(
status,
intl.formatMessage({ id: `app.actionsBar.emojiMenu.${status}Label` }),
() => { handleEmojiChange(status); this.resetMenuState(); },
() => { handleEmojiChange(user.id, status); this.resetMenuState(); },
getEmojiList[status],
)));
@ -273,7 +283,12 @@ class UserDropdown extends PureComponent {
actions.push(this.makeDropdownItem(
'setstatus',
intl.formatMessage(messages.statusTriggerLabel),
() => this.setState({ showNestedOptions: true, isActionsOpen: true }),
() => this.setState(
{
showNestedOptions: true,
isActionsOpen: true,
}, Session.set('dropdownOpen', true),
),
'user',
'right_arrow',
));
@ -440,7 +455,8 @@ class UserDropdown extends PureComponent {
);
if (!isDropdownVisible) {
const offsetPageTop = (dropdownTrigger.offsetTop + dropdownTrigger.offsetHeight) - scrollContainer.scrollTop;
const { offsetTop, offsetHeight } = dropdownTrigger;
const offsetPageTop = (offsetTop + offsetHeight) - scrollContainer.scrollTop;
nextState.dropdownOffset = window.innerHeight - offsetPageTop;
nextState.dropdownDirection = 'bottom';

View File

@ -101,7 +101,7 @@
}
.userAvatar {
flex: 0 0 2.2rem;
flex: 0 0 2.25rem;
}
.userItemContents {

View File

@ -37,7 +37,7 @@ const UserOptionsContainer = withTracker((props) => {
} = props;
const toggleStatus = () => {
users.forEach(id => setEmojiStatus(id, 'none'));
users.forEach(user => setEmojiStatus(user.userId, 'none'));
notify(
intl.formatMessage(intlMessages.clearStatusMessage), 'info', 'clear_status',
);

View File

@ -75,6 +75,7 @@ class VideoService {
getAllWebcamUsers() {
const webcamsLocked = this.webcamsLocked();
const webcamsOnlyForModerator = this.webcamsOnlyForModerator();
const hideUserList = this.hideUserList();
const currentUser = Users.findOne({ userId: Auth.userID });
const currentUserIsViewer = currentUser.role === ROLE_VIEWER;
const sharedWebcam = this.isSharing;
@ -96,7 +97,7 @@ class VideoService {
const userIsModerator = user => user.role === ROLE_MODERATOR;
if (webcamsOnlyForModerator && currentUserIsViewer) {
if ((webcamsOnlyForModerator || hideUserList) && currentUserIsViewer) {
users = users.filter(userIsModerator);
}
@ -117,6 +118,11 @@ class VideoService {
return m.lockSettingsProps ? m.lockSettingsProps.disableCam : false;
}
hideUserList() {
const m = Meetings.findOne({ meetingId: Auth.meetingID }) || {};
return m.lockSettingsProps ? m.lockSettingsProps.hideUserList : false;
}
userId() {
return Auth.userID;
}

View File

@ -8,8 +8,8 @@ const CURSOR_INTERVAL = 32;
export default class CursorListener extends Component {
static touchCenterPoint(touches) {
let totalX = 0; let
totalY = 0;
let totalX = 0;
let totalY = 0;
for (let i = 0; i < touches.length; i += 1) {
totalX += touches[i].clientX;
@ -128,6 +128,12 @@ export default class CursorListener extends Component {
this.checkCursor(-1, -1);
}
clearTouchEvents() {
this.removeTouchListeners();
this.touchStarted = false;
this.checkCursor(-1, -1);
}
handleTouchStart(event) {
event.preventDefault();
@ -153,20 +159,14 @@ export default class CursorListener extends Component {
handleTouchEnd(event) {
event.preventDefault();
if (event.touches.length === 1) {
this.removeTouchListeners();
this.touchStarted = false;
this.checkCursor(-1, -1);
}
this.clearTouchEvents();
}
handleTouchCancel(event) {
event.preventDefault();
if (event.touches.length === 0) {
this.removeTouchListeners();
this.touchStarted = false;
this.checkCursor(-1, -1);
this.clearTouchEvents();
}
}

View File

@ -71,13 +71,20 @@ class WhiteboardToolbar extends Component {
constructor(props) {
super(props);
const { annotations } = this.props;
const { annotations, multiUser, isPresenter } = this.props;
let annotationSelected = {
icon: 'hand',
value: 'hand',
};
if (multiUser && !isPresenter) {
annotationSelected = {
icon: 'pen_tool',
value: 'pencil',
};
}
if (!annotations.some(el => el.value === annotationSelected.value) && annotations.length > 0) {
annotationSelected = annotations[annotations.length - 1];
}
@ -123,19 +130,28 @@ class WhiteboardToolbar extends Component {
this.panOff = this.panOff.bind(this);
}
componentWillMount() {
const { actions } = this.props;
componentDidMount() {
const { actions, multiUser, isPresenter } = this.props;
const drawSettings = actions.getCurrentDrawSettings();
const {
annotationSelected, thicknessSelected, colorSelected, fontSizeSelected,
} = this.state;
document.addEventListener('keydown', this.panOn);
document.addEventListener('keyup', this.panOff);
// if there are saved drawSettings in the session storage
// - retrieve them and update toolbar values
if (drawSettings) {
if (multiUser && !isPresenter) {
drawSettings.whiteboardAnnotationTool = 'pencil';
this.handleAnnotationChange({ icon: 'pen_tool', value: 'pencil' });
}
this.setToolbarValues(drawSettings);
// no drawSettings in the sessionStorage - setting default values
} else {
// setting default drawing settings if they haven't been set previously
const {
annotationSelected, thicknessSelected, colorSelected, fontSizeSelected,
} = this.state;
actions.setInitialWhiteboardToolbarValues(
annotationSelected.value,
thicknessSelected.value * 2,
@ -147,13 +163,6 @@ class WhiteboardToolbar extends Component {
},
);
}
}
componentDidMount() {
const { annotationSelected } = this.state;
document.addEventListener('keydown', this.panOn);
document.addEventListener('keyup', this.panOff);
if (annotationSelected.value !== 'text') {
// trigger initial animation on the thickness circle, otherwise it stays at 0

View File

@ -469,6 +469,7 @@
"app.lock-viewers.PublicChatLabel": "الدردشة العامة",
"app.lock-viewers.PrivateChatLable": "دردشة خاصة",
"app.lock-viewers.notesLabel": "ملاحظات مشتركة",
"app.lock-viewers.userListLabel": "قائمة المستخدمين",
"app.lock-viewers.Layout": "مظهر",
"app.lock-viewers.ariaTitle": "نافذة قفل المشاهدين",
"app.recording.startTitle": "بدء التسجيل",

View File

@ -352,6 +352,7 @@
"app.lock-viewers.microphoneLable": "Mikrofon",
"app.lock-viewers.PublicChatLabel": "Veřejný chat",
"app.lock-viewers.PrivateChatLable": "Soukromý chat",
"app.lock-viewers.userListLabel": "Seznam uživatelů",
"app.lock-viewers.Layout": "Rozložení obrazovky",
"app.recording.startTitle": "Zahájit nahrávání",
"app.recording.startDescription": "(Můžete využít nahrávací tlačítko později pro pozastavení nahrávání.)",

View File

@ -21,6 +21,9 @@
"app.chat.offline": "Offline",
"app.chat.emptyLogLabel": "Chat-Log ist leer",
"app.chat.clearPublicChatMessage": "Der öffentliche Chatverlauf wurde durch einen Moderator gelöscht",
"app.chat.severalPeople": "Mehrere Personen",
"app.chat.pluralTyping": "tippen",
"app.chat.singularTyping": "tippt",
"app.captions.label": "Untertitel",
"app.captions.menu.close": "Schließen",
"app.captions.menu.start": "Start",
@ -95,6 +98,7 @@
"app.userList.userOptions.disablePrivChat": "Privater Chat ist deaktiviert",
"app.userList.userOptions.disablePubChat": "Öffentlicher Chat ist deaktiviert",
"app.userList.userOptions.disableNote": "Geteilte Notizen sind jetzt gesperrt",
"app.userList.userOptions.hideUserList": "Die Teilnehmerliste ist jetzt für die Teilnehmer nicht mehr sichtbar",
"app.userList.userOptions.webcamsOnlyForModerator": "Nur Moderatoren können die Teilnehmerwebcams sehen (wegen eingeschränkter Rechteeinstellungen)",
"app.userList.content.participants.options.clearedStatus": "Status aller Teilnehmer zurückgesetzt",
"app.userList.userOptions.enableCam": "Teilnehmer dürfen ihre Webcam verwenden",
@ -102,11 +106,15 @@
"app.userList.userOptions.enablePrivChat": "Privater Chat ist erlaubt",
"app.userList.userOptions.enablePubChat": "Öffentlicher Chat ist erlaubt",
"app.userList.userOptions.enableNote": "Geteilte Notizen sind erlaubt",
"app.userList.userOptions.showUserList": "Die Teilnehmerliste ist jetzt für die Teilnehmer sichtbar",
"app.userList.userOptions.enableOnlyModeratorWebcam": "Sie können Ihre Webcam jetzt freigeben, jeder wird Sie sehen.",
"app.media.label": "Media",
"app.media.autoplayAlertDesc": "Zugang erlauben",
"app.media.screenshare.start": "Bildschirmfreigabe wurde gestartet",
"app.media.screenshare.end": "Bildschirmfreigabe wurde gestoppt",
"app.media.screenshare.safariNotSupported": "Bildschirmfreigabe wird gegenwärtig von Safari nicht unterstützt. Bitte verwenden Sie Firefox oder Google Chrome.",
"app.media.screenshare.autoplayBlockedDesc": "Wir benötigen Ihre Zustimmung, um Ihnen den Bildschirm des Präsentators zu zeigen.",
"app.media.screenshare.autoplayAllowLabel": "Geteilten Bildschirm ansehen",
"app.meeting.ended": "Diese Konferenz wurde beendet",
"app.meeting.meetingTimeRemaining": "Verbleibende Konferenzzeit: {0}",
"app.meeting.meetingTimeHasEnded": "Die Zeit ist abgelaufen. Die Konferenz wird in Kürze beendet",
@ -389,6 +397,9 @@
"app.audioModal.audioDialTitle": "Mit dem Telefon teilnehmen",
"app.audioDial.audioDialDescription": "Anrufen",
"app.audioDial.audioDialConfrenceText": "und geben Sie die Konferenz-PIN-Nummer ein:",
"app.audioModal.autoplayBlockedDesc": "Wir benötigen Ihre Zustimmung für die Audiowiedergabe.",
"app.audioModal.playAudio": "Audio wiedergeben",
"app.audioModal.playAudio.arialabel" : "Audio wiedergeben",
"app.audioDial.tipIndicator": "Tipp",
"app.audioDial.tipMessage": "Drücken Sie die '0' Taste Ihres Telefons, um sich stumm bzw. freizuschalten.",
"app.audioModal.connecting": "Verbinde",
@ -494,6 +505,7 @@
"app.lock-viewers.PublicChatLabel": "Öffentlicher Chat",
"app.lock-viewers.PrivateChatLable": "Privater Chat",
"app.lock-viewers.notesLabel": "Geteilte Notizen",
"app.lock-viewers.userListLabel": "Teilnehmerliste",
"app.lock-viewers.Layout": "Layout",
"app.lock-viewers.ariaTitle": "Teilnehmerrechtedialog",
"app.recording.startTitle": "Aufzeichnung starten",
@ -599,6 +611,8 @@
"app.videoDock.webcamFocusDesc": "Ausgewählte Webcam vergrößern",
"app.videoDock.webcamUnfocusLabel": "Normalgröße",
"app.videoDock.webcamUnfocusDesc": "Ausgewählte Webcam auf Normalgröße verkleinern",
"app.videoDock.autoplayBlockedDesc": "Wir benötigen Ihre Zustimmung, um Ihnen die Webcams anderer Teilnehmer zu zeigen.",
"app.videoDock.autoplayAllowLabel": "Webcams zeigen",
"app.invitation.title": "Breakoutraum-Einladung",
"app.invitation.confirm": "Einladen",
"app.createBreakoutRoom.title": "Breakout-Räume",

View File

@ -98,6 +98,7 @@
"app.userList.userOptions.disablePrivChat": "Private chat is disabled",
"app.userList.userOptions.disablePubChat": "Public chat is disabled",
"app.userList.userOptions.disableNote": "Shared notes are now locked",
"app.userList.userOptions.hideUserList": "User list is now hidden for viewers",
"app.userList.userOptions.webcamsOnlyForModerator": "Only moderators are able to see viewers' webcams (due to lock settings)",
"app.userList.content.participants.options.clearedStatus": "Cleared all user status",
"app.userList.userOptions.enableCam": "Viewers' webcams are enabled",
@ -105,6 +106,7 @@
"app.userList.userOptions.enablePrivChat": "Private chat is enabled",
"app.userList.userOptions.enablePubChat": "Public chat is enabled",
"app.userList.userOptions.enableNote": "Shared notes are now enabled",
"app.userList.userOptions.showUserList": "User list is now shown to viewers",
"app.userList.userOptions.enableOnlyModeratorWebcam": "You can enable your webcam now, everyone will see you",
"app.media.label": "Media",
"app.media.autoplayAlertDesc": "Allow Access",
@ -503,6 +505,7 @@
"app.lock-viewers.PublicChatLabel": "Public chat",
"app.lock-viewers.PrivateChatLable": "Private chat",
"app.lock-viewers.notesLabel": "Shared notes",
"app.lock-viewers.userListLabel": "User list",
"app.lock-viewers.Layout": "Layout",
"app.lock-viewers.ariaTitle": "Lock viewers modal",
"app.recording.startTitle": "Start recording",

View File

@ -483,6 +483,7 @@
"app.lock-viewers.PublicChatLabel": "Chat público",
"app.lock-viewers.PrivateChatLable": "Chat privado",
"app.lock-viewers.notesLabel": "Notas compartidas",
"app.lock-viewers.userListLabel": "Lista de usuarios",
"app.lock-viewers.Layout": "Diseño de pantalla",
"app.lock-viewers.ariaTitle": "Bloquear ventana flotante de espectadores",
"app.recording.startTitle": "Iniciar grabación",

View File

@ -409,6 +409,7 @@
"app.lock-viewers.microphoneLable": "Micrófono",
"app.lock-viewers.PublicChatLabel": "Chat público",
"app.lock-viewers.PrivateChatLable": "Chat privado",
"app.lock-viewers.userListLabel": "Lista de usuarios",
"app.lock-viewers.Layout": "Diseño de pantalla",
"app.recording.startTitle": "Iniciar grabación",
"app.recording.stopTitle": "Detener la grabación",

View File

@ -493,6 +493,7 @@
"app.lock-viewers.PublicChatLabel": "Txat publikoa",
"app.lock-viewers.PrivateChatLable": "Txat pribatua",
"app.lock-viewers.notesLabel": "Partekatutako oharrak",
"app.lock-viewers.userListLabel": "Erabiltzaileen zerrenda",
"app.lock-viewers.Layout": "Diseinua",
"app.lock-viewers.ariaTitle": "Blokeatu ikusleen modua",
"app.recording.startTitle": "Hasi grabatzen",

View File

@ -493,6 +493,7 @@
"app.lock-viewers.PublicChatLabel": "گفتگوی عمومی",
"app.lock-viewers.PrivateChatLable": "گفتگوی خصوصی",
"app.lock-viewers.notesLabel": "یادداشت های اشتراکی",
"app.lock-viewers.userListLabel": "لیست کاربر",
"app.lock-viewers.Layout": "چینش صفحه",
"app.lock-viewers.ariaTitle": "قفل کردن کادر کاربران",
"app.recording.startTitle": "شروع ضبط",

View File

@ -477,6 +477,7 @@
"app.lock-viewers.PublicChatLabel": "Julkinen chat",
"app.lock-viewers.PrivateChatLable": "Yksityinen chat",
"app.lock-viewers.notesLabel": "Jaetut muistiinpanot",
"app.lock-viewers.userListLabel": "Lista käyttäjistä",
"app.lock-viewers.Layout": "Sommittelu",
"app.recording.startTitle": "Aloita nauhoitus",
"app.recording.stopTitle": "Tauota nauhoitus",

View File

@ -487,6 +487,7 @@
"app.lock-viewers.PublicChatLabel": "Discussion publique",
"app.lock-viewers.PrivateChatLable": "Discussion privée",
"app.lock-viewers.notesLabel": "Notes partagées",
"app.lock-viewers.userListLabel": "Liste d'utilisateur",
"app.lock-viewers.Layout": "Disposition",
"app.lock-viewers.ariaTitle": "Verrouille la fenêtre modale des participants",
"app.recording.startTitle": "Commencer l'enregistrement",

View File

@ -53,6 +53,7 @@
"app.audioModal.no": "לא",
"app.lock-viewers.PublicChatLabel": "צ'אט ציבורי",
"app.lock-viewers.PrivateChatLable": "צ'אט פרטי",
"app.lock-viewers.userListLabel": "משתתפים",
"app.videoPreview.cancelLabel": "בטל",
"app.video.cancel": "בטל",
"app.externalVideo.noteLabel": "הערה: הסרטון לא יופיע בהקלטה, מומלץ לשלוח הקישור גם בצ'אט "

View File

@ -415,6 +415,7 @@
"app.lock-viewers.microphoneLable": "माइक्रोफ़ोन",
"app.lock-viewers.PublicChatLabel": "सार्वजनिक बातचीत",
"app.lock-viewers.PrivateChatLable": "गोपनीय बातचीत",
"app.lock-viewers.userListLabel": "उपयोगकर्ता सूची",
"app.lock-viewers.Layout": "ख़ाका",
"app.recording.startTitle": "रिकॉर्डिंग शुरू",
"app.recording.stopTitle": "रिकॉर्डिंग रोके",

View File

@ -484,6 +484,7 @@
"app.lock-viewers.PublicChatLabel": "Nyilvános üzenetek",
"app.lock-viewers.PrivateChatLable": "Privát üzenetek",
"app.lock-viewers.notesLabel": "Megosztott jegyzetek",
"app.lock-viewers.userListLabel": "Felhasználók",
"app.lock-viewers.Layout": "Elrendezés",
"app.lock-viewers.ariaTitle": "Zárolt felhasználók ablaka",
"app.recording.startTitle": "Felvétel indítása",

View File

@ -442,6 +442,7 @@
"app.lock-viewers.PublicChatLabel": "Chat pubblica",
"app.lock-viewers.PrivateChatLable": "Chat privata",
"app.lock-viewers.notesLabel": "Note condivise",
"app.lock-viewers.userListLabel": "Lista utenti",
"app.lock-viewers.Layout": "Disposizione",
"app.lock-viewers.ariaTitle": "Finestra di dialogo di blocco utente",
"app.recording.startTitle": "Avvia registrazione",

View File

@ -464,6 +464,7 @@
"app.lock-viewers.PublicChatLabel": "グループチャット",
"app.lock-viewers.PrivateChatLable": "非公開チャット",
"app.lock-viewers.notesLabel": "共有メモ",
"app.lock-viewers.userListLabel": "ユーザーリスト",
"app.lock-viewers.Layout": "レイアウト",
"app.lock-viewers.ariaTitle": "ビューアーウィンドウをロック",
"app.recording.startTitle": "録画開始",

View File

@ -464,6 +464,7 @@
"app.lock-viewers.PublicChatLabel": "グループチャット",
"app.lock-viewers.PrivateChatLable": "非公開チャット",
"app.lock-viewers.notesLabel": "共有メモ",
"app.lock-viewers.userListLabel": "ユーザーリスト",
"app.lock-viewers.Layout": "レイアウト",
"app.lock-viewers.ariaTitle": "ビューアーウィンドウをロック",
"app.recording.startTitle": "録画開始",

View File

@ -484,6 +484,7 @@
"app.lock-viewers.PublicChatLabel": "ជជែក​សាធារណៈ",
"app.lock-viewers.PrivateChatLable": "ជជែក​ឯកជន",
"app.lock-viewers.notesLabel": "កំណត់ត្រារួម",
"app.lock-viewers.userListLabel": "បញ្ជីអ្នកចូលរួម",
"app.lock-viewers.Layout": "ទម្រង់",
"app.lock-viewers.ariaTitle": "ផ្ទាំងបិទមុខងារសម្រាប់អ្នកមើល",
"app.recording.startTitle": "ចាប់ផ្តើមថត",

View File

@ -456,6 +456,7 @@
"app.lock-viewers.PublicChatLabel": "Chat público",
"app.lock-viewers.PrivateChatLable": "Chat privado",
"app.lock-viewers.notesLabel": "Notas partilhadas",
"app.lock-viewers.userListLabel": "Lista de utilizadores",
"app.lock-viewers.Layout": "Layout",
"app.lock-viewers.ariaTitle": "Bloquear a modal dos espetadores",
"app.recording.startTitle": "Iniciar gravação",

View File

@ -483,6 +483,7 @@
"app.lock-viewers.PublicChatLabel": "Bate-papo público",
"app.lock-viewers.PrivateChatLable": "Bate-papo privado",
"app.lock-viewers.notesLabel": "Notas compartilhadas",
"app.lock-viewers.userListLabel": "Lista de participantes",
"app.lock-viewers.Layout": "Layout",
"app.lock-viewers.ariaTitle": "Janela de restrição de participantes",
"app.recording.startTitle": "Iniciar gravação",

View File

@ -489,6 +489,7 @@
"app.lock-viewers.PublicChatLabel": "Общий чат",
"app.lock-viewers.PrivateChatLable": "Приватный чат",
"app.lock-viewers.notesLabel": "Общие заметки",
"app.lock-viewers.userListLabel": "Список пользователей",
"app.lock-viewers.Layout": "Расположение окон",
"app.lock-viewers.ariaTitle": "Окно блокировки участников",
"app.recording.startTitle": "Включить запись",

View File

@ -484,6 +484,7 @@
"app.lock-viewers.PublicChatLabel": "Publik chatt",
"app.lock-viewers.PrivateChatLable": "Privat chatt",
"app.lock-viewers.notesLabel": "Delade anteckningar",
"app.lock-viewers.userListLabel": "Användarlista",
"app.lock-viewers.Layout": "Layout",
"app.lock-viewers.ariaTitle": "Lås åhörarnas modal",
"app.recording.startTitle": "Starta inspelningen",

View File

@ -439,6 +439,7 @@
"app.lock-viewers.microphoneLable": "Мікрофон",
"app.lock-viewers.PublicChatLabel": "Публічний чат",
"app.lock-viewers.PrivateChatLable": "Приватний чат",
"app.lock-viewers.userListLabel": "Список користувачів",
"app.lock-viewers.Layout": "Розміщення вікон",
"app.recording.startTitle": "Почати запис",
"app.recording.startDescription": "(Ви зможете натиснути цю кнопку пізніше, щоб зробити паузу запису.)",

View File

@ -442,6 +442,7 @@
"app.lock-viewers.PublicChatLabel": "Cuộc trò chuyện công cộng",
"app.lock-viewers.PrivateChatLable": "Cuộc trò chuyện riêng tư",
"app.lock-viewers.notesLabel": "Chia sẻ ghi chú",
"app.lock-viewers.userListLabel": "Danh sách người dùng",
"app.lock-viewers.Layout": "Layout",
"app.lock-viewers.ariaTitle": "Khóa phương thức người xem",
"app.recording.startTitle": "Bắt đầu ghi âm",

View File

@ -493,6 +493,7 @@
"app.lock-viewers.PublicChatLabel": "公共聊天",
"app.lock-viewers.PrivateChatLable": "私聊",
"app.lock-viewers.notesLabel": "共享的笔记",
"app.lock-viewers.userListLabel": "用户列表",
"app.lock-viewers.Layout": "页面布局",
"app.lock-viewers.ariaTitle": "锁定观众模式",
"app.recording.startTitle": "开始录制",

View File

@ -4,6 +4,7 @@
"app.chat.errorMinMessageLength": "訊息僅 {0} 個字(數),太短了",
"app.chat.errorMaxMessageLength": "訊息有 {0} 個字(數),太長了",
"app.chat.disconnected": "您已經斷線了,訊息無法被送出。",
"app.chat.locked": "訊息已被鎖定,無法送出。",
"app.chat.inputLabel": "聊天訊息輸入 {0}",
"app.chat.inputPlaceholder": "發送訊息給 {0}",
"app.chat.titlePublic": "公開聊天",
@ -20,6 +21,9 @@
"app.chat.offline": "下線",
"app.chat.emptyLogLabel": "空的聊天紀錄",
"app.chat.clearPublicChatMessage": "公開聊天紀錄已被主持人清空了",
"app.chat.severalPeople": "許多參加者",
"app.chat.pluralTyping": "許多人正在打字",
"app.chat.singularTyping": "正在打字",
"app.captions.label": "字幕",
"app.captions.menu.close": "關閉",
"app.captions.menu.start": "開始",
@ -493,6 +497,7 @@
"app.lock-viewers.PublicChatLabel": "公開聊天",
"app.lock-viewers.PrivateChatLable": "私聊",
"app.lock-viewers.notesLabel": "共享筆記",
"app.lock-viewers.userListLabel": "用戶列表",
"app.lock-viewers.Layout": "佈局",
"app.lock-viewers.ariaTitle": "鎖定與會者模式",
"app.recording.startTitle": "開始錄製",

View File

@ -322,6 +322,7 @@ lockSettingsDisableMic=false
lockSettingsDisablePrivateChat=false
lockSettingsDisablePublicChat=false
lockSettingsDisableNote=false
lockSettingsHideUserList=false
lockSettingsLockedLayout=false
lockSettingsLockOnJoin=true
lockSettingsLockOnJoinConfigurable=false

View File

@ -147,6 +147,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<property name="lockSettingsDisablePrivateChat" value="${lockSettingsDisablePrivateChat}"/>
<property name="lockSettingsDisablePublicChat" value="${lockSettingsDisablePublicChat}"/>
<property name="lockSettingsDisableNote" value="${lockSettingsDisableNote}"/>
<property name="lockSettingsHideUserList" value="${lockSettingsHideUserList}"/>
<property name="lockSettingsLockedLayout" value="${lockSettingsLockedLayout}"/>
<property name="lockSettingsLockOnJoin" value="${lockSettingsLockOnJoin}"/>
<property name="lockSettingsLockOnJoinConfigurable" value="${lockSettingsLockOnJoinConfigurable}"/>

View File

@ -19,7 +19,7 @@ GEM
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mini_portile2 (2.4.0)
nokogiri (1.10.3)
nokogiri (1.10.4)
mini_portile2 (~> 2.4.0)
open4 (1.3.4)
parallel (1.17.0)