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:
Pedro Beschorner Marin 2020-10-02 14:29:27 -03:00
parent 4ba2082fb6
commit 0365018e92
29 changed files with 398 additions and 3 deletions

View File

@ -7,6 +7,7 @@ trait GuestsApp extends GetGuestsWaitingApprovalReqMsgHdlr
with GuestsWaitingApprovedMsgHdlr
with GuestWaitingLeftMsgHdlr
with SetGuestPolicyMsgHdlr
with SetGuestLobbyMessageMsgHdlr
with GetGuestPolicyReqMsgHdlr {
this: MeetingActor =>

View File

@ -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)

View File

@ -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 =>

View File

@ -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)

View File

@ -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)

View File

@ -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)
}
}
}

View File

@ -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)

View File

@ -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.
*/

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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 =>

View File

@ -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))
}

View File

@ -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,
});

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -148,6 +148,7 @@ export default function addMeeting(meeting) {
meetingId,
meetingEnded,
publishedPoll: false,
guestLobbyMessage: '',
randomlySelectedUser: '',
}, flat(newMeeting, {
safe: true,

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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);

View File

@ -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,
};

View File

@ -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;

View File

@ -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

View File

@ -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",

View File

@ -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);

View File

@ -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())