Add guest lobby messages
Moderators are able to send a message to the meeting's guest lobby. This new event reaches bbb-web and is sent to the guest user with her/his status response while polling. All guest users that are waiting for acceptance will be able to read this message. enableGuestLobbyMessage is disabled by default.
This commit is contained in:
parent
4ba2082fb6
commit
0365018e92
@ -7,6 +7,7 @@ trait GuestsApp extends GetGuestsWaitingApprovalReqMsgHdlr
|
||||
with GuestsWaitingApprovedMsgHdlr
|
||||
with GuestWaitingLeftMsgHdlr
|
||||
with SetGuestPolicyMsgHdlr
|
||||
with SetGuestLobbyMessageMsgHdlr
|
||||
with GetGuestPolicyReqMsgHdlr {
|
||||
|
||||
this: MeetingActor =>
|
||||
|
@ -24,6 +24,9 @@ object GuestsWaiting {
|
||||
guests.setGuestPolicy(policy)
|
||||
}
|
||||
|
||||
def setGuestLobbyMessage(guests: GuestsWaiting, message: String): Unit = {
|
||||
guests.setGuestLobbyMessage(message)
|
||||
}
|
||||
}
|
||||
|
||||
class GuestsWaiting {
|
||||
@ -31,6 +34,8 @@ class GuestsWaiting {
|
||||
|
||||
private var guestPolicy = GuestPolicy(GuestPolicyType.ALWAYS_ACCEPT, SystemUser.ID)
|
||||
|
||||
private var guestLobbyMessage = ""
|
||||
|
||||
private def toVector: Vector[GuestWaiting] = guests.values.toVector
|
||||
|
||||
private def save(user: GuestWaiting): GuestWaiting = {
|
||||
@ -49,6 +54,8 @@ class GuestsWaiting {
|
||||
|
||||
def getGuestPolicy(): GuestPolicy = guestPolicy
|
||||
def setGuestPolicy(policy: GuestPolicy) = guestPolicy = policy
|
||||
|
||||
def setGuestLobbyMessage(message: String) = guestLobbyMessage = message
|
||||
}
|
||||
|
||||
case class GuestWaiting(intId: String, name: String, role: String, guest: Boolean, avatar: String, authenticated: Boolean, registeredOn: Long)
|
||||
|
@ -95,6 +95,8 @@ class ReceivedJsonMsgHandlerActor(
|
||||
routeGenericMsg[SetGuestPolicyCmdMsg](envelope, jsonNode)
|
||||
case GetGuestPolicyReqMsg.NAME =>
|
||||
routeGenericMsg[GetGuestPolicyReqMsg](envelope, jsonNode)
|
||||
case SetGuestLobbyMessageCmdMsg.NAME =>
|
||||
routeGenericMsg[SetGuestLobbyMessageCmdMsg](envelope, jsonNode)
|
||||
|
||||
// Users
|
||||
case GetUsersMeetingReqMsg.NAME =>
|
||||
|
@ -443,6 +443,7 @@ class MeetingActor(
|
||||
// Guests
|
||||
case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m)
|
||||
case m: SetGuestPolicyCmdMsg => handleSetGuestPolicyMsg(m)
|
||||
case m: SetGuestLobbyMessageCmdMsg => handleSetGuestLobbyMessageMsg(m)
|
||||
case m: GuestsWaitingApprovedMsg => handleGuestsWaitingApprovedMsg(m)
|
||||
case m: GuestWaitingLeftMsg => handleGuestWaitingLeftMsg(m)
|
||||
case m: GetGuestPolicyReqMsg => handleGetGuestPolicyReqMsg(m)
|
||||
|
@ -139,6 +139,8 @@ class AnalyticsActor extends Actor with ActorLogging {
|
||||
case m: GuestsWaitingForApprovalEvtMsg => logMessage(msg)
|
||||
case m: SetGuestPolicyCmdMsg => logMessage(msg)
|
||||
case m: GuestPolicyChangedEvtMsg => logMessage(msg)
|
||||
case m: SetGuestLobbyMessageCmdMsg => logMessage(msg)
|
||||
case m: GuestLobbyMessageChangedEvtMsg => logMessage(msg)
|
||||
|
||||
// System
|
||||
case m: ClientToServerLatencyTracerMsg => traceMessage(msg)
|
||||
|
@ -0,0 +1,31 @@
|
||||
package org.bigbluebutton.core2.message.handlers.guests
|
||||
|
||||
import org.bigbluebutton.common2.msgs.SetGuestLobbyMessageCmdMsg
|
||||
import org.bigbluebutton.core.models.{ GuestsWaiting }
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core.running.MeetingActor
|
||||
|
||||
trait SetGuestLobbyMessageMsgHdlr extends RightsManagementTrait {
|
||||
this: MeetingActor =>
|
||||
|
||||
val liveMeeting: LiveMeeting
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
def handleSetGuestLobbyMessageMsg(msg: SetGuestLobbyMessageCmdMsg): Unit = {
|
||||
if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
val reason = "No permission to set guest lobby message in meeting."
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
||||
} else {
|
||||
GuestsWaiting.setGuestLobbyMessage(liveMeeting.guestsWaiting, msg.body.message)
|
||||
val event = MsgBuilder.buildGuestLobbyMessageChangedEvtMsg(
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
msg.header.userId,
|
||||
msg.body.message
|
||||
)
|
||||
outGW.send(event)
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,17 @@ object MsgBuilder {
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
def buildGuestLobbyMessageChangedEvtMsg(meetingId: String, userId: String, message: String): BbbCommonEnvCoreMsg = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
|
||||
val envelope = BbbCoreEnvelope(GuestLobbyMessageChangedEvtMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(GuestLobbyMessageChangedEvtMsg.NAME, meetingId, userId)
|
||||
|
||||
val body = GuestLobbyMessageChangedEvtMsgBody(message)
|
||||
val event = GuestLobbyMessageChangedEvtMsg(header, body)
|
||||
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
def buildGuestApprovedEvtMsg(meetingId: String, userId: String, status: String, approvedBy: String): BbbCommonEnvCoreMsg = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
|
||||
val envelope = BbbCoreEnvelope(GuestApprovedEvtMsg.NAME, routing)
|
||||
|
@ -103,6 +103,26 @@ case class GuestPolicyChangedEvtMsg(
|
||||
) extends BbbCoreMsg
|
||||
case class GuestPolicyChangedEvtMsgBody(policy: String, setBy: String)
|
||||
|
||||
/**
|
||||
* Message from user to set the guest lobby message.
|
||||
*/
|
||||
object SetGuestLobbyMessageCmdMsg { val NAME = "SetGuestLobbyMessageCmdMsg" }
|
||||
case class SetGuestLobbyMessageCmdMsg(
|
||||
header: BbbClientMsgHeader,
|
||||
body: SetGuestLobbyMessageCmdMsgBody
|
||||
) extends StandardMsg
|
||||
case class SetGuestLobbyMessageCmdMsgBody(message: String)
|
||||
|
||||
/**
|
||||
* Message sent to all clients that guest lobby message has been changed.
|
||||
*/
|
||||
object GuestLobbyMessageChangedEvtMsg { val NAME = "GuestLobbyMessageChangedEvtMsg" }
|
||||
case class GuestLobbyMessageChangedEvtMsg(
|
||||
header: BbbClientMsgHeader,
|
||||
body: GuestLobbyMessageChangedEvtMsgBody
|
||||
) extends BbbCoreMsg
|
||||
case class GuestLobbyMessageChangedEvtMsgBody(message: String)
|
||||
|
||||
/**
|
||||
* Message from user to get the guest policy.
|
||||
*/
|
||||
|
@ -61,6 +61,7 @@ import org.bigbluebutton.api.messaging.messages.CreateBreakoutRoom;
|
||||
import org.bigbluebutton.api.messaging.messages.CreateMeeting;
|
||||
import org.bigbluebutton.api.messaging.messages.EndMeeting;
|
||||
import org.bigbluebutton.api.messaging.messages.GuestPolicyChanged;
|
||||
import org.bigbluebutton.api.messaging.messages.GuestLobbyMessageChanged;
|
||||
import org.bigbluebutton.api.messaging.messages.GuestStatusChangedEventMsg;
|
||||
import org.bigbluebutton.api.messaging.messages.GuestsStatus;
|
||||
import org.bigbluebutton.api.messaging.messages.IMessage;
|
||||
@ -1101,6 +1102,8 @@ public class MeetingService implements MessageListener {
|
||||
processGuestStatusChangedEventMsg((GuestStatusChangedEventMsg) message);
|
||||
} else if (message instanceof GuestPolicyChanged) {
|
||||
processGuestPolicyChanged((GuestPolicyChanged) message);
|
||||
} else if (message instanceof GuestLobbyMessageChanged) {
|
||||
processGuestLobbyMessageChanged((GuestLobbyMessageChanged) message);
|
||||
} else if (message instanceof RecordChapterBreak) {
|
||||
processRecordingChapterBreak((RecordChapterBreak) message);
|
||||
} else if (message instanceof AddPad) {
|
||||
@ -1125,6 +1128,13 @@ public class MeetingService implements MessageListener {
|
||||
}
|
||||
}
|
||||
|
||||
public void processGuestLobbyMessageChanged(GuestLobbyMessageChanged msg) {
|
||||
Meeting m = getMeeting(msg.meetingId);
|
||||
if (m != null) {
|
||||
m.setGuestLobbyMessage(msg.message);
|
||||
}
|
||||
}
|
||||
|
||||
public void processAddPad(AddPad msg) {
|
||||
Meeting m = getMeeting(msg.meetingId);
|
||||
if (m != null) {
|
||||
|
@ -68,6 +68,7 @@ public class Meeting {
|
||||
private String defaultAvatarURL;
|
||||
private String defaultConfigToken;
|
||||
private String guestPolicy = GuestPolicy.ASK_MODERATOR;
|
||||
private String guestLobbyMessage = "";
|
||||
private Boolean authenticatedGuest = false;
|
||||
private boolean userHasJoined = false;
|
||||
private Map<String, String> pads;
|
||||
@ -376,6 +377,14 @@ public class Meeting {
|
||||
return guestPolicy;
|
||||
}
|
||||
|
||||
public void setGuestLobbyMessage(String message) {
|
||||
guestLobbyMessage = message;
|
||||
}
|
||||
|
||||
public String getGuestLobbyMessage() {
|
||||
return guestLobbyMessage;
|
||||
}
|
||||
|
||||
public void setAuthenticatedGuest(Boolean authGuest) {
|
||||
authenticatedGuest = authGuest;
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package org.bigbluebutton.api.messaging.messages;
|
||||
|
||||
public class GuestLobbyMessageChanged implements IMessage {
|
||||
public final String meetingId;
|
||||
public final String message;
|
||||
|
||||
public GuestLobbyMessageChanged(String meetingId, String message) {
|
||||
this.meetingId = meetingId;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
@ -94,6 +94,8 @@ class ReceivedJsonMsgHdlrActor(val msgFromAkkaAppsEventBus: MsgFromAkkaAppsEvent
|
||||
route[GuestsWaitingApprovedEvtMsg](envelope, jsonNode)
|
||||
case GuestPolicyChangedEvtMsg.NAME =>
|
||||
route[GuestPolicyChangedEvtMsg](envelope, jsonNode)
|
||||
case GuestLobbyMessageChangedEvtMsg.NAME =>
|
||||
route[GuestLobbyMessageChangedEvtMsg](envelope, jsonNode)
|
||||
case AddPadEvtMsg.NAME =>
|
||||
route[AddPadEvtMsg](envelope, jsonNode)
|
||||
case AddCaptionsPadsEvtMsg.NAME =>
|
||||
|
@ -39,6 +39,7 @@ class OldMeetingMsgHdlrActor(val olgMsgGW: OldMessageReceivedGW)
|
||||
case m: PresentationUploadTokenSysPubMsg => handlePresentationUploadTokenSysPubMsg(m)
|
||||
case m: GuestsWaitingApprovedEvtMsg => handleGuestsWaitingApprovedEvtMsg(m)
|
||||
case m: GuestPolicyChangedEvtMsg => handleGuestPolicyChangedEvtMsg(m)
|
||||
case m: GuestLobbyMessageChangedEvtMsg => handleGuestLobbyMessageChangedEvtMsg(m)
|
||||
case m: AddCaptionsPadsEvtMsg => handleAddCaptionsPadsEvtMsg(m)
|
||||
case m: AddPadEvtMsg => handleAddPadEvtMsg(m)
|
||||
case m: RecordingChapterBreakSysMsg => handleRecordingChapterBreakSysMsg(m)
|
||||
@ -52,6 +53,10 @@ class OldMeetingMsgHdlrActor(val olgMsgGW: OldMessageReceivedGW)
|
||||
olgMsgGW.handle(new GuestPolicyChanged(msg.header.meetingId, msg.body.policy))
|
||||
}
|
||||
|
||||
def handleGuestLobbyMessageChangedEvtMsg(msg: GuestLobbyMessageChangedEvtMsg): Unit = {
|
||||
olgMsgGW.handle(new GuestLobbyMessageChanged(msg.header.meetingId, msg.body.message))
|
||||
}
|
||||
|
||||
def handleAddPadEvtMsg(msg: AddPadEvtMsg): Unit = {
|
||||
olgMsgGW.handle(new AddPad(msg.header.meetingId, msg.body.padId, msg.body.readOnlyId))
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import allowPendingUsers from '/imports/api/guest-users/server/methods/allowPendingUsers';
|
||||
import changeGuestPolicy from '/imports/api/guest-users/server/methods/changeGuestPolicy';
|
||||
import setGuestLobbyMessage from '/imports/api/guest-users/server/methods/setGuestLobbyMessage';
|
||||
|
||||
Meteor.methods({
|
||||
allowPendingUsers,
|
||||
changeGuestPolicy,
|
||||
setGuestLobbyMessage,
|
||||
});
|
||||
|
@ -0,0 +1,24 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { check } from 'meteor/check';
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { extractCredentials } from '/imports/api/common/server/helpers';
|
||||
|
||||
const REDIS_CONFIG = Meteor.settings.private.redis;
|
||||
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
|
||||
const EVENT_NAME = 'SetGuestLobbyMessageCmdMsg';
|
||||
|
||||
export default function setGuestLobbyMessage(message) {
|
||||
check(message, String);
|
||||
|
||||
const { meetingId, requesterUserId } = extractCredentials(this.userId);
|
||||
|
||||
check(meetingId, String);
|
||||
check(requesterUserId, String);
|
||||
|
||||
const payload = { message };
|
||||
|
||||
Logger.info(`User=${requesterUserId} set guest lobby message to ${message}`);
|
||||
|
||||
return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
|
||||
}
|
@ -4,6 +4,7 @@ import handleGetAllMeetings from './handlers/getAllMeetings';
|
||||
import handleMeetingEnd from './handlers/meetingEnd';
|
||||
import handleMeetingDestruction from './handlers/meetingDestruction';
|
||||
import handleMeetingLocksChange from './handlers/meetingLockChange';
|
||||
import handleGuestLobbyMessageChanged from './handlers/guestLobbyMessageChanged';
|
||||
import handleUserLockChange from './handlers/userLockChange';
|
||||
import handleRecordingStatusChange from './handlers/recordingStatusChange';
|
||||
import handleRecordingTimerChange from './handlers/recordingTimerChange';
|
||||
@ -21,5 +22,6 @@ RedisPubSub.on('RecordingStatusChangedEvtMsg', handleRecordingStatusChange);
|
||||
RedisPubSub.on('UpdateRecordingTimerEvtMsg', handleRecordingTimerChange);
|
||||
RedisPubSub.on('WebcamsOnlyForModeratorChangedEvtMsg', handleChangeWebcamOnlyModerator);
|
||||
RedisPubSub.on('GetLockSettingsRespMsg', handleMeetingLocksChange);
|
||||
RedisPubSub.on('GuestLobbyMessageChangedEvtMsg', handleGuestLobbyMessageChanged);
|
||||
RedisPubSub.on('MeetingTimeRemainingUpdateEvtMsg', handleTimeRemainingUpdate);
|
||||
RedisPubSub.on('SelectRandomViewerRespMsg', handleSelectRandomViewer);
|
||||
|
@ -0,0 +1,11 @@
|
||||
import setGuestLobbyMessage from '../modifiers/setGuestLobbyMessage';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
export default function handleGuestLobbyMessageChanged({ body }, meetingId) {
|
||||
const { message } = body;
|
||||
|
||||
check(meetingId, String);
|
||||
check(message, String);
|
||||
|
||||
return setGuestLobbyMessage(meetingId, message);
|
||||
}
|
@ -148,6 +148,7 @@ export default function addMeeting(meeting) {
|
||||
meetingId,
|
||||
meetingEnded,
|
||||
publishedPoll: false,
|
||||
guestLobbyMessage: '',
|
||||
randomlySelectedUser: '',
|
||||
}, flat(newMeeting, {
|
||||
safe: true,
|
||||
|
@ -0,0 +1,32 @@
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
export default function setGuestLobbyMessage(meetingId, guestLobbyMessage) {
|
||||
check(meetingId, String);
|
||||
check(guestLobbyMessage, String);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
guestLobbyMessage,
|
||||
},
|
||||
};
|
||||
|
||||
const cb = (err, numChanged) => {
|
||||
if (err) {
|
||||
return Logger.error(`Changing meeting guest lobby message: ${err}`);
|
||||
}
|
||||
|
||||
if (!numChanged) {
|
||||
return Logger.info(`Meeting's ${meetingId} guest lobby message=${guestLobbyMessage} wasn't updated`);
|
||||
}
|
||||
|
||||
return Logger.info(`Meeting's ${meetingId} guest lobby message=${guestLobbyMessage} updated`);
|
||||
};
|
||||
|
||||
return Meetings.update(selector, modifier, cb);
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import TextareaAutosize from 'react-autosize-textarea';
|
||||
import PropTypes from 'prop-types';
|
||||
import logger from '/imports/startup/client/logger';
|
||||
import Button from '/imports/ui/components/button/component';
|
||||
import { styles } from './styles.scss';
|
||||
|
||||
const propTypes = {
|
||||
placeholder: PropTypes.string,
|
||||
send: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
placeholder: '',
|
||||
send: () => logger.warn({ logCode: 'text_input_send_function' }, `Missing`),
|
||||
};
|
||||
|
||||
const messages = defineMessages({
|
||||
sendLabel: {
|
||||
id: 'app.textInput.sendLabel',
|
||||
description: 'Text input send button label',
|
||||
},
|
||||
});
|
||||
|
||||
class TextInput extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = { message: '' };
|
||||
}
|
||||
|
||||
handleOnChange(e) {
|
||||
const message = e.target.value;
|
||||
this.setState({ message });
|
||||
}
|
||||
|
||||
handleOnClick() {
|
||||
const { send } = this.props;
|
||||
const { message } = this.state;
|
||||
|
||||
send(message);
|
||||
this.setState({ message: '' });
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
intl,
|
||||
maxLength,
|
||||
placeholder,
|
||||
} = this.props;
|
||||
|
||||
const { message } = this.state;
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<TextareaAutosize
|
||||
className={styles.textarea}
|
||||
maxLength={maxLength}
|
||||
onChange={(e) => this.handleOnChange(e)}
|
||||
placeholder={placeholder}
|
||||
value={message}
|
||||
/>
|
||||
<Button
|
||||
circle
|
||||
className={styles.button}
|
||||
color="primary"
|
||||
hideLabel
|
||||
icon="send"
|
||||
label={intl.formatMessage(messages.sendLabel)}
|
||||
onClick={() => this.handleOnClick()}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TextInput.propTypes = propTypes;
|
||||
TextInput.defaultProps = defaultProps;
|
||||
|
||||
export default injectIntl(TextInput);
|
@ -0,0 +1,53 @@
|
||||
@import "/imports/ui/stylesheets/mixins/focus";
|
||||
@import "/imports/ui/stylesheets/mixins/_indicators";
|
||||
@import "/imports/ui/stylesheets/variables/_all";
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.textarea {
|
||||
@include inputFocus(var(--color-blue-light));
|
||||
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
background-clip: padding-box;
|
||||
margin: 0;
|
||||
color: var(--color-text);
|
||||
-webkit-appearance: none;
|
||||
padding: calc(var(--sm-padding-y) * 2.5) calc(var(--sm-padding-x) * 1.25);
|
||||
resize: none;
|
||||
transition: none;
|
||||
border-radius: var(--border-radius);
|
||||
font-size: var(--font-size-base);
|
||||
min-height: 2.5rem;
|
||||
max-height: 10rem;
|
||||
border: 1px solid var(--color-gray-lighter);
|
||||
box-shadow: 0 0 0 1px var(--color-gray-lighter);
|
||||
|
||||
&:hover {
|
||||
@include highContrastOutline();
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
@include highContrastOutline();
|
||||
outline-style: solid;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
margin:0 0 0 var(--sm-padding-x);
|
||||
align-self: center;
|
||||
font-size: 0.9rem;
|
||||
|
||||
[dir="rtl"] & {
|
||||
margin: 0 var(--sm-padding-x) 0 0;
|
||||
-webkit-transform: scale(-1, 1);
|
||||
-moz-transform: scale(-1, 1);
|
||||
-ms-transform: scale(-1, 1);
|
||||
-o-transform: scale(-1, 1);
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import { Session } from 'meteor/session';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import injectWbResizeEvent from '/imports/ui/components/presentation/resize-wrapper/component';
|
||||
import UserAvatar from '/imports/ui/components/user-avatar/component';
|
||||
import TextInput from '/imports/ui/components/text-input/component';
|
||||
import Button from '/imports/ui/components/button/component';
|
||||
import { styles } from './styles';
|
||||
|
||||
@ -48,6 +49,14 @@ const intlMessages = defineMessages({
|
||||
id: 'app.userList.guest.rememberChoice',
|
||||
description: 'Remember label for checkbox',
|
||||
},
|
||||
emptyMessage: {
|
||||
id: 'app.userList.guest.emptyMessage',
|
||||
description: 'Empty guest lobby message label',
|
||||
},
|
||||
inputPlaceholder: {
|
||||
id: 'app.userList.guest.inputPlaceholder',
|
||||
description: 'Placeholder to guest lobby message input',
|
||||
},
|
||||
accept: {
|
||||
id: 'app.userList.guest.acceptLabel',
|
||||
description: 'Accept guest button label'
|
||||
@ -153,6 +162,9 @@ const WaitingUsers = (props) => {
|
||||
guestUsers,
|
||||
guestUsersCall,
|
||||
changeGuestPolicy,
|
||||
isGuestLobbyMessageEnabled,
|
||||
setGuestLobbyMessage,
|
||||
guestLobbyMessage,
|
||||
authenticatedGuest,
|
||||
} = props;
|
||||
|
||||
@ -234,6 +246,16 @@ const WaitingUsers = (props) => {
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
{isGuestLobbyMessageEnabled ? (
|
||||
<div className={styles.lobbyMessage}>
|
||||
<TextInput
|
||||
maxLength={128}
|
||||
placeholder={intl.formatMessage(intlMessages.inputPlaceholder)}
|
||||
send={setGuestLobbyMessage}
|
||||
/>
|
||||
<p><i>"{guestLobbyMessage.length > 0 ? guestLobbyMessage : intl.formatMessage(intlMessages.emptyMessage)}"</i></p>
|
||||
</div>
|
||||
) : null}
|
||||
<div>
|
||||
<div>
|
||||
<p className={styles.mainTitle}>{intl.formatMessage(intlMessages.optionTitle)}</p>
|
||||
|
@ -38,6 +38,9 @@ export default withTracker(() => {
|
||||
authenticatedUsers,
|
||||
guestUsersCall: Service.guestUsersCall,
|
||||
changeGuestPolicy: Service.changeGuestPolicy,
|
||||
isGuestLobbyMessageEnabled: Service.isGuestLobbyMessageEnabled,
|
||||
setGuestLobbyMessage: Service.setGuestLobbyMessage,
|
||||
guestLobbyMessage: Service.getGuestLobbyMessage(),
|
||||
authenticatedGuest,
|
||||
};
|
||||
})(WaitingContainer);
|
||||
|
@ -1,9 +1,30 @@
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
|
||||
const guestUsersCall = (guestsArray, status) => makeCall('allowPendingUsers', guestsArray, status);
|
||||
|
||||
const changeGuestPolicy = policyRule => makeCall('changeGuestPolicy', policyRule);
|
||||
|
||||
const isGuestLobbyMessageEnabled = Meteor.settings.public.app.enableGuestLobbyMessage;
|
||||
|
||||
const getGuestLobbyMessage = () => {
|
||||
const meeting = Meetings.findOne(
|
||||
{ meetingId: Auth.meetingID },
|
||||
{ fields: { guestLobbyMessage: 1 } },
|
||||
);
|
||||
|
||||
if (meeting) return meeting.guestLobbyMessage;
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
const setGuestLobbyMessage = (message) => makeCall('setGuestLobbyMessage', message);
|
||||
|
||||
export default {
|
||||
guestUsersCall,
|
||||
changeGuestPolicy,
|
||||
isGuestLobbyMessageEnabled,
|
||||
getGuestLobbyMessage,
|
||||
setGuestLobbyMessage,
|
||||
};
|
||||
|
@ -186,6 +186,18 @@
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.lobbyMessage {
|
||||
border-bottom: 1px solid var(--color-gray-lightest);
|
||||
|
||||
p {
|
||||
background-color: var(--color-off-white);
|
||||
box-sizing: border-box;
|
||||
color: var(--color-gray);
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.rememberContainer {
|
||||
margin: 1rem 1rem;
|
||||
height: 2rem;
|
||||
|
@ -22,6 +22,7 @@ public:
|
||||
# in some cases we want only custom logoutUrl to be used when provided on meeting create. Default value: true
|
||||
allowDefaultLogoutUrl: true
|
||||
allowUserLookup: false
|
||||
enableGuestLobbyMessage: false
|
||||
enableNetworkInformation: false
|
||||
enableLimitOfViewersInWebcam: false
|
||||
enableMultipleCameras: true
|
||||
|
@ -48,6 +48,7 @@
|
||||
"app.captions.pad.dictationStop": "Stop dictation",
|
||||
"app.captions.pad.dictationOnDesc": "Turns speech recognition on",
|
||||
"app.captions.pad.dictationOffDesc": "Turns speech recognition off",
|
||||
"app.textInput.sendLabel": "Send",
|
||||
"app.note.title": "Shared Notes",
|
||||
"app.note.label": "Note",
|
||||
"app.note.hideNoteLabel": "Hide note",
|
||||
@ -538,6 +539,8 @@
|
||||
"app.userList.guest.pendingGuestUsers": "{0} Pending Guest Users",
|
||||
"app.userList.guest.pendingGuestAlert": "Has joined the session and is waiting for your approval.",
|
||||
"app.userList.guest.rememberChoice": "Remember choice",
|
||||
"app.userList.guest.emptyMessage": "There is currently no message",
|
||||
"app.userList.guest.inputPlaceholder": "Message to the guests' lobby",
|
||||
"app.userList.guest.acceptLabel": "Accept",
|
||||
"app.userList.guest.denyLabel": "Deny",
|
||||
"app.user-info.title": "Directory Lookup",
|
||||
|
@ -59,12 +59,24 @@
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
function updateMessage(message) {
|
||||
document.querySelector('#content > p').innerHTML = message;
|
||||
}
|
||||
|
||||
var lobbyMessage = '';
|
||||
function updateLobbyMessage(message) {
|
||||
if (message !== lobbyMessage) {
|
||||
lobbyMessage = message;
|
||||
if (lobbyMessage.length !== 0) {
|
||||
updateMessage(lobbyMessage);
|
||||
} else {
|
||||
updateMessage('Please wait for a moderator to approve you joining the meeting.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findSessionToken() {
|
||||
return location.search
|
||||
.substr(1)
|
||||
@ -75,7 +87,7 @@
|
||||
};
|
||||
|
||||
function fetchGuestWait(sessionToken) {
|
||||
const GUEST_WAIT_ENDPOINT = '/bigbluebutton/api/guestWait';
|
||||
const GUEST_WAIT_ENDPOINT = '/bigbluebutton/api/guestWait';
|
||||
const urlTest = new URL(`${window.location.origin}${GUEST_WAIT_ENDPOINT}`);
|
||||
const concatedParams = sessionToken.concat('&redirect=false');
|
||||
urlTest.search = concatedParams;
|
||||
@ -95,7 +107,6 @@
|
||||
fetchGuestWait(token)
|
||||
.then(async (resp) => await resp.json())
|
||||
.then((data) => {
|
||||
console.log("data=" + JSON.stringify(data));
|
||||
var status = data.response.guestStatus;
|
||||
|
||||
if (REDIRECT_STATUSES.includes(status)) {
|
||||
@ -104,6 +115,8 @@
|
||||
return;
|
||||
}
|
||||
|
||||
updateLobbyMessage(data.response.lobbyMessage);
|
||||
|
||||
return pollGuestStatus(token, attempt + 1, limit, everyMs);
|
||||
});
|
||||
}, everyMs);
|
||||
|
@ -1323,6 +1323,7 @@ class ApiController {
|
||||
// Get the client url we stored in the join api call before
|
||||
// being told to wait.
|
||||
String clientURL = us.clientUrl;
|
||||
String lobbyMsg = meeting.getGuestLobbyMessage()
|
||||
log.info("clientURL = " + clientURL)
|
||||
log.info("redirect = ." + redirectClient)
|
||||
if (!StringUtils.isEmpty(params.redirect)) {
|
||||
@ -1412,6 +1413,7 @@ class ApiController {
|
||||
auth_token us.authToken
|
||||
session_token session[sessionToken]
|
||||
guestStatus guestWaitStatus
|
||||
lobbyMessage lobbyMsg
|
||||
url destUrl
|
||||
}
|
||||
render(contentType: "application/json", text: builder.toPrettyString())
|
||||
|
Loading…
Reference in New Issue
Block a user