Merge branch 'master' of github.com:bigbluebutton/bigbluebutton into iframe-outside-commands

This commit is contained in:
Anton Georgiev 2018-11-12 17:01:06 +00:00
commit 98ac77dc03
47 changed files with 75 additions and 344 deletions

View File

@ -12,7 +12,7 @@ env:
- BBB_SERVER_URL=http://localhost/bigbluebutton/api
script:
- bash ./build_script.sh
- travis_wait bash ./build_script.sh
after_script:
- docker stop $docker

View File

@ -1,92 +0,0 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import deepMerge from '/imports/utils/deepMerge';
export default class Acl {
constructor(config, Users) {
this.Users = Users;
this.config = config;
}
can(permission, credentials) {
check(permission, String);
const permissions = this.getPermissions(credentials);
return this.checkToken(credentials) && this.fetchPermission(permission, permissions);
}
checkToken(credentials) {
// skip token check in client `can` calls since we dont have the authToken in the collection
if (!Meteor.isServer) return true;
const { meetingId, requesterUserId: userId, requesterToken: authToken } = credentials;
const User = this.Users.findOne({
meetingId,
userId,
authToken,
validated: true,
connectionStatus: 'online',
// TODO: We cant check for approved until we move subscription login out of <Base /> // TODO 4767
// approved: true,
});
return !!User; // if he found a user means the meeting/user/token is valid
}
fetchPermission(permission, permissions) {
if (!permission) return false;
if (Match.test(permissions, String)) {
return permissions.indexOf(permission) > -1;
} else if (Match.test(permissions, Array)) {
return permissions.some(internalAcl => (this.fetchPermission(permission, internalAcl)));
} else if (Match.test(permissions, Object)) {
if (permission.indexOf('.') > -1) {
return this.fetchPermission(
permission.substring(permission.indexOf('.') + 1),
permissions[permission.substring(0, permission.indexOf('.'))],
);
}
return permissions[permission];
}
return false;
}
getPermissions(credentials) {
if (!credentials) {
return false;
}
const { meetingId, requesterUserId: userId } = credentials;
const user = this.Users.findOne({
meetingId,
userId,
});
const containRole = Acl.containsRole(user);
if (containRole) {
const { roles } = user;
let permissions = {};
roles.forEach((role) => {
// There is a big issue here, if we just send the content from the this.config
// inside the deepMerge, we change both permissions and the config.
// Couldn't find a better way to prevent the changing.
// The problems occurs in the `sources.shift()`.
permissions = deepMerge(permissions, JSON.parse(JSON.stringify(this.config[role])));
});
return permissions;
}
return false;
}
static containsRole(user) {
return Match.test(user, Object) &&
Match.test(user.roles, Array);
}
}

View File

@ -1,4 +1,3 @@
import Acl from '/imports/startup/acl';
import { getMultiUserStatus } from '/imports/api/common/server/helpers';
import RedisPubSub from '/imports/startup/server/redis';
import { Meteor } from 'meteor/meteor';

View File

@ -1,4 +1,3 @@
import Acl from '/imports/startup/acl';
import { getMultiUserStatus } from '/imports/api/common/server/helpers';
import RedisPubSub from '/imports/startup/server/redis';
import { Meteor } from 'meteor/meteor';

View File

@ -1,4 +1,3 @@
import Acl from '/imports/startup/acl';
import { getMultiUserStatus } from '/imports/api/common/server/helpers';
import RedisPubSub from '/imports/startup/server/redis';
import { Meteor } from 'meteor/meteor';

View File

@ -2,7 +2,6 @@ import Annotations from '/imports/api/annotations';
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
function annotations(credentials) {
const { meetingId, requesterUserId, requesterToken } = credentials;
@ -18,7 +17,7 @@ function annotations(credentials) {
function publish(...args) {
const boundAnnotations = annotations.bind(this);
return mapToAcl('subscriptions.annotations', boundAnnotations)(args);
return boundAnnotations(...args);
}
Meteor.publish('annotations', publish);

View File

@ -1,11 +1,10 @@
import { Meteor } from 'meteor/meteor';
import createBreakoutRoom from '/imports/api/breakouts/server/methods/createBreakout';
import mapToAcl from '/imports/startup/mapToAcl';
import requestJoinURL from './methods/requestJoinURL';
import endAllBreakouts from './methods/endAllBreakouts';
Meteor.methods(mapToAcl(['methods.requestJoinURL', 'methods.createBreakoutRoom', 'methods.endAllBreakouts'], {
Meteor.methods({
requestJoinURL,
createBreakoutRoom,
endAllBreakouts,
}));
});

View File

@ -1,5 +1,4 @@
import { Meteor } from 'meteor/meteor';
import mapToAcl from '/imports/startup/mapToAcl';
import Breakouts from '/imports/api/breakouts';
import Logger from '/imports/startup/server/logger';
@ -32,7 +31,7 @@ function breakouts(credentials) {
function publish(...args) {
const boundBreakouts = breakouts.bind(this);
return mapToAcl('subscriptions.breakouts', boundBreakouts)(args);
return boundBreakouts(...args);
}
Meteor.publish('breakouts', publish);

View File

@ -2,7 +2,6 @@ import Captions from '/imports/api/captions';
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
function captions(credentials) {
const { meetingId, requesterUserId, requesterToken } = credentials;
@ -18,7 +17,7 @@ function captions(credentials) {
function publish(...args) {
const boundCaptions = captions.bind(this);
return mapToAcl('subscriptions.captions', boundCaptions)(args);
return boundCaptions(...args);
}
Meteor.publish('captions', publish);

View File

@ -1,6 +1,5 @@
import { getMultiUserStatus } from '/imports/api/common/server/helpers';
import RedisPubSub from '/imports/startup/server/redis';
import Acl from '/imports/startup/acl';
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';

View File

@ -1,9 +1,8 @@
import { Meteor } from 'meteor/meteor';
import mapToAcl from '/imports/startup/mapToAcl';
import sendGroupChatMsg from './methods/sendGroupChatMsg';
import clearPublicChatHistory from './methods/clearPublicChatHistory';
Meteor.methods(mapToAcl(['methods.sendGroupChatMsg', 'methods.clearPublicChatHistory'], {
Meteor.methods({
sendGroupChatMsg,
clearPublicChatHistory,
}));
});

View File

@ -3,7 +3,6 @@ import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
function groupChatMsg(credentials, chatsIds) {
const { meetingId, requesterUserId, requesterToken } = credentials;
@ -27,7 +26,7 @@ function groupChatMsg(credentials, chatsIds) {
function publish(...args) {
const boundGroupChat = groupChatMsg.bind(this);
return mapToAcl('subscriptions.group-chat-msg', boundGroupChat)(args);
return boundGroupChat(...args);
}
Meteor.publish('group-chat-msg', publish);

View File

@ -1,9 +1,8 @@
import { Meteor } from 'meteor/meteor';
import mapToAcl from '/imports/startup/mapToAcl';
import createGroupChat from './methods/createGroupChat';
import destroyGroupChat from './methods/destroyGroupChat';
Meteor.methods(mapToAcl(['methods.createGroupChat', 'methods.destroyGroupChat'], {
Meteor.methods({
createGroupChat,
destroyGroupChat,
}));
});

View File

@ -3,7 +3,6 @@ import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
function groupChat(credentials) {
const { meetingId, requesterUserId, requesterToken } = credentials;
@ -28,7 +27,7 @@ function groupChat(credentials) {
function publish(...args) {
const boundGroupChat = groupChat.bind(this);
return mapToAcl('subscriptions.group-chat', boundGroupChat)(args);
return boundGroupChat(...args);
}
Meteor.publish('group-chat', publish);

View File

@ -1,11 +1,10 @@
import { Meteor } from 'meteor/meteor';
import mapToAcl from '/imports/startup/mapToAcl';
import endMeeting from './methods/endMeeting';
import toggleRecording from './methods/toggleRecording';
import transferUser from './methods/transferUser';
Meteor.methods(mapToAcl(['methods.endMeeting', 'methods.toggleRecording', 'methods.transferUser'], {
Meteor.methods({
endMeeting,
toggleRecording,
transferUser,
}));
});

View File

@ -2,7 +2,6 @@ import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Meetings from '/imports/api/meetings';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
function meetings(credentials) {
const { meetingId, requesterUserId, requesterToken } = credentials;
@ -32,7 +31,7 @@ function meetings(credentials) {
function publish(...args) {
const boundMeetings = meetings.bind(this);
return mapToAcl('subscriptions.meetings', boundMeetings)(args);
return boundMeetings(...args);
}
Meteor.publish('meetings', publish);

View File

@ -1,13 +1,12 @@
import { Meteor } from 'meteor/meteor';
import mapToAcl from '/imports/startup/mapToAcl';
import publishVote from './methods/publishVote';
import publishPoll from './methods/publishPoll';
import startPoll from './methods/startPoll';
import stopPoll from './methods/stopPoll';
Meteor.methods(mapToAcl(['methods.publishVote', 'methods.startPoll', 'methods.stopPoll', 'methods.publishPoll'], {
Meteor.methods({
publishVote,
publishPoll,
startPoll,
stopPoll,
}));
});

View File

@ -2,7 +2,6 @@ import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import Polls from '/imports/api/polls';
import mapToAcl from '/imports/startup/mapToAcl';
Meteor.publish('current-poll', (meetingId) => {
check(meetingId, String);
@ -36,7 +35,7 @@ function polls(credentials) {
function publish(...args) {
const boundPolls = polls.bind(this);
return mapToAcl('subscriptions.polls', boundPolls)(args);
return boundPolls(...args);
}
Meteor.publish('polls', publish);

View File

@ -2,7 +2,6 @@ import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import PresentationPods from '/imports/api/presentation-pods';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
function presentationPods(credentials) {
const { meetingId, requesterUserId, requesterToken } = credentials;
@ -18,7 +17,7 @@ function presentationPods(credentials) {
function publish(...args) {
const boundPresentationPods = presentationPods.bind(this);
return mapToAcl('subscriptions.presentation-pods', boundPresentationPods)(args);
return boundPresentationPods(...args);
}
Meteor.publish('presentation-pods', publish);

View File

@ -1,9 +1,6 @@
import { Meteor } from 'meteor/meteor';
import mapToAcl from '/imports/startup/mapToAcl';
import requestPresentationUploadToken from './methods/requestPresentationUploadToken';
Meteor.methods(mapToAcl([
'methods.requestPresentationUploadToken',
], {
Meteor.methods({
requestPresentationUploadToken,
}));
});

View File

@ -1,12 +1,8 @@
import { Meteor } from 'meteor/meteor';
import mapToAcl from '/imports/startup/mapToAcl';
import removePresentation from './methods/removePresentation';
import setPresentation from './methods/setPresentation';
Meteor.methods(mapToAcl([
'methods.removePresentation',
'methods.setPresentation',
], {
Meteor.methods({
removePresentation,
setPresentation,
}));
});

View File

@ -2,7 +2,6 @@ import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Presentations from '/imports/api/presentations';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
function presentations(credentials) {
const { meetingId, requesterUserId, requesterToken } = credentials;
@ -18,7 +17,7 @@ function presentations(credentials) {
function publish(...args) {
const boundPresentations = presentations.bind(this);
return mapToAcl('subscriptions.presentations', boundPresentations)(args);
return boundPresentations(...args);
}
Meteor.publish('presentations', publish);

View File

@ -2,7 +2,6 @@ import Screenshare from '/imports/api/screenshare';
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
function screenshare(credentials) {
const { meetingId, requesterUserId } = credentials;
@ -17,7 +16,7 @@ function screenshare(credentials) {
function publish(...args) {
const boundScreenshare = screenshare.bind(this);
return mapToAcl('subscriptions.screenshare', boundScreenshare)(args);
return boundScreenshare(...args);
}
Meteor.publish('screenshare', publish);

View File

@ -1,9 +1,8 @@
import { Meteor } from 'meteor/meteor';
import mapToAcl from '/imports/startup/mapToAcl';
import switchSlide from './methods/switchSlide';
import zoomSlide from './methods/zoomSlide';
Meteor.methods(mapToAcl(['methods.switchSlide', 'methods.zoomSlide'], {
Meteor.methods({
switchSlide,
zoomSlide,
}));
});

View File

@ -2,7 +2,6 @@ import Slides from '/imports/api/slides';
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
function slides(credentials) {
const { meetingId, requesterUserId, requesterToken } = credentials;
@ -18,7 +17,7 @@ function slides(credentials) {
function publish(...args) {
const boundSlides = slides.bind(this);
return mapToAcl('subscriptions.slides', boundSlides)(args);
return boundSlides(...args);
}
Meteor.publish('slides', publish);

View File

@ -2,7 +2,6 @@ import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import UserSettings from '/imports/api/users-settings';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
function userSettings(credentials) {
const { meetingId, requesterUserId } = credentials;
@ -17,7 +16,7 @@ function userSettings(credentials) {
function publish(...args) {
const boundUserSettings = userSettings.bind(this);
return mapToAcl('subscriptions.users-settings', boundUserSettings)(args);
return boundUserSettings(...args);
}
Meteor.publish('users-settings', publish);

View File

@ -1,17 +1,14 @@
import { Meteor } from 'meteor/meteor';
import mapToAcl from '/imports/startup/mapToAcl';
import validateAuthToken from './methods/validateAuthToken';
import setEmojiStatus from './methods/setEmojiStatus';
import assignPresenter from './methods/assignPresenter';
import changeRole from './methods/changeRole';
import removeUser from './methods/removeUser';
Meteor.methods(mapToAcl(['methods.setEmojiStatus', 'methods.assignPresenter', 'methods.changeRole',
'methods.removeUser'], {
Meteor.methods({
setEmojiStatus,
assignPresenter,
changeRole,
removeUser,
}));
Meteor.methods({ validateAuthToken });
validateAuthToken,
});

View File

@ -3,7 +3,6 @@ import Users from '/imports/api/users';
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
import userLeaving from './methods/userLeaving';
@ -68,7 +67,7 @@ function users(credentials) {
function publish(...args) {
const boundUsers = users.bind(this);
return mapToAcl('subscriptions.users', boundUsers)(args);
return boundUsers(...args);
}
Meteor.publish('users', publish);

View File

@ -1,13 +1,11 @@
import { Meteor } from 'meteor/meteor';
import mapToAcl from '/imports/startup/mapToAcl';
import listenOnlyToggle from './methods/listenOnlyToggle';
import muteToggle from './methods/muteToggle';
import ejectUserFromVoice from './methods/ejectUserFromVoice';
Meteor.methods(mapToAcl(['methods.listenOnlyToggle', 'methods.toggleSelfVoice',
'methods.toggleVoice', 'methods.ejectUserFromVoice'], {
Meteor.methods({
listenOnlyToggle,
toggleSelfVoice: (credentials) => { muteToggle(credentials, credentials.requesterUserId); },
toggleVoice: muteToggle,
ejectUserFromVoice,
}));
});

View File

@ -2,7 +2,6 @@ import VoiceUsers from '/imports/api/voice-users';
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
function voiceUser(credentials) {
const { meetingId, requesterUserId } = credentials;
@ -17,7 +16,7 @@ function voiceUser(credentials) {
function publish(...args) {
const boundVoiceUser = voiceUser.bind(this);
return mapToAcl('subscriptions.voiceUser', boundVoiceUser)(args);
return boundVoiceUser(...args);
}
Meteor.publish('voiceUsers', publish);

View File

@ -1,7 +1,6 @@
import { Meteor } from 'meteor/meteor';
import mapToAcl from '/imports/startup/mapToAcl';
import changeWhiteboardAccess from './methods/changeWhiteboardAccess';
Meteor.methods(mapToAcl(['methods.modifyWhiteboardAccess'], {
Meteor.methods({
changeWhiteboardAccess,
}));
});

View File

@ -2,7 +2,6 @@ import WhiteboardMultiUser from '/imports/api/whiteboard-multi-user/';
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
function whiteboardMultiUser(credentials) {
const { meetingId, requesterUserId, requesterToken } = credentials;
@ -16,7 +15,7 @@ function whiteboardMultiUser(credentials) {
function publish(...args) {
const boundMultiUser = whiteboardMultiUser.bind(this);
return mapToAcl('subscriptions.whiteboard-multi-user', boundMultiUser)(args);
return boundMultiUser(...args);
}
Meteor.publish('whiteboard-multi-user', publish);

View File

@ -1,12 +0,0 @@
import { Meteor } from 'meteor/meteor';
import Users from '/imports/api/users';
import Acl from '/imports/api/acl/Acl';
const AclSingleton = new Acl();
Meteor.startup(() => {
AclSingleton.config = Meteor.settings.public.acl;
AclSingleton.Users = Users;
});
export default AclSingleton;

View File

@ -1,45 +0,0 @@
import Acl from '/imports/startup/acl';
import { Meteor } from 'meteor/meteor';
const injectAclActionCheck = (name, handler) => (
(...args) => {
const credentials = args[0];
if (!Acl.can(name, credentials)) {
throw new Meteor.Error(
'acl-not-allowed',
`The user can't perform the action "${name}".`,
);
}
return handler(...args);
}
);
const injectAclSubscribeCheck = (name, handler) => (
(...args) => {
const credentials = args[args.length - 1];
if (!Acl.can(name, ...credentials)) {
throw new Meteor.Error(
'acl-not-allowed',
`The user can't perform the subscription "${name}".`,
);
}
return handler(...credentials);
}
);
const mapToAcl = (name, handler) => {
// The Meteor#methods require an object, while the Meteor#subscribe an function.
if (handler instanceof Function) {
return injectAclSubscribeCheck(name, handler);
}
return Object.keys(handler).reduce((previous, current, index) => {
const newPrevious = previous;
newPrevious[current] = injectAclActionCheck(name[index], handler[current]);
return newPrevious;
}, {});
};
export default mapToAcl;

View File

@ -149,7 +149,7 @@ class ActionsDropdown extends Component {
onClick={toggleRecording}
/>
: null),
(isUserPresenter && !meetingIsBreakout && !hasBreakoutRoom ?
(isUserModerator && !meetingIsBreakout && !hasBreakoutRoom ?
<DropdownListItem
icon="rooms"
label={intl.formatMessage(intlMessages.createBreakoutRoom)}

View File

@ -36,9 +36,8 @@ const intlMessages = defineMessages({
description: 'room intl to name the breakout meetings',
},
});
const BREAKOUT_CONFIG = Meteor.settings.public.breakout;
const MIN_BREAKOUT_ROOMS = BREAKOUT_CONFIG.rooms.min;
const MAX_BREAKOUT_ROOMS = BREAKOUT_CONFIG.rooms.max;
const MIN_BREAKOUT_ROOMS = 2;
const MAX_BREAKOUT_ROOMS = 8;
class BreakoutRoom extends Component {
constructor(props) {
@ -60,7 +59,7 @@ class BreakoutRoom extends Component {
const {
createBreakoutRoom,
meetingName,
intl
intl,
} = this.props;
const { numberOfRooms, durationTime } = this.state;
@ -113,7 +112,12 @@ class BreakoutRoom extends Component {
<div className={styles.breakoutSettings}>
<label>
<p className={styles.labelText}>{intl.formatMessage(intlMessages.numberOfRooms)}</p>
<select name="numberOfRooms" className={styles.inputRooms} value={this.state.numberOfRooms} onChange={this.changeNumberOfRooms}>
<select
name="numberOfRooms"
className={styles.inputRooms}
value={this.state.numberOfRooms}
onChange={this.changeNumberOfRooms}
>
{
_.range(MIN_BREAKOUT_ROOMS, MAX_BREAKOUT_ROOMS + 1).map(item => (<option key={_.uniqueId('value-')}>{item}</option>))
}
@ -122,7 +126,13 @@ class BreakoutRoom extends Component {
<label >
<p className={styles.labelText}>{intl.formatMessage(intlMessages.duration)}</p>
<div className={styles.durationArea}>
<input type="number" className={styles.duration} min={MIN_BREAKOUT_ROOMS} value={this.state.durationTime} onChange={this.changeDurationTime} />
<input
type="number"
className={styles.duration}
min={MIN_BREAKOUT_ROOMS}
value={this.state.durationTime}
onChange={this.changeDurationTime}
/>
<span>
<HoldButton
key="decrease-breakout-time"

View File

@ -6,8 +6,8 @@ import Modal from 'react-modal';
import cx from 'classnames';
import Resizable from 're-resizable';
import browser from 'browser-detect';
import BreakoutRoomContainer from '/imports/ui/components/breakout-room/container';
import PollingContainer from '/imports/ui/components/polling/container';
import BreakoutRoomContainer from '/imports/ui/components/Breakout-room/container';
import ToastContainer from '../toast/container';
import ModalContainer from '../modal/container';
import NotificationsBarContainer from '../notifications-bar/container';

View File

@ -92,8 +92,8 @@ class BreakoutRoom extends Component {
}
transferUserToBreakoutRoom(breakoutId) {
const { transferToBreakout, meetingId } = this.props;
transferToBreakout(meetingId, breakoutId);
const { transferToBreakout } = this.props;
transferToBreakout(breakoutId);
this.setState({ joinedAudioOnly: true, breakoutId });
}
returnBackToMeeeting(breakoutId) {
@ -105,7 +105,7 @@ class BreakoutRoom extends Component {
renderUserActions(breakoutId) {
const {
isMicrophoneUser,
isPresenter,
isModerator,
intl,
} = this.props;
@ -117,7 +117,7 @@ class BreakoutRoom extends Component {
waiting,
} = this.state;
const presenterJoinedAudio = isMicrophoneUser && isPresenter;
const moderatorJoinedAudio = isMicrophoneUser && isModerator;
const disable = waiting && requestedBreakoutId !== breakoutId;
const audioAction = joinedAudioOnly ?
() => this.returnBackToMeeeting(breakoutId) :
@ -133,13 +133,13 @@ class BreakoutRoom extends Component {
className={styles.joinButton}
/>
{
presenterJoinedAudio ?
moderatorJoinedAudio ?
[
('|'),
(
<Button
label={
presenterJoinedAudio &&
moderatorJoinedAudio &&
stateBreakoutId === breakoutId &&
joinedAudioOnly
?

View File

@ -21,7 +21,6 @@ export default withTracker((props) => {
closeBreakoutPanel,
} = Service;
const breakoutRooms = findBreakouts();
const isMicrophoneUser = AudioService.isConnected() && !AudioService.isListenOnly();
return {
@ -33,7 +32,7 @@ export default withTracker((props) => {
transferUserToMeeting,
transferToBreakout,
isMicrophoneUser,
meetingId,
meetingId: meetingId(),
isPresenter: isPresenter(),
isModerator: isModerator(),
closeBreakoutPanel,

View File

@ -38,7 +38,7 @@ const requestJoinURL = (breakoutId) => {
const transferUserToMeeting = (fromMeetingId, toMeetingId) => makeCall('transferUser', fromMeetingId, toMeetingId);
const transferToBreakout = (fromMeetingId, breakoutId) => {
const transferToBreakout = (breakoutId) => {
const breakoutRooms = findBreakouts();
const breakoutRoom = breakoutRooms.filter(breakout => breakout.breakoutId === breakoutId).shift();
const breakoutMeeting = Meetings.findOne({
@ -48,7 +48,7 @@ const transferToBreakout = (fromMeetingId, breakoutId) => {
{ 'meetingProp.isBreakout': true },
],
});
transferUserToMeeting(fromMeetingId, breakoutMeeting.meetingId);
transferUserToMeeting(Auth.meetingID, breakoutMeeting.meetingId);
};
const isPresenter = () => {
@ -63,6 +63,7 @@ const isModerator = () => {
return mappedUser.isModerator;
};
const closeBreakoutPanel = () => Session.set('breakoutRoomIsOpen', false);
export default {
@ -72,7 +73,7 @@ export default {
breakoutRoomUser,
transferUserToMeeting,
transferToBreakout,
meetingId: Auth.meetingID,
meetingId: () => Auth.meetingID,
isPresenter,
closeBreakoutPanel,
isModerator,

View File

@ -9,7 +9,6 @@ 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 Auth from '/imports/ui/services/auth';
import Acl from '/imports/startup/acl';
import Button from '/imports/ui/components/button/component';
import ChatService from './../service';
@ -80,6 +79,8 @@ class ChatDropdown extends Component {
const saveIcon = 'save_notes';
const copyIcon = 'copy';
const user = ChatService.getUser(Auth.userID);
return _.compact([
<DropdownListItem
icon={saveIcon}
@ -104,7 +105,7 @@ class ChatDropdown extends Component {
label={intl.formatMessage(intlMessages.copy)}
key={this.actionsKey[1]}
/>,
Acl.can('methods.clearPublicChatHistory', Auth.credentials) ? (
user.isModerator ? (
<DropdownListItem
icon={clearIcon}
label={intl.formatMessage(intlMessages.clear)}

View File

@ -23,6 +23,10 @@ const intlMessage = defineMessages({
id: 'app.error.meeting.ended',
description: 'user logged conference',
},
'acl-not-allowed': {
id: 'app.error.removed',
description: 'Message to display when user is removed from the conference',
},
messageEnded: {
id: 'app.meeting.endedMessage',
description: 'message saying to go back to home screen',

View File

@ -1,31 +0,0 @@
import { Match } from 'meteor/check';
export default function deepMerge(merge, ...sources) {
if (!sources.length) return merge;
const source = sources.shift();
let merged = merge;
if (Match.test(merged, Array)) {
if (Match.test(source, Array)) {
merged.push(...source);
} else {
merged.push(source);
}
} else if (Match.test(merged, Object)) {
if (Match.test(source, Object)) {
Object.keys(source).forEach((key) => {
if (!merged[key]) {
merged[key] = source[key];
} else {
deepMerge(merged[key], source[key]);
}
});
}
} else {
merged = source;
}
return deepMerge(merged, ...sources);
}

View File

@ -102,72 +102,6 @@ public:
allowOutsideCommands:
toggleRecording: false
toggleSelfVoice: false
acl:
viewer:
subscriptions:
- users
- cursor
- screenshare
- meetings
- polls
- chat
- presentations
- annotations
- slides
- captions
- breakouts
- voiceUsers
- whiteboard-multi-user
- presentation-pods
- group-chat
- group-chat-msg
- users-settings
methods:
- listenOnlyToggle
- userLogout
- setEmojiStatus
- toggleSelfVoice
- publishVote
- sendChat
- createGroupChat
- destroyGroupChat
- sendGroupChatMsg
- requestJoinURL
moderator:
methods:
- assignPresenter
- removeUser
- muteUser
- unmuteUser
- endMeeting
- toggleVoice
- clearPublicChatHistory
- changeRole
- ejectUserFromVoice
- toggleRecording
- endAllBreakouts
presenter:
methods:
- assignPresenter
- switchSlide
- modifyWhiteboardAccess
- undoAnnotation
- clearWhiteboard
- moveCursor
- sendAnnotation
- removePresentation
- setPresentation
- zoomSlide
- requestPresentationUploadToken
- transferUser
- createBreakoutRoom
- startPoll
- stopPoll
- publishPoll
breakout:
rooms:
min: 2
max: 8
poll:
max_custom: 5
chat: