diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/GuestsApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/GuestsApp.scala index 823e999df6..9c97de077f 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/GuestsApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/GuestsApp.scala @@ -7,6 +7,7 @@ trait GuestsApp extends GetGuestsWaitingApprovalReqMsgHdlr with GuestsWaitingApprovedMsgHdlr with GuestWaitingLeftMsgHdlr with SetGuestPolicyMsgHdlr + with SetGuestLobbyMessageMsgHdlr with GetGuestPolicyReqMsgHdlr { this: MeetingActor => diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/GuestsWaiting.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/GuestsWaiting.scala index 88339acf05..67eedfcb48 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/GuestsWaiting.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/GuestsWaiting.scala @@ -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) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala index 497e168bb5..fcb3dcf925 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala @@ -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 => diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala index 98172c3dae..066935415b 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala @@ -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) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/AnalyticsActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/AnalyticsActor.scala index 7c08cdfdfd..608f9c2d84 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/AnalyticsActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/AnalyticsActor.scala @@ -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) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/SetGuestLobbyMessageMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/SetGuestLobbyMessageMsgHdlr.scala new file mode 100755 index 0000000000..dc5ad18a1d --- /dev/null +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/SetGuestLobbyMessageMsgHdlr.scala @@ -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) + } + } +} diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala index 82f228180c..cebf928937 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala @@ -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) diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala index fd438b24b8..308c481f56 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala @@ -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. */ diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java index 814736ff60..fad6f60344 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java @@ -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) { diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java index 1393228d56..e2884db344 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java @@ -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 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; } diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/messaging/messages/GuestLobbyMessageChanged.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/messaging/messages/GuestLobbyMessageChanged.java new file mode 100755 index 0000000000..ca76c835e8 --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/messaging/messages/GuestLobbyMessageChanged.java @@ -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; + } +} diff --git a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/bus/ReceivedJsonMsgHdlrActor.scala b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/bus/ReceivedJsonMsgHdlrActor.scala index 7eb216f80f..2676e1bdad 100755 --- a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/bus/ReceivedJsonMsgHdlrActor.scala +++ b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/bus/ReceivedJsonMsgHdlrActor.scala @@ -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 => diff --git a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/OldMeetingMsgHdlrActor.scala b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/OldMeetingMsgHdlrActor.scala index 09fd1744a7..ac25f7610c 100755 --- a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/OldMeetingMsgHdlrActor.scala +++ b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/OldMeetingMsgHdlrActor.scala @@ -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)) } diff --git a/bigbluebutton-html5/imports/api/guest-users/server/methods.js b/bigbluebutton-html5/imports/api/guest-users/server/methods.js index dc7ccd93f0..f1dbcab3f7 100644 --- a/bigbluebutton-html5/imports/api/guest-users/server/methods.js +++ b/bigbluebutton-html5/imports/api/guest-users/server/methods.js @@ -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, }); diff --git a/bigbluebutton-html5/imports/api/guest-users/server/methods/setGuestLobbyMessage.js b/bigbluebutton-html5/imports/api/guest-users/server/methods/setGuestLobbyMessage.js new file mode 100644 index 0000000000..770ea41523 --- /dev/null +++ b/bigbluebutton-html5/imports/api/guest-users/server/methods/setGuestLobbyMessage.js @@ -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); +} diff --git a/bigbluebutton-html5/imports/api/meetings/server/eventHandlers.js b/bigbluebutton-html5/imports/api/meetings/server/eventHandlers.js index 81f0ef206a..e13e7c5f2d 100644 --- a/bigbluebutton-html5/imports/api/meetings/server/eventHandlers.js +++ b/bigbluebutton-html5/imports/api/meetings/server/eventHandlers.js @@ -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); diff --git a/bigbluebutton-html5/imports/api/meetings/server/handlers/guestLobbyMessageChanged.js b/bigbluebutton-html5/imports/api/meetings/server/handlers/guestLobbyMessageChanged.js new file mode 100644 index 0000000000..e6bca30ecf --- /dev/null +++ b/bigbluebutton-html5/imports/api/meetings/server/handlers/guestLobbyMessageChanged.js @@ -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); +} diff --git a/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js b/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js index a17bec63f2..a0dea94a0a 100755 --- a/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js +++ b/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js @@ -148,6 +148,7 @@ export default function addMeeting(meeting) { meetingId, meetingEnded, publishedPoll: false, + guestLobbyMessage: '', randomlySelectedUser: '', }, flat(newMeeting, { safe: true, diff --git a/bigbluebutton-html5/imports/api/meetings/server/modifiers/setGuestLobbyMessage.js b/bigbluebutton-html5/imports/api/meetings/server/modifiers/setGuestLobbyMessage.js new file mode 100644 index 0000000000..8769aac5b5 --- /dev/null +++ b/bigbluebutton-html5/imports/api/meetings/server/modifiers/setGuestLobbyMessage.js @@ -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); +} diff --git a/bigbluebutton-html5/imports/ui/components/text-input/component.jsx b/bigbluebutton-html5/imports/ui/components/text-input/component.jsx new file mode 100644 index 0000000000..d3a1a3d1eb --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/text-input/component.jsx @@ -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 ( +
+ this.handleOnChange(e)} + placeholder={placeholder} + value={message} + /> +
+ ); + } +} + +TextInput.propTypes = propTypes; +TextInput.defaultProps = defaultProps; + +export default injectIntl(TextInput); diff --git a/bigbluebutton-html5/imports/ui/components/text-input/styles.scss b/bigbluebutton-html5/imports/ui/components/text-input/styles.scss new file mode 100644 index 0000000000..5f13ce4fa0 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/text-input/styles.scss @@ -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); + } +} diff --git a/bigbluebutton-html5/imports/ui/components/waiting-users/component.jsx b/bigbluebutton-html5/imports/ui/components/waiting-users/component.jsx index 94d4890011..db6ee3d12c 100755 --- a/bigbluebutton-html5/imports/ui/components/waiting-users/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/waiting-users/component.jsx @@ -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) => { /> + {isGuestLobbyMessageEnabled ? ( +
+ +

"{guestLobbyMessage.length > 0 ? guestLobbyMessage : intl.formatMessage(intlMessages.emptyMessage)}"

+
+ ) : null}

{intl.formatMessage(intlMessages.optionTitle)}

diff --git a/bigbluebutton-html5/imports/ui/components/waiting-users/container.jsx b/bigbluebutton-html5/imports/ui/components/waiting-users/container.jsx index 7e6e879ae4..0341da5a5d 100644 --- a/bigbluebutton-html5/imports/ui/components/waiting-users/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/waiting-users/container.jsx @@ -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); diff --git a/bigbluebutton-html5/imports/ui/components/waiting-users/service.js b/bigbluebutton-html5/imports/ui/components/waiting-users/service.js index 23392f0c52..62d4165000 100644 --- a/bigbluebutton-html5/imports/ui/components/waiting-users/service.js +++ b/bigbluebutton-html5/imports/ui/components/waiting-users/service.js @@ -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, }; diff --git a/bigbluebutton-html5/imports/ui/components/waiting-users/styles.scss b/bigbluebutton-html5/imports/ui/components/waiting-users/styles.scss index 95f43ac21c..bc5791c572 100644 --- a/bigbluebutton-html5/imports/ui/components/waiting-users/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/waiting-users/styles.scss @@ -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; diff --git a/bigbluebutton-html5/private/config/settings.yml b/bigbluebutton-html5/private/config/settings.yml index d268233858..add66fbc5d 100755 --- a/bigbluebutton-html5/private/config/settings.yml +++ b/bigbluebutton-html5/private/config/settings.yml @@ -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 diff --git a/bigbluebutton-html5/private/locales/en.json b/bigbluebutton-html5/private/locales/en.json index 4e0dc1eb55..4b8fd113e8 100755 --- a/bigbluebutton-html5/private/locales/en.json +++ b/bigbluebutton-html5/private/locales/en.json @@ -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", diff --git a/bigbluebutton-html5/private/static/guest-wait/guest-wait.html b/bigbluebutton-html5/private/static/guest-wait/guest-wait.html index 8de097f302..e2181188db 100755 --- a/bigbluebutton-html5/private/static/guest-wait/guest-wait.html +++ b/bigbluebutton-html5/private/static/guest-wait/guest-wait.html @@ -59,12 +59,24 @@ } } - +