From 35ddbed8c2b416516108a98fa18bd88aebb4a5f5 Mon Sep 17 00:00:00 2001
From: Tainan Felipe
Date: Tue, 14 Dec 2021 15:45:59 -0300
Subject: [PATCH 001/180] add msg userLeftFlag
---
akka-bbb-apps/.bsp/sbt.json | 1 +
.../org/bigbluebutton/SystemConfiguration.scala | 2 +-
.../apps/users/SelectRandomViewerReqMsgHdlr.scala | 2 +-
.../org/bigbluebutton/core/models/Users2x.scala | 7 +++++--
.../core2/message/senders/MsgBuilder.scala | 10 ++++++++++
.../org/bigbluebutton/common2/msgs/UsersMsgs.scala | 14 ++++++++++++++
6 files changed, 32 insertions(+), 4 deletions(-)
create mode 100644 akka-bbb-apps/.bsp/sbt.json
diff --git a/akka-bbb-apps/.bsp/sbt.json b/akka-bbb-apps/.bsp/sbt.json
new file mode 100644
index 0000000000..6847cb6874
--- /dev/null
+++ b/akka-bbb-apps/.bsp/sbt.json
@@ -0,0 +1 @@
+{"name":"sbt","version":"1.5.2","bspVersion":"2.0.0-M5","languages":["scala"],"argv":["/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java","-Xms100m","-Xmx100m","-classpath","/home/dev/.local/share/JetBrains/IdeaIC2021.3/Scala/launcher/sbt-launch.jar","xsbt.boot.Boot","-bsp","--sbt-launch-jar=/home/dev/.local/share/JetBrains/IdeaIC2021.3/Scala/launcher/sbt-launch.jar"]}
\ No newline at end of file
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala
index e6d16fbe4a..34ace5d20a 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala
@@ -51,7 +51,7 @@ trait SystemConfiguration {
lazy val endMeetingWhenNoMoreAuthedUsersAfterMinutes = Try(config.getInt("apps.endMeetingWhenNoMoreAuthedUsersAfterMinutes")).getOrElse(2)
lazy val reduceDuplicatedPick = Try(config.getBoolean("apps.reduceDuplicatedPick")).getOrElse(false)
-
+
// Redis server configuration
lazy val redisHost = Try(config.getString("redis.host")).getOrElse("127.0.0.1")
lazy val redisPort = Try(config.getInt("redis.port")).getOrElse(6379)
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/SelectRandomViewerReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/SelectRandomViewerReqMsgHdlr.scala
index 54b13f5a21..762012dc4c 100644
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/SelectRandomViewerReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/SelectRandomViewerReqMsgHdlr.scala
@@ -50,7 +50,7 @@ trait SelectRandomViewerReqMsgHdlr extends RightsManagementTrait {
Users2x.setUserExempted(liveMeeting.users2x, pickedUser, true)
}
}
-
+
val userIds = users.map { case (v) => v.intId }
broadcastEvent(msg, userIds, pickedUser)
}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala
index 13d8922495..9d6e3d64bb 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala
@@ -2,6 +2,7 @@ package org.bigbluebutton.core.models
import com.softwaremill.quicklens._
import org.bigbluebutton.core.util.TimeUtil
+import org.bigbluebutton.core2.message.senders.MsgBuilder
object Users2x {
def findWithIntId(users: Users2x, intId: String): Option[UserState] = {
@@ -33,6 +34,8 @@ object Users2x {
} yield {
val newUser = u.copy(userLeftFlag = UserLeftFlag(true, System.currentTimeMillis()))
users.save(newUser)
+ val userLeftMeetingEvent = MsgBuilder.buildUserLeftMeetingEvtMsg(, u.intId)
+ outGW.send(userLeftMeetingEvent)
newUser
}
}
@@ -50,7 +53,7 @@ object Users2x {
def findAllExpiredUserLeftFlags(users: Users2x, meetingExpireWhenLastUserLeftInMs: Long): Vector[UserState] = {
if (meetingExpireWhenLastUserLeftInMs > 0) {
users.toVector filter (u => u.userLeftFlag.left && u.userLeftFlag.leftOn != 0 &&
- System.currentTimeMillis() - u.userLeftFlag.leftOn > 10000)
+ System.currentTimeMillis() - u.userLeftFlag.leftOn > 30000)
} else {
// When meetingExpireWhenLastUserLeftInMs is set zero we need to
// remove user right away to end the meeting as soon as possible.
@@ -164,7 +167,7 @@ object Users2x {
newUser
}
}
-
+
def hasPresenter(users: Users2x): Boolean = {
findPresenter(users) match {
case Some(p) => true
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 4fcf02542d..6f4ee9e751 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
@@ -306,6 +306,16 @@ object MsgBuilder {
BbbCommonEnvCoreMsg(envelope, event)
}
+ def buildUserLeftFlagEvtMsg(meetingId: String, userId: String, userLeftFlag: Boolean): BbbCommonEnvCoreMsg = {
+ val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
+ val envelope = BbbCoreEnvelope(UserLeftFlagEvtMsg.NAME, routing)
+ val header = BbbClientMsgHeader(UserLeftFlagEvtMsg.NAME, meetingId, userId)
+ val body = UserLeftFlagEvtMsgBody(userId, userLeftFlag)
+ val event = UserLeftFlagEvtMsg(header, body)
+
+ BbbCommonEnvCoreMsg(envelope, event)
+ }
+
def buildUserInactivityInspectMsg(meetingId: String, userId: String, responseDelay: Long): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(UserInactivityInspectMsg.NAME, routing)
diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/UsersMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/UsersMsgs.scala
index 90b2abd1b4..4be0f8d6a1 100755
--- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/UsersMsgs.scala
+++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/UsersMsgs.scala
@@ -77,6 +77,20 @@ case class UserLeftMeetingEvtMsg(
) extends BbbCoreMsg
case class UserLeftMeetingEvtMsgBody(intId: String)
+object UserLeftFlagEvtMsg {
+ val NAME = "UserLeftFlagEvtMsg"
+ def apply(meetingId: String, userId: String, body: UserLeftFlagEvtMsgBody): UserLeftFlagEvtMsg = {
+ val header = BbbClientMsgHeader(UserLeftFlagEvtMsg.NAME, meetingId, userId)
+ UserLeftFlagEvtMsg(header, body)
+ }
+}
+
+case class UserLeftFlagEvtMsg(
+ header: BbbClientMsgHeader,
+ body: UserLeftFlagEvtMsgBody
+) extends BbbCoreMsg
+case class UserLeftFlagEvtMsgBody(intId: String, userLeftFlag: Boolean)
+
object UserJoinedMeetingEvtMsg {
val NAME = "UserJoinedMeetingEvtMsg"
def apply(meetingId: String, userId: String, body: UserJoinedMeetingEvtMsgBody): UserJoinedMeetingEvtMsg = {
From 9c844bdd26be38d19db5fad4ce2edbdb73f4d50a Mon Sep 17 00:00:00 2001
From: Tainan Felipe
Date: Thu, 6 Jan 2022 18:07:22 -0300
Subject: [PATCH 002/180] Add userLeftFlag message
---
...rJoinMeetingAfterReconnectReqMsgHdlr.scala | 9 ++++++-
.../users/UserJoinMeetingReqMsgHdlr.scala | 7 ++++++
.../core/apps/users/UserLeaveReqMsgHdlr.scala | 7 ++++++
.../bigbluebutton/core/models/Users2x.scala | 2 --
.../server/modifiers/addUserPersistentData.js | 2 +-
.../imports/api/users/server/eventHandlers.js | 2 ++
.../api/users/server/handlers/userLeftFlag.js | 10 ++++++++
.../api/users/server/modifiers/addUser.js | 1 +
.../users/server/modifiers/userLeftFlag.js | 24 +++++++++++++++++++
.../imports/api/users/server/publishers.js | 1 +
10 files changed, 61 insertions(+), 4 deletions(-)
create mode 100644 bigbluebutton-html5/imports/api/users/server/handlers/userLeftFlag.js
create mode 100644 bigbluebutton-html5/imports/api/users/server/modifiers/userLeftFlag.js
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala
index 88a06ea09e..cd8ec9b5be 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala
@@ -6,6 +6,7 @@ import org.bigbluebutton.core.apps.voice.UserJoinedVoiceConfEvtMsgHdlr
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.Users2x
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, MeetingActor, OutMsgRouter }
+import org.bigbluebutton.core2.message.senders.MsgBuilder
trait UserJoinMeetingAfterReconnectReqMsgHdlr extends HandlerHelpers with UserJoinedVoiceConfEvtMsgHdlr {
this: MeetingActor =>
@@ -15,12 +16,18 @@ trait UserJoinMeetingAfterReconnectReqMsgHdlr extends HandlerHelpers with UserJo
def handleUserJoinMeetingAfterReconnectReqMsg(msg: UserJoinMeetingAfterReconnectReqMsg, state: MeetingState2x): MeetingState2x = {
log.info("Received user joined after reconnecting. user {} meetingId={}", msg.body.userId, msg.header.meetingId)
-
Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) match {
case Some(reconnectingUser) =>
if (reconnectingUser.userLeftFlag.left) {
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
// User has reconnected. Just reset it's flag. ralam Oct 23, 2018
+ println(Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId))
+ for {
+ u <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
+ } yield {
+ val userLeftFlagMeetingEvent = MsgBuilder.buildUserLeftFlagEvtMsg(liveMeeting.props.meetingProp.intId, u.intId, false)
+ outGW.send(userLeftFlagMeetingEvent)
+ }
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
}
state
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala
index cd8011ff15..e85f341fee 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala
@@ -5,6 +5,7 @@ import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers
import org.bigbluebutton.core.models.{ Users2x, VoiceUsers }
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, MeetingActor, OutMsgRouter }
+import org.bigbluebutton.core2.message.senders.MsgBuilder
trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
this: MeetingActor =>
@@ -20,6 +21,12 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
if (reconnectingUser.userLeftFlag.left) {
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
// User has reconnected. Just reset it's flag. ralam Oct 23, 2018
+ for {
+ u <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
+ } yield {
+ val userLeftFlagMeetingEvent = MsgBuilder.buildUserLeftFlagEvtMsg(liveMeeting.props.meetingProp.intId, u.intId, false)
+ outGW.send(userLeftFlagMeetingEvent)
+ }
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
}
state
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala
index 6be39e1d9d..829f7310a0 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala
@@ -4,6 +4,7 @@ import org.bigbluebutton.common2.msgs.UserLeaveReqMsg
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.{ RegisteredUsers, Users2x }
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
+import org.bigbluebutton.core2.message.senders.MsgBuilder
trait UserLeaveReqMsgHdlr {
this: MeetingActor =>
@@ -19,6 +20,12 @@ trait UserLeaveReqMsgHdlr {
// Just flag that user has left as the user might be reconnecting.
// An audit will remove this user if it hasn't rejoined after a certain period of time.
// ralam oct 23, 2018
+ for {
+ u <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
+ } yield {
+ val userLeftFlagMeetingEvent = MsgBuilder.buildUserLeftFlagEvtMsg(liveMeeting.props.meetingProp.intId, u.intId, true)
+ outGW.send(userLeftFlagMeetingEvent)
+ }
Users2x.setUserLeftFlag(liveMeeting.users2x, msg.body.userId)
}
if (msg.body.loggedOut) {
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala
index 9d6e3d64bb..355e9ae768 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala
@@ -34,8 +34,6 @@ object Users2x {
} yield {
val newUser = u.copy(userLeftFlag = UserLeftFlag(true, System.currentTimeMillis()))
users.save(newUser)
- val userLeftMeetingEvent = MsgBuilder.buildUserLeftMeetingEvtMsg(, u.intId)
- outGW.send(userLeftMeetingEvent)
newUser
}
}
diff --git a/bigbluebutton-html5/imports/api/users-persistent-data/server/modifiers/addUserPersistentData.js b/bigbluebutton-html5/imports/api/users-persistent-data/server/modifiers/addUserPersistentData.js
index 401ad1d4a0..2cd8fdf6b1 100644
--- a/bigbluebutton-html5/imports/api/users-persistent-data/server/modifiers/addUserPersistentData.js
+++ b/bigbluebutton-html5/imports/api/users-persistent-data/server/modifiers/addUserPersistentData.js
@@ -25,10 +25,10 @@ export default function addUserPersistentData(user) {
locked: Boolean,
avatar: String,
clientType: String,
+ left: Boolean,
effectiveConnectionType: null,
});
-
const {
intId,
extId,
diff --git a/bigbluebutton-html5/imports/api/users/server/eventHandlers.js b/bigbluebutton-html5/imports/api/users/server/eventHandlers.js
index 6e26902b81..3e189a562f 100644
--- a/bigbluebutton-html5/imports/api/users/server/eventHandlers.js
+++ b/bigbluebutton-html5/imports/api/users/server/eventHandlers.js
@@ -1,6 +1,7 @@
import RedisPubSub from '/imports/startup/server/redis';
import handleRemoveUser from './handlers/removeUser';
import handleUserJoined from './handlers/userJoined';
+import handleUserLeftFlag from './handlers/userLeftFlag';
import handleValidateAuthToken from './handlers/validateAuthToken';
import handlePresenterAssigned from './handlers/presenterAssigned';
import handleEmojiStatus from './handlers/emojiStatus';
@@ -15,4 +16,5 @@ RedisPubSub.on('ValidateAuthTokenRespMsg', handleValidateAuthToken);
RedisPubSub.on('UserEmojiChangedEvtMsg', handleEmojiStatus);
RedisPubSub.on('UserEjectedFromMeetingEvtMsg', handleUserEjected);
RedisPubSub.on('UserRoleChangedEvtMsg', handleChangeRole);
+RedisPubSub.on('UserLeftFlagEvtMsg', handleUserLeftFlag);
RedisPubSub.on('UserInactivityInspectMsg', handleUserInactivityInspect);
diff --git a/bigbluebutton-html5/imports/api/users/server/handlers/userLeftFlag.js b/bigbluebutton-html5/imports/api/users/server/handlers/userLeftFlag.js
new file mode 100644
index 0000000000..f5f784982f
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/users/server/handlers/userLeftFlag.js
@@ -0,0 +1,10 @@
+import { check } from 'meteor/check';
+
+import userLeftFlag from '../modifiers/userLeftFlag';
+
+export default function handleUserLeftFlag({ body }, meetingId) {
+ const user = body;
+ check(user, Object);
+
+ userLeftFlag(meetingId, user.intId, user.userLeftFlag);
+}
diff --git a/bigbluebutton-html5/imports/api/users/server/modifiers/addUser.js b/bigbluebutton-html5/imports/api/users/server/modifiers/addUser.js
index 9d45538a08..47e35b6881 100755
--- a/bigbluebutton-html5/imports/api/users/server/modifiers/addUser.js
+++ b/bigbluebutton-html5/imports/api/users/server/modifiers/addUser.js
@@ -61,6 +61,7 @@ export default function addUser(meetingId, userData) {
inactivityCheck: false,
responseDelay: 0,
loggedOut: false,
+ left: false,
...flat(user),
};
diff --git a/bigbluebutton-html5/imports/api/users/server/modifiers/userLeftFlag.js b/bigbluebutton-html5/imports/api/users/server/modifiers/userLeftFlag.js
new file mode 100644
index 0000000000..4cd1b4130d
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/users/server/modifiers/userLeftFlag.js
@@ -0,0 +1,24 @@
+import Logger from '/imports/startup/server/logger';
+import Users from '/imports/api/users';
+
+export default function userLeftFlag(meetingId, userId, left) {
+ const selector = {
+ meetingId,
+ userId,
+ };
+
+ const modifier = {
+ $set: {
+ left,
+ },
+ };
+
+ try {
+ const numberAffected = Users.update(selector, modifier);
+ if (numberAffected) {
+ Logger.info(`Updated user ${userId} with left flag as ${left}`);
+ }
+ } catch (err) {
+ Logger.error(`Changed user role: ${err}`);
+ }
+}
diff --git a/bigbluebutton-html5/imports/api/users/server/publishers.js b/bigbluebutton-html5/imports/api/users/server/publishers.js
index 1935728a22..ba78a24a9d 100644
--- a/bigbluebutton-html5/imports/api/users/server/publishers.js
+++ b/bigbluebutton-html5/imports/api/users/server/publishers.js
@@ -59,6 +59,7 @@ function users(role) {
{ meetingId },
],
intId: { $exists: true },
+ left: false,
};
const User = Users.findOne({ userId, meetingId }, { fields: { role: 1 } });
From 5ae5d230b8868ec838459376259c2a05b2a45584 Mon Sep 17 00:00:00 2001
From: Tainan Felipe
Date: Fri, 7 Jan 2022 11:37:23 -0300
Subject: [PATCH 003/180] Create util function in HandlerHelper
---
akka-bbb-apps/.bsp/sbt.json | 1 -
.../org/bigbluebutton/SystemConfiguration.scala | 1 -
.../apps/users/SelectRandomViewerReqMsgHdlr.scala | 1 -
.../UserJoinMeetingAfterReconnectReqMsgHdlr.scala | 7 +------
.../apps/users/UserJoinMeetingReqMsgHdlr.scala | 8 ++------
.../core/apps/users/UserLeaveReqMsgHdlr.scala | 13 ++++---------
.../org/bigbluebutton/core/models/Users2x.scala | 2 +-
.../core/running/HandlerHelpers.scala | 14 ++++++++++++++
.../api/users/server/modifiers/createDummyUser.js | 1 +
9 files changed, 23 insertions(+), 25 deletions(-)
delete mode 100644 akka-bbb-apps/.bsp/sbt.json
diff --git a/akka-bbb-apps/.bsp/sbt.json b/akka-bbb-apps/.bsp/sbt.json
deleted file mode 100644
index 6847cb6874..0000000000
--- a/akka-bbb-apps/.bsp/sbt.json
+++ /dev/null
@@ -1 +0,0 @@
-{"name":"sbt","version":"1.5.2","bspVersion":"2.0.0-M5","languages":["scala"],"argv":["/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java","-Xms100m","-Xmx100m","-classpath","/home/dev/.local/share/JetBrains/IdeaIC2021.3/Scala/launcher/sbt-launch.jar","xsbt.boot.Boot","-bsp","--sbt-launch-jar=/home/dev/.local/share/JetBrains/IdeaIC2021.3/Scala/launcher/sbt-launch.jar"]}
\ No newline at end of file
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala
index 34ace5d20a..30353048d9 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala
@@ -51,7 +51,6 @@ trait SystemConfiguration {
lazy val endMeetingWhenNoMoreAuthedUsersAfterMinutes = Try(config.getInt("apps.endMeetingWhenNoMoreAuthedUsersAfterMinutes")).getOrElse(2)
lazy val reduceDuplicatedPick = Try(config.getBoolean("apps.reduceDuplicatedPick")).getOrElse(false)
-
// Redis server configuration
lazy val redisHost = Try(config.getString("redis.host")).getOrElse("127.0.0.1")
lazy val redisPort = Try(config.getInt("redis.port")).getOrElse(6379)
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/SelectRandomViewerReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/SelectRandomViewerReqMsgHdlr.scala
index 762012dc4c..85858f2dfd 100644
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/SelectRandomViewerReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/SelectRandomViewerReqMsgHdlr.scala
@@ -50,7 +50,6 @@ trait SelectRandomViewerReqMsgHdlr extends RightsManagementTrait {
Users2x.setUserExempted(liveMeeting.users2x, pickedUser, true)
}
}
-
val userIds = users.map { case (v) => v.intId }
broadcastEvent(msg, userIds, pickedUser)
}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala
index cd8ec9b5be..4d32b49abc 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala
@@ -22,12 +22,7 @@ trait UserJoinMeetingAfterReconnectReqMsgHdlr extends HandlerHelpers with UserJo
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
// User has reconnected. Just reset it's flag. ralam Oct 23, 2018
println(Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId))
- for {
- u <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
- } yield {
- val userLeftFlagMeetingEvent = MsgBuilder.buildUserLeftFlagEvtMsg(liveMeeting.props.meetingProp.intId, u.intId, false)
- outGW.send(userLeftFlagMeetingEvent)
- }
+ sendUserLeftFlagEvtMsg(outGW, liveMeeting, msg.body.userId, false);
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
}
state
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala
index e85f341fee..585632f442 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala
@@ -21,14 +21,10 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
if (reconnectingUser.userLeftFlag.left) {
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
// User has reconnected. Just reset it's flag. ralam Oct 23, 2018
- for {
- u <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
- } yield {
- val userLeftFlagMeetingEvent = MsgBuilder.buildUserLeftFlagEvtMsg(liveMeeting.props.meetingProp.intId, u.intId, false)
- outGW.send(userLeftFlagMeetingEvent)
- }
+ sendUserLeftFlagEvtMsg(outGW, liveMeeting, msg.body.userId, false);
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
}
+
state
case None =>
val newState = userJoinMeeting(outGW, msg.body.authToken, msg.body.clientType, liveMeeting, state)
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala
index 829f7310a0..054afb1be1 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala
@@ -3,10 +3,9 @@ package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs.UserLeaveReqMsg
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.{ RegisteredUsers, Users2x }
-import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
-import org.bigbluebutton.core2.message.senders.MsgBuilder
+import org.bigbluebutton.core.running.{ HandlerHelpers, MeetingActor, OutMsgRouter }
-trait UserLeaveReqMsgHdlr {
+trait UserLeaveReqMsgHdlr extends HandlerHelpers {
this: MeetingActor =>
val outGW: OutMsgRouter
@@ -20,12 +19,8 @@ trait UserLeaveReqMsgHdlr {
// Just flag that user has left as the user might be reconnecting.
// An audit will remove this user if it hasn't rejoined after a certain period of time.
// ralam oct 23, 2018
- for {
- u <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
- } yield {
- val userLeftFlagMeetingEvent = MsgBuilder.buildUserLeftFlagEvtMsg(liveMeeting.props.meetingProp.intId, u.intId, true)
- outGW.send(userLeftFlagMeetingEvent)
- }
+ sendUserLeftFlagEvtMsg(outGW, liveMeeting, msg.body.userId, true);
+
Users2x.setUserLeftFlag(liveMeeting.users2x, msg.body.userId)
}
if (msg.body.loggedOut) {
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala
index 355e9ae768..1456349577 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala
@@ -51,7 +51,7 @@ object Users2x {
def findAllExpiredUserLeftFlags(users: Users2x, meetingExpireWhenLastUserLeftInMs: Long): Vector[UserState] = {
if (meetingExpireWhenLastUserLeftInMs > 0) {
users.toVector filter (u => u.userLeftFlag.left && u.userLeftFlag.leftOn != 0 &&
- System.currentTimeMillis() - u.userLeftFlag.leftOn > 30000)
+ System.currentTimeMillis() - u.userLeftFlag.leftOn > 10000)
} else {
// When meetingExpireWhenLastUserLeftInMs is set zero we need to
// remove user right away to end the meeting as soon as possible.
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala
index ea649d84a5..1054d25591 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala
@@ -37,6 +37,20 @@ trait HandlerHelpers extends SystemConfiguration {
}
}
+ def sendUserLeftFlagEvtMsg(
+ outGW: OutMsgRouter,
+ liveMeeting: LiveMeeting,
+ intId: String,
+ leftFlag: Boolean
+ ): Unit = {
+ for {
+ u <- Users2x.findWithIntId(liveMeeting.users2x, intId)
+ } yield {
+ val userLeftFlagMeetingEvent = MsgBuilder.buildUserLeftFlagEvtMsg(liveMeeting.props.meetingProp.intId, u.intId, leftFlag)
+ outGW.send(userLeftFlagMeetingEvent)
+ }
+ }
+
def userJoinMeeting(outGW: OutMsgRouter, authToken: String, clientType: String,
liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = {
diff --git a/bigbluebutton-html5/imports/api/users/server/modifiers/createDummyUser.js b/bigbluebutton-html5/imports/api/users/server/modifiers/createDummyUser.js
index c8551f0d63..b5390ed728 100644
--- a/bigbluebutton-html5/imports/api/users/server/modifiers/createDummyUser.js
+++ b/bigbluebutton-html5/imports/api/users/server/modifiers/createDummyUser.js
@@ -19,6 +19,7 @@ export default function createDummyUser(meetingId, userId, authToken) {
authToken,
clientType: 'HTML5',
validated: null,
+ left: false,
};
try {
From 7fc8f91a61386125855207c0d538a2b47887a627 Mon Sep 17 00:00:00 2001
From: Joao Victor
Date: Wed, 9 Feb 2022 10:09:27 -0300
Subject: [PATCH 004/180] Restore audio within main room after leaving breakout
room
---
.../ui/components/breakout-room/component.jsx | 43 +++++++++++++++++++
.../ui/components/breakout-room/container.jsx | 4 +-
2 files changed, 46 insertions(+), 1 deletion(-)
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
index c3c96cc946..cc37b69665 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
@@ -13,6 +13,12 @@ import { PANELS, ACTIONS } from '../layout/enums';
import { screenshareHasEnded } from '/imports/ui/components/screenshare/service';
import UserListService from '/imports/ui/components/user-list/service';
import AudioManager from '/imports/ui/services/audio-manager';
+import Users from '/imports/api/users';
+import Auth from '/imports/ui/services/auth';
+import {
+ didUserSelectedMicrophone,
+ didUserSelectedListenOnly
+} from '/imports/ui/components/audio/audio-modal/service';
const intlMessages = defineMessages({
breakoutTitle: {
@@ -264,6 +270,8 @@ class BreakoutRoom extends PureComponent {
intl,
isUserInBreakoutRoom,
exitAudio,
+ joinMicrophone,
+ joinListenOnly,
setBreakoutAudioTransferStatus,
getBreakoutAudioTransferStatus,
} = this.props;
@@ -340,6 +348,41 @@ class BreakoutRoom extends PureComponent {
VideoService.storeDeviceIds();
VideoService.exitVideo();
if (UserListService.amIPresenter()) screenshareHasEnded();
+
+ Tracker.autorun((c) => {
+ const selector = {
+ meetingId: breakoutId,
+ };
+
+ const query = Users.find(selector, {
+ fields: {
+ loggedOut: 1,
+ extId: 1,
+ },
+ });
+
+ const rejoinAudio = () => {
+ if (didUserSelectedMicrophone()) {
+ joinMicrophone();
+ } else if (didUserSelectedListenOnly()) {
+ joinListenOnly();
+ }
+ c.stop();
+ }
+
+ query.observe({
+ added: (user) => {
+ if (user?.extId?.startsWith(Auth.userID) && user?.loggedOut) {
+ rejoinAudio();
+ }
+ },
+ changed: (user) => {
+ if (user?.extId?.startsWith(Auth.userID) && user?.loggedOut) {
+ rejoinAudio();
+ }
+ }
+ })
+ })
}}
disabled={disable}
className={styles.joinButton}
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
index f624e063c0..7e01ac6f20 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
@@ -51,7 +51,9 @@ export default withTracker((props) => {
amIModerator: amIModerator(),
isMeteorConnected,
isUserInBreakoutRoom,
- exitAudio: () => AudioManager.exitAudio(),
+ exitAudio: () => AudioManager.forceExitAudio(),
+ joinMicrophone: () => AudioManager.joinMicrophone(),
+ joinListenOnly: () => AudioManager.joinListenOnly(),
isReconnecting,
setBreakoutAudioTransferStatus,
getBreakoutAudioTransferStatus,
From 507ec568d23cb6b6cf63ff5a07ce89603683c541 Mon Sep 17 00:00:00 2001
From: Joao Victor
Date: Wed, 9 Feb 2022 11:43:54 -0300
Subject: [PATCH 005/180] Error handling and variable name change
---
.../audio/audio-controls/container.jsx | 2 +-
.../ui/components/breakout-room/component.jsx | 18 ++++++++++++++----
.../ui/components/breakout-room/container.jsx | 2 +-
3 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx
index 2e6bda4c36..108f1351da 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx
@@ -64,7 +64,7 @@ const handleLeaveAudio = () => {
Storage.setItem('getEchoTest', true);
}
- Service.exitAudio();
+ Service.forceExitAudio();
logger.info({
logCode: 'audiocontrols_leave_audio',
extraInfo: { logType: 'user_action' },
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
index cc37b69665..9812aa6041 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
@@ -269,7 +269,7 @@ class BreakoutRoom extends PureComponent {
amIModerator,
intl,
isUserInBreakoutRoom,
- exitAudio,
+ forceExitAudio,
joinMicrophone,
joinListenOnly,
setBreakoutAudioTransferStatus,
@@ -340,7 +340,7 @@ class BreakoutRoom extends PureComponent {
this.getBreakoutURL(breakoutId);
// leave main room's audio,
// and stops video and screenshare when joining a breakout room
- exitAudio();
+ forceExitAudio();
logger.info({
logCode: 'breakoutroom_join',
extraInfo: { logType: 'user_action' },
@@ -363,9 +363,19 @@ class BreakoutRoom extends PureComponent {
const rejoinAudio = () => {
if (didUserSelectedMicrophone()) {
- joinMicrophone();
+ joinMicrophone().catch(() => {
+ logger.info({
+ logCode: 'mainroom_audio_rejoin',
+ extraInfo: { logType: 'user_action' },
+ }, 'leaving breakout room rejoined audio in the main room');
+ });
} else if (didUserSelectedListenOnly()) {
- joinListenOnly();
+ joinListenOnly().catch(() => {
+ logger.info({
+ logCode: 'mainroom_audio_rejoin',
+ extraInfo: { logType: 'user_action' },
+ }, 'leaving breakout room rejoined audio in the main room');
+ });
}
c.stop();
}
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
index 7e01ac6f20..e1756d980f 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
@@ -51,7 +51,7 @@ export default withTracker((props) => {
amIModerator: amIModerator(),
isMeteorConnected,
isUserInBreakoutRoom,
- exitAudio: () => AudioManager.forceExitAudio(),
+ forceExitAudio: () => AudioManager.forceExitAudio(),
joinMicrophone: () => AudioManager.joinMicrophone(),
joinListenOnly: () => AudioManager.joinListenOnly(),
isReconnecting,
From 9db73b8aac0fae6925c9d8589a5177b9eaa8675b Mon Sep 17 00:00:00 2001
From: Joao Victor
Date: Wed, 9 Feb 2022 14:03:35 -0300
Subject: [PATCH 006/180] Fix error log and add some linting
---
.../ui/components/breakout-room/component.jsx | 20 +++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
index 9812aa6041..7a36ed8603 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
@@ -17,7 +17,7 @@ import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
import {
didUserSelectedMicrophone,
- didUserSelectedListenOnly
+ didUserSelectedListenOnly,
} from '/imports/ui/components/audio/audio-modal/service';
const intlMessages = defineMessages({
@@ -364,17 +364,17 @@ class BreakoutRoom extends PureComponent {
const rejoinAudio = () => {
if (didUserSelectedMicrophone()) {
joinMicrophone().catch(() => {
- logger.info({
+ logger.warn({
logCode: 'mainroom_audio_rejoin',
extraInfo: { logType: 'user_action' },
- }, 'leaving breakout room rejoined audio in the main room');
+ }, 'leaving breakout room couldn\'t rejoin audio in the main room');
});
} else if (didUserSelectedListenOnly()) {
joinListenOnly().catch(() => {
- logger.info({
+ logger.warn({
logCode: 'mainroom_audio_rejoin',
extraInfo: { logType: 'user_action' },
- }, 'leaving breakout room rejoined audio in the main room');
+ }, 'leaving breakout room couldn\'t rejoin audio in the main room');
});
}
c.stop();
@@ -382,17 +382,17 @@ class BreakoutRoom extends PureComponent {
query.observe({
added: (user) => {
- if (user?.extId?.startsWith(Auth.userID) && user?.loggedOut) {
+ if (user?.loggedOut && user?.extId?.startsWith(Auth.userID)) {
rejoinAudio();
}
},
changed: (user) => {
- if (user?.extId?.startsWith(Auth.userID) && user?.loggedOut) {
+ if (user?.loggedOut && user?.extId?.startsWith(Auth.userID)) {
rejoinAudio();
}
- }
- })
- })
+ },
+ });
+ });
}}
disabled={disable}
className={styles.joinButton}
From 5128a773a7e72487d4e27ee5248815d5f8523dc8 Mon Sep 17 00:00:00 2001
From: Joao Victor
Date: Thu, 10 Feb 2022 09:30:43 -0300
Subject: [PATCH 007/180] Mute user after rejoining audio
---
.../ui/components/breakout-room/component.jsx | 27 ++++++++++++-------
1 file changed, 17 insertions(+), 10 deletions(-)
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
index 7a36ed8603..37aa861d34 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
@@ -15,6 +15,7 @@ import UserListService from '/imports/ui/components/user-list/service';
import AudioManager from '/imports/ui/services/audio-manager';
import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
+import { makeCall } from '/imports/ui/services/api';
import {
didUserSelectedMicrophone,
didUserSelectedListenOnly,
@@ -361,24 +362,30 @@ class BreakoutRoom extends PureComponent {
},
});
+ const logMessage = () => {
+ logger.warn({
+ logCode: 'mainroom_audio_rejoin',
+ extraInfo: { logType: 'user_action' },
+ }, 'leaving breakout room couldn\'t rejoin audio in the main room');
+ };
+
const rejoinAudio = () => {
if (didUserSelectedMicrophone()) {
- joinMicrophone().catch(() => {
- logger.warn({
- logCode: 'mainroom_audio_rejoin',
- extraInfo: { logType: 'user_action' },
- }, 'leaving breakout room couldn\'t rejoin audio in the main room');
+ joinMicrophone().then(() => {
+ makeCall('toggleVoice', null, true).catch(() => {
+ forceExitAudio();
+ logMessage();
+ });
+ }).catch(() => {
+ logMessage();
});
} else if (didUserSelectedListenOnly()) {
joinListenOnly().catch(() => {
- logger.warn({
- logCode: 'mainroom_audio_rejoin',
- extraInfo: { logType: 'user_action' },
- }, 'leaving breakout room couldn\'t rejoin audio in the main room');
+ logMessage();
});
}
c.stop();
- }
+ };
query.observe({
added: (user) => {
From 3e7a44033bc2743abda867a643b39d264ec0adea Mon Sep 17 00:00:00 2001
From: Joao Victor
Date: Fri, 11 Feb 2022 13:27:34 -0300
Subject: [PATCH 008/180] Move out rejoinAudio function to container
---
.../ui/components/breakout-room/component.jsx | 35 ++-----------------
.../ui/components/breakout-room/container.jsx | 31 ++++++++++++++--
2 files changed, 32 insertions(+), 34 deletions(-)
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
index 37aa861d34..f6df8e2f28 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
@@ -15,11 +15,6 @@ import UserListService from '/imports/ui/components/user-list/service';
import AudioManager from '/imports/ui/services/audio-manager';
import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
-import { makeCall } from '/imports/ui/services/api';
-import {
- didUserSelectedMicrophone,
- didUserSelectedListenOnly,
-} from '/imports/ui/components/audio/audio-modal/service';
const intlMessages = defineMessages({
breakoutTitle: {
@@ -271,8 +266,7 @@ class BreakoutRoom extends PureComponent {
intl,
isUserInBreakoutRoom,
forceExitAudio,
- joinMicrophone,
- joinListenOnly,
+ rejoinAudio,
setBreakoutAudioTransferStatus,
getBreakoutAudioTransferStatus,
} = this.props;
@@ -362,40 +356,17 @@ class BreakoutRoom extends PureComponent {
},
});
- const logMessage = () => {
- logger.warn({
- logCode: 'mainroom_audio_rejoin',
- extraInfo: { logType: 'user_action' },
- }, 'leaving breakout room couldn\'t rejoin audio in the main room');
- };
-
- const rejoinAudio = () => {
- if (didUserSelectedMicrophone()) {
- joinMicrophone().then(() => {
- makeCall('toggleVoice', null, true).catch(() => {
- forceExitAudio();
- logMessage();
- });
- }).catch(() => {
- logMessage();
- });
- } else if (didUserSelectedListenOnly()) {
- joinListenOnly().catch(() => {
- logMessage();
- });
- }
- c.stop();
- };
-
query.observe({
added: (user) => {
if (user?.loggedOut && user?.extId?.startsWith(Auth.userID)) {
rejoinAudio();
+ c.stop();
}
},
changed: (user) => {
if (user?.loggedOut && user?.extId?.startsWith(Auth.userID)) {
rejoinAudio();
+ c.stop();
}
},
});
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
index e1756d980f..8658e3190b 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
@@ -5,6 +5,10 @@ import AudioManager from '/imports/ui/services/audio-manager';
import BreakoutComponent from './component';
import Service from './service';
import LayoutContext from '../layout/context';
+import {
+ didUserSelectedMicrophone,
+ didUserSelectedListenOnly,
+} from '/imports/ui/components/audio/audio-modal/service';
const BreakoutContainer = (props) => {
const layoutContext = useContext(LayoutContext);
@@ -36,6 +40,30 @@ export default withTracker((props) => {
getBreakoutAudioTransferStatus,
} = AudioService;
+ const logMessage = () => {
+ logger.warn({
+ logCode: 'mainroom_audio_rejoin',
+ extraInfo: { logType: 'user_action' },
+ }, 'leaving breakout room couldn\'t rejoin audio in the main room');
+ };
+
+ const rejoinAudio = () => {
+ if (didUserSelectedMicrophone()) {
+ AudioManager.joinMicrophone().then(() => {
+ makeCall('toggleVoice', null, true).catch(() => {
+ forceExitAudio();
+ logMessage();
+ });
+ }).catch(() => {
+ logMessage();
+ });
+ } else if (didUserSelectedListenOnly()) {
+ AudioManager.joinListenOnly().catch(() => {
+ logMessage();
+ });
+ }
+ };
+
return {
...props,
breakoutRooms,
@@ -52,8 +80,7 @@ export default withTracker((props) => {
isMeteorConnected,
isUserInBreakoutRoom,
forceExitAudio: () => AudioManager.forceExitAudio(),
- joinMicrophone: () => AudioManager.joinMicrophone(),
- joinListenOnly: () => AudioManager.joinListenOnly(),
+ rejoinAudio,
isReconnecting,
setBreakoutAudioTransferStatus,
getBreakoutAudioTransferStatus,
From 88930d67764ff22632a227a6df81f3e7ffa8780a Mon Sep 17 00:00:00 2001
From: Joao Victor
Date: Fri, 11 Feb 2022 13:32:02 -0300
Subject: [PATCH 009/180] Rename function for logging
---
.../imports/ui/components/breakout-room/container.jsx | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
index 8658e3190b..1c118f5af7 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
@@ -40,7 +40,7 @@ export default withTracker((props) => {
getBreakoutAudioTransferStatus,
} = AudioService;
- const logMessage = () => {
+ const logUserCouldNotRejoinAudio = () => {
logger.warn({
logCode: 'mainroom_audio_rejoin',
extraInfo: { logType: 'user_action' },
@@ -52,14 +52,14 @@ export default withTracker((props) => {
AudioManager.joinMicrophone().then(() => {
makeCall('toggleVoice', null, true).catch(() => {
forceExitAudio();
- logMessage();
+ logUserCouldNotRejoinAudio();
});
}).catch(() => {
- logMessage();
+ logUserCouldNotRejoinAudio();
});
} else if (didUserSelectedListenOnly()) {
AudioManager.joinListenOnly().catch(() => {
- logMessage();
+ logUserCouldNotRejoinAudio();
});
}
};
From 45a873876f837bde24d122f16fea29b784377033 Mon Sep 17 00:00:00 2001
From: Joao Victor
Date: Fri, 11 Feb 2022 13:37:22 -0300
Subject: [PATCH 010/180] Add import and fix function call
---
.../imports/ui/components/breakout-room/container.jsx | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
index 1c118f5af7..79e64eece3 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
@@ -9,6 +9,7 @@ import {
didUserSelectedMicrophone,
didUserSelectedListenOnly,
} from '/imports/ui/components/audio/audio-modal/service';
+import { makeCall } from '/imports/ui/services/api';
const BreakoutContainer = (props) => {
const layoutContext = useContext(LayoutContext);
@@ -51,7 +52,7 @@ export default withTracker((props) => {
if (didUserSelectedMicrophone()) {
AudioManager.joinMicrophone().then(() => {
makeCall('toggleVoice', null, true).catch(() => {
- forceExitAudio();
+ AudioManager.forceExitAudio();
logUserCouldNotRejoinAudio();
});
}).catch(() => {
From e9cd895c298d92d97212d79354d68a237abf41f2 Mon Sep 17 00:00:00 2001
From: Joao Victor
Date: Fri, 11 Feb 2022 13:45:55 -0300
Subject: [PATCH 011/180] Fix duplicated function
---
.../ui/components/breakout-room/component.jsx | 21 ++++++++-----------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
index f6df8e2f28..4098a11821 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
@@ -356,19 +356,16 @@ class BreakoutRoom extends PureComponent {
},
});
+ const observeLogOut = (user) => {
+ if (user?.loggedOut && user?.extId?.startsWith(Auth.userID)) {
+ rejoinAudio();
+ c.stop();
+ }
+ }
+
query.observe({
- added: (user) => {
- if (user?.loggedOut && user?.extId?.startsWith(Auth.userID)) {
- rejoinAudio();
- c.stop();
- }
- },
- changed: (user) => {
- if (user?.loggedOut && user?.extId?.startsWith(Auth.userID)) {
- rejoinAudio();
- c.stop();
- }
- },
+ added: observeLogOut,
+ changed: observeLogOut,
});
});
}}
From 68346a8ac9bab53c4b25dccada1fddf11b9d064d Mon Sep 17 00:00:00 2001
From: Tainan Felipe
Date: Wed, 16 Feb 2022 15:46:02 -0300
Subject: [PATCH 012/180] Rename the message to userLeftFlagUpdated
---
...UserJoinMeetingAfterReconnectReqMsgHdlr.scala | 2 +-
.../apps/users/UserJoinMeetingReqMsgHdlr.scala | 2 +-
.../core/apps/users/UserLeaveReqMsgHdlr.scala | 2 +-
.../core/running/HandlerHelpers.scala | 4 ++--
.../core2/message/senders/MsgBuilder.scala | 10 +++++-----
.../bigbluebutton/common2/msgs/UsersMsgs.scala | 16 ++++++++--------
.../imports/api/users/server/eventHandlers.js | 4 ++--
.../{userLeftFlag.js => userLeftFlagUpdated.js} | 2 +-
.../{userLeftFlag.js => userLeftFlagUpdated.js} | 2 +-
9 files changed, 22 insertions(+), 22 deletions(-)
rename bigbluebutton-html5/imports/api/users/server/handlers/{userLeftFlag.js => userLeftFlagUpdated.js} (77%)
rename bigbluebutton-html5/imports/api/users/server/modifiers/{userLeftFlag.js => userLeftFlagUpdated.js} (86%)
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala
index 4d32b49abc..670d3907d4 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala
@@ -22,7 +22,7 @@ trait UserJoinMeetingAfterReconnectReqMsgHdlr extends HandlerHelpers with UserJo
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
// User has reconnected. Just reset it's flag. ralam Oct 23, 2018
println(Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId))
- sendUserLeftFlagEvtMsg(outGW, liveMeeting, msg.body.userId, false);
+ sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false);
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
}
state
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala
index 585632f442..790bccb72f 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala
@@ -21,7 +21,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
if (reconnectingUser.userLeftFlag.left) {
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
// User has reconnected. Just reset it's flag. ralam Oct 23, 2018
- sendUserLeftFlagEvtMsg(outGW, liveMeeting, msg.body.userId, false);
+ sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false);
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala
index 054afb1be1..9da0080f5c 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala
@@ -19,7 +19,7 @@ trait UserLeaveReqMsgHdlr extends HandlerHelpers {
// Just flag that user has left as the user might be reconnecting.
// An audit will remove this user if it hasn't rejoined after a certain period of time.
// ralam oct 23, 2018
- sendUserLeftFlagEvtMsg(outGW, liveMeeting, msg.body.userId, true);
+ sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, true);
Users2x.setUserLeftFlag(liveMeeting.users2x, msg.body.userId)
}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala
index af320f7937..f2ed221aaa 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala
@@ -38,7 +38,7 @@ trait HandlerHelpers extends SystemConfiguration {
}
}
- def sendUserLeftFlagEvtMsg(
+ def sendUserLeftFlagUpdatedEvtMsg(
outGW: OutMsgRouter,
liveMeeting: LiveMeeting,
intId: String,
@@ -47,7 +47,7 @@ trait HandlerHelpers extends SystemConfiguration {
for {
u <- Users2x.findWithIntId(liveMeeting.users2x, intId)
} yield {
- val userLeftFlagMeetingEvent = MsgBuilder.buildUserLeftFlagEvtMsg(liveMeeting.props.meetingProp.intId, u.intId, leftFlag)
+ val userLeftFlagMeetingEvent = MsgBuilder.buildUserLeftFlagUpdatedEvtMsg(liveMeeting.props.meetingProp.intId, u.intId, leftFlag)
outGW.send(userLeftFlagMeetingEvent)
}
}
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 c4ce2054ec..a67f205e62 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
@@ -309,12 +309,12 @@ object MsgBuilder {
BbbCommonEnvCoreMsg(envelope, event)
}
- def buildUserLeftFlagEvtMsg(meetingId: String, userId: String, userLeftFlag: Boolean): BbbCommonEnvCoreMsg = {
+ def buildUserLeftFlagUpdatedEvtMsg(meetingId: String, userId: String, userLeftFlag: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
- val envelope = BbbCoreEnvelope(UserLeftFlagEvtMsg.NAME, routing)
- val header = BbbClientMsgHeader(UserLeftFlagEvtMsg.NAME, meetingId, userId)
- val body = UserLeftFlagEvtMsgBody(userId, userLeftFlag)
- val event = UserLeftFlagEvtMsg(header, body)
+ val envelope = BbbCoreEnvelope(UserLeftFlagUpdatedEvtMsg.NAME, routing)
+ val header = BbbClientMsgHeader(UserLeftFlagUpdatedEvtMsg.NAME, meetingId, userId)
+ val body = UserLeftFlagUpdatedEvtMsgBody(userId, userLeftFlag)
+ val event = UserLeftFlagUpdatedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/UsersMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/UsersMsgs.scala
index 1209ec94fb..be583273f7 100755
--- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/UsersMsgs.scala
+++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/UsersMsgs.scala
@@ -77,19 +77,19 @@ case class UserLeftMeetingEvtMsg(
) extends BbbCoreMsg
case class UserLeftMeetingEvtMsgBody(intId: String)
-object UserLeftFlagEvtMsg {
- val NAME = "UserLeftFlagEvtMsg"
- def apply(meetingId: String, userId: String, body: UserLeftFlagEvtMsgBody): UserLeftFlagEvtMsg = {
- val header = BbbClientMsgHeader(UserLeftFlagEvtMsg.NAME, meetingId, userId)
- UserLeftFlagEvtMsg(header, body)
+object UserLeftFlagUpdatedEvtMsg {
+ val NAME = "UserLeftFlagUpdatedEvtMsg"
+ def apply(meetingId: String, userId: String, body: UserLeftFlagUpdatedEvtMsgBody): UserLeftFlagUpdatedEvtMsg = {
+ val header = BbbClientMsgHeader(UserLeftFlagUpdatedEvtMsg.NAME, meetingId, userId)
+ UserLeftFlagUpdatedEvtMsg(header, body)
}
}
-case class UserLeftFlagEvtMsg(
+case class UserLeftFlagUpdatedEvtMsg(
header: BbbClientMsgHeader,
- body: UserLeftFlagEvtMsgBody
+ body: UserLeftFlagUpdatedEvtMsgBody
) extends BbbCoreMsg
-case class UserLeftFlagEvtMsgBody(intId: String, userLeftFlag: Boolean)
+case class UserLeftFlagUpdatedEvtMsgBody(intId: String, userLeftFlag: Boolean)
object UserJoinedMeetingEvtMsg {
val NAME = "UserJoinedMeetingEvtMsg"
diff --git a/bigbluebutton-html5/imports/api/users/server/eventHandlers.js b/bigbluebutton-html5/imports/api/users/server/eventHandlers.js
index df0298c4c0..4ee8f23acc 100644
--- a/bigbluebutton-html5/imports/api/users/server/eventHandlers.js
+++ b/bigbluebutton-html5/imports/api/users/server/eventHandlers.js
@@ -1,7 +1,7 @@
import RedisPubSub from '/imports/startup/server/redis';
import handleRemoveUser from './handlers/removeUser';
import handleUserJoined from './handlers/userJoined';
-import handleUserLeftFlag from './handlers/userLeftFlag';
+import handleUserLeftFlagUpdated from './handlers/userLeftFlagUpdated';
import handleValidateAuthToken from './handlers/validateAuthToken';
import handlePresenterAssigned from './handlers/presenterAssigned';
import handleEmojiStatus from './handlers/emojiStatus';
@@ -17,6 +17,6 @@ RedisPubSub.on('ValidateAuthTokenRespMsg', handleValidateAuthToken);
RedisPubSub.on('UserEmojiChangedEvtMsg', handleEmojiStatus);
RedisPubSub.on('UserEjectedFromMeetingEvtMsg', handleUserEjected);
RedisPubSub.on('UserRoleChangedEvtMsg', handleChangeRole);
-RedisPubSub.on('UserLeftFlagEvtMsg', handleUserLeftFlag);
+RedisPubSub.on('UserLeftFlagUpdatedEvtMsg', handleUserLeftFlagUpdated);
RedisPubSub.on('UserPinStateChangedEvtMsg', handleUserPinChanged);
RedisPubSub.on('UserInactivityInspectMsg', handleUserInactivityInspect);
diff --git a/bigbluebutton-html5/imports/api/users/server/handlers/userLeftFlag.js b/bigbluebutton-html5/imports/api/users/server/handlers/userLeftFlagUpdated.js
similarity index 77%
rename from bigbluebutton-html5/imports/api/users/server/handlers/userLeftFlag.js
rename to bigbluebutton-html5/imports/api/users/server/handlers/userLeftFlagUpdated.js
index f5f784982f..17abfb89af 100644
--- a/bigbluebutton-html5/imports/api/users/server/handlers/userLeftFlag.js
+++ b/bigbluebutton-html5/imports/api/users/server/handlers/userLeftFlagUpdated.js
@@ -1,6 +1,6 @@
import { check } from 'meteor/check';
-import userLeftFlag from '../modifiers/userLeftFlag';
+import userLeftFlag from '../modifiers/userLeftFlagUpdated';
export default function handleUserLeftFlag({ body }, meetingId) {
const user = body;
diff --git a/bigbluebutton-html5/imports/api/users/server/modifiers/userLeftFlag.js b/bigbluebutton-html5/imports/api/users/server/modifiers/userLeftFlagUpdated.js
similarity index 86%
rename from bigbluebutton-html5/imports/api/users/server/modifiers/userLeftFlag.js
rename to bigbluebutton-html5/imports/api/users/server/modifiers/userLeftFlagUpdated.js
index 4cd1b4130d..ae157ec9bb 100644
--- a/bigbluebutton-html5/imports/api/users/server/modifiers/userLeftFlag.js
+++ b/bigbluebutton-html5/imports/api/users/server/modifiers/userLeftFlagUpdated.js
@@ -1,7 +1,7 @@
import Logger from '/imports/startup/server/logger';
import Users from '/imports/api/users';
-export default function userLeftFlag(meetingId, userId, left) {
+export default function userLeftFlagUpdated(meetingId, userId, left) {
const selector = {
meetingId,
userId,
From e798d31df28deaa7ee89340cf25353447a331362 Mon Sep 17 00:00:00 2001
From: Guilherme Leme
Date: Fri, 4 Mar 2022 16:40:59 -0300
Subject: [PATCH 013/180] [issue-14321] - Fixed the sudden change of
presentations when clicking on the confirm button.
---
.../presentation/presentation-uploader/component.jsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/component.jsx
index 1d5242feff..535704a21c 100755
--- a/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/component.jsx
@@ -300,8 +300,8 @@ class PresentationUploader extends Component {
this.setState({
presentations: Object.values({
- ...propPresentations,
...presentations,
+ ...propPresentations,
}),
});
}
@@ -548,8 +548,8 @@ class PresentationUploader extends Component {
const { presentations: propPresentations } = this.props;
const ids = new Set(propPresentations.map((d) => d.ID));
const merged = [
- ...propPresentations,
...presentations.filter((d) => !ids.has(d.ID)),
+ ...propPresentations,
];
this.setState(
{ presentations: merged },
From ab869b42217559aeada60a55c2ef116f7c27b3e7 Mon Sep 17 00:00:00 2001
From: Joao Victor
Date: Wed, 9 Mar 2022 16:08:56 -0300
Subject: [PATCH 014/180] Prevent redirect after feedback of user
---
.../ui/components/meeting-ended/component.jsx | 32 +++++--------------
1 file changed, 8 insertions(+), 24 deletions(-)
diff --git a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
index bce1e038ba..141b4cd666 100755
--- a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
@@ -174,14 +174,8 @@ class MeetingEnded extends PureComponent {
}
confirmRedirect() {
- const {
- selected,
- } = this.state;
-
- if (selected <= 0) {
- if (meetingIsBreakout()) window.close();
- if (allowRedirectToLogoutURL()) logoutRouteHandler();
- }
+ if (meetingIsBreakout()) window.close();
+ if (allowRedirectToLogoutURL()) logoutRouteHandler();
}
getEndingMessage() {
@@ -236,18 +230,9 @@ class MeetingEnded extends PureComponent {
dispatched: true,
});
- if (allowRedirectToLogoutURL()) {
- const FEEDBACK_WAIT_TIME = 500;
- setTimeout(() => {
- fetch(url, options)
- .then(() => {
- logoutRouteHandler();
- })
- .catch(() => {
- logoutRouteHandler();
- });
- }, FEEDBACK_WAIT_TIME);
- }
+ fetch(url, options).catch((e) => {
+ logger.warn(e);
+ });
}
renderNoFeedback() {
@@ -309,7 +294,6 @@ class MeetingEnded extends PureComponent {
const { intl, code, ejectedReason } = this.props;
const {
selected,
- dispatched,
} = this.state;
const noRating = selected <= 0;
@@ -347,17 +331,17 @@ class MeetingEnded extends PureComponent {
) : null}
) : null}
- {noRating && allowRedirectToLogoutURL() ? (
+ {noRating ? (
) : null}
-
-
-
{intl.formatMessage(intlMessages.optionTitle)}
- {
- buttonsData.map((buttonData) => renderButton(
- intl.formatMessage(buttonData.messageId),
- buttonData,
- ))
- }
-
+
+
{intl.formatMessage(intlMessages.optionTitle)}
+ {
+ buttonsData.map((buttonData) => renderButton(
+ intl.formatMessage(buttonData.messageId),
+ buttonData,
+ ))
+ }
{allowRememberChoice ? (
diff --git a/bigbluebutton-html5/imports/ui/components/waiting-users/styles.scss b/bigbluebutton-html5/imports/ui/components/waiting-users/styles.scss
index 7cde5272df..6bf4e2239b 100644
--- a/bigbluebutton-html5/imports/ui/components/waiting-users/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/waiting-users/styles.scss
@@ -87,6 +87,12 @@
margin: .3rem 0;
font-weight: 400;
font-size: var(--font-size-base);
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.moderatorActions {
+ padding: 0 .2rem;
}
.mainTitle {
From 43318d3a88c1e951d32664418b33caeb930f7af7 Mon Sep 17 00:00:00 2001
From: Paulo Lanzarin <4529051+prlanzarin@users.noreply.github.com>
Date: Fri, 18 Mar 2022 10:50:25 -0300
Subject: [PATCH 022/180] build: bump bbb-webrtc-sfu to v2.6.11
---
bbb-webrtc-sfu.placeholder.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bbb-webrtc-sfu.placeholder.sh b/bbb-webrtc-sfu.placeholder.sh
index 4ba8f2f671..3dbf98b019 100644
--- a/bbb-webrtc-sfu.placeholder.sh
+++ b/bbb-webrtc-sfu.placeholder.sh
@@ -1 +1 @@
-git clone --branch v2.6.10 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu
+git clone --branch v2.6.11 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu
From c798eee1f7c2996d8a22577ea249539b93ac0f02 Mon Sep 17 00:00:00 2001
From: Guilherme Leme
Date: Fri, 18 Mar 2022 12:16:27 -0300
Subject: [PATCH 023/180] [issue-14304] - Implemented reading of
`/etc/bigbluebutton/recording.yml` and `/etc/bigbluebutton/presentation.yml`
---
record-and-playback/core/lib/recordandplayback.rb | 11 ++++++++++-
.../presentation/scripts/caption/presentation | 7 +++++++
.../presentation/scripts/process/presentation.rb | 7 +++++++
.../presentation/scripts/publish/presentation.rb | 6 ++++++
4 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/record-and-playback/core/lib/recordandplayback.rb b/record-and-playback/core/lib/recordandplayback.rb
index 8a4526fcb2..b7b5fdded0 100644
--- a/record-and-playback/core/lib/recordandplayback.rb
+++ b/record-and-playback/core/lib/recordandplayback.rb
@@ -240,8 +240,17 @@ module BigBlueButton
def self.read_props
return @props if @props
+ filepathRecOverride = "/etc/bigbluebutton/recording.yml"
+ isThereOverride = File.file?(filepathRecOverride)
+
filepath = File.join(BigBlueButton.rap_scripts_path, 'bigbluebutton.yml')
- @props = YAML::load(File.open(filepath))
+ props = YAML::load(File.open(filepath))
+ if (isThereOverride)
+ recOverrideProps = YAML::load(File.open(filepathRecOverride))
+ @props = props.merge(recOverrideProps)
+ else
+ @props = props
+ end
end
def self.create_redis_publisher
diff --git a/record-and-playback/presentation/scripts/caption/presentation b/record-and-playback/presentation/scripts/caption/presentation
index 8f991b760e..d9be5f6e88 100755
--- a/record-and-playback/presentation/scripts/caption/presentation
+++ b/record-and-playback/presentation/scripts/caption/presentation
@@ -47,6 +47,13 @@ presentation_props = File.open(File.expand_path('../presentation.yml', __dir__))
YAML.safe_load(pres_yml)
end
+filepathPresOverride = "/etc/bigbluebutton/presentation.yml"
+isThereOverride = File.file?(filepathPresOverride)
+if (isThereOverride)
+ presOverrideProps = YAML::load(File.open(filepathPresOverride))
+ presentation_props = presentation_props.merge(presOverrideProps)
+end
+
logger = Journald::Logger.new('caption/presentation')
logger.tag(record_id: recording_id)
diff --git a/record-and-playback/presentation/scripts/process/presentation.rb b/record-and-playback/presentation/scripts/process/presentation.rb
index 557947034d..91af81db2f 100755
--- a/record-and-playback/presentation/scripts/process/presentation.rb
+++ b/record-and-playback/presentation/scripts/process/presentation.rb
@@ -41,6 +41,13 @@ meeting_id = opts[:meeting_id]
# This script lives in scripts/archive/steps while properties.yaml lives in scripts/
props = BigBlueButton.read_props
presentation_props = YAML.safe_load(File.open('presentation.yml'))
+filepathPresOverride = "/etc/bigbluebutton/presentation.yml"
+isThereOverride = File.file?(filepathPresOverride)
+if (isThereOverride)
+ presOverrideProps = YAML::load(File.open(filepathPresOverride))
+ presentation_props = presentation_props.merge(presOverrideProps)
+end
+
presentation_props['audio_offset'] = 0 if presentation_props['audio_offset'].nil?
presentation_props['include_deskshare'] = false if presentation_props['include_deskshare'].nil?
diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb
index 7d370b3b5b..9e4a72397d 100755
--- a/record-and-playback/presentation/scripts/publish/presentation.rb
+++ b/record-and-playback/presentation/scripts/publish/presentation.rb
@@ -32,6 +32,12 @@ require 'json'
# This script lives in scripts/archive/steps while properties.yaml lives in scripts/
bbb_props = BigBlueButton.read_props
$presentation_props = YAML::load(File.read('presentation.yml'))
+filepathPresOverride = "/etc/bigbluebutton/presentation.yml"
+isThereOverride = File.file?(filepathPresOverride)
+if (isThereOverride)
+ presOverrideProps = YAML::load(File.open(filepathPresOverride))
+ $presentation_props = $presentation_props.merge(presOverrideProps)
+end
# There's a couple of places where stuff is mysteriously divided or multiplied
# by 2. This is just here to call out how spooky that is.
From f72981f1a8f08fc75706cdb4b6f370a656e5976d Mon Sep 17 00:00:00 2001
From: gabriellpr
Date: Tue, 15 Mar 2022 17:01:21 -0300
Subject: [PATCH 024/180] Implements a progress bar to the external video for
the viewer.
---
.../external-video-player/component.jsx | 24 ++++++++++++++++--
.../external-video-player/styles.scss | 25 +++++++++++++++++++
2 files changed, 47 insertions(+), 2 deletions(-)
diff --git a/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx b/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx
index 22688dcc32..c9de7e4da2 100644
--- a/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx
@@ -77,6 +77,8 @@ class VideoPlayer extends Component {
volume: 1,
playbackRate: 1,
key: 0,
+ played:0,
+ loaded:0,
};
this.hideVolume = {
@@ -299,12 +301,16 @@ class VideoPlayer extends Component {
}
}
- handleOnProgress() {
+ handleOnProgress(data) {
const { mutedByEchoTest } = this.state;
const volume = this.getCurrentVolume();
const muted = this.getMuted();
+ const { played, loaded } = data;
+
+ this.setState({played, loaded});
+
if (!mutedByEchoTest) {
this.setState({ volume, muted });
}
@@ -547,7 +553,7 @@ class VideoPlayer extends Component {
const {
playing, playbackRate, mutedByEchoTest, autoPlayBlocked,
- volume, muted, key, showHoverToolBar,
+ volume, muted, key, showHoverToolBar, played, loaded
} = this.state;
// This looks weird, but I need to get this nested player
@@ -615,6 +621,20 @@ class VideoPlayer extends Component {
{
!isPresenter
? [
+ (
+
+ ),
(
& {
+ display: block;
+ }
+
+ height: 5px;
+ width: 100%;
+
+ background-color: transparent;
+}
+
+.loaded {
+ height: 100%;
+
+ background-color: gray;
+}
+
+.played {
+ height: 100%;
+
+ background-color: #DF2721;
+}
From b3320c2414889de19cadffb5589437d797ae29a6 Mon Sep 17 00:00:00 2001
From: Guilherme Leme
Date: Mon, 21 Mar 2022 10:47:01 -0300
Subject: [PATCH 025/180] [issue-14304] - changes in review.
---
record-and-playback/core/lib/recordandplayback.rb | 10 ++++------
.../presentation/scripts/process/presentation.rb | 4 ++--
.../presentation/scripts/publish/presentation.rb | 4 ++--
3 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/record-and-playback/core/lib/recordandplayback.rb b/record-and-playback/core/lib/recordandplayback.rb
index b7b5fdded0..4c5cc227c8 100644
--- a/record-and-playback/core/lib/recordandplayback.rb
+++ b/record-and-playback/core/lib/recordandplayback.rb
@@ -241,15 +241,13 @@ module BigBlueButton
return @props if @props
filepathRecOverride = "/etc/bigbluebutton/recording.yml"
- isThereOverride = File.file?(filepathRecOverride)
+ hasOverride = File.file?(filepathRecOverride)
filepath = File.join(BigBlueButton.rap_scripts_path, 'bigbluebutton.yml')
- props = YAML::load(File.open(filepath))
- if (isThereOverride)
+ @props = YAML::load(File.open(filepath))
+ if (hasOverride)
recOverrideProps = YAML::load(File.open(filepathRecOverride))
- @props = props.merge(recOverrideProps)
- else
- @props = props
+ @props = @props.merge(recOverrideProps)
end
end
diff --git a/record-and-playback/presentation/scripts/process/presentation.rb b/record-and-playback/presentation/scripts/process/presentation.rb
index 91af81db2f..72d11768fd 100755
--- a/record-and-playback/presentation/scripts/process/presentation.rb
+++ b/record-and-playback/presentation/scripts/process/presentation.rb
@@ -42,8 +42,8 @@ meeting_id = opts[:meeting_id]
props = BigBlueButton.read_props
presentation_props = YAML.safe_load(File.open('presentation.yml'))
filepathPresOverride = "/etc/bigbluebutton/presentation.yml"
-isThereOverride = File.file?(filepathPresOverride)
-if (isThereOverride)
+hasOverride = File.file?(filepathPresOverride)
+if (hasOverride)
presOverrideProps = YAML::load(File.open(filepathPresOverride))
presentation_props = presentation_props.merge(presOverrideProps)
end
diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb
index 9e4a72397d..55e14653d8 100755
--- a/record-and-playback/presentation/scripts/publish/presentation.rb
+++ b/record-and-playback/presentation/scripts/publish/presentation.rb
@@ -33,8 +33,8 @@ require 'json'
bbb_props = BigBlueButton.read_props
$presentation_props = YAML::load(File.read('presentation.yml'))
filepathPresOverride = "/etc/bigbluebutton/presentation.yml"
-isThereOverride = File.file?(filepathPresOverride)
-if (isThereOverride)
+hasOverride = File.file?(filepathPresOverride)
+if (hasOverride)
presOverrideProps = YAML::load(File.open(filepathPresOverride))
$presentation_props = $presentation_props.merge(presOverrideProps)
end
From 0469ee6a64abf4550a6bf1cb2bd83c65fe6040ce Mon Sep 17 00:00:00 2001
From: Anton B
Date: Mon, 21 Mar 2022 12:04:43 -0300
Subject: [PATCH 026/180] fix handleDownload with playwright
---
.../user-options/component.jsx | 1 +
bigbluebutton-tests/playwright/chat/chat.js | 28 +++++++++++++------
.../playwright/chat/chat.spec.js | 7 ++---
bigbluebutton-tests/playwright/chat/util.js | 6 ++--
.../playwright/core/elements.js | 1 +
bigbluebutton-tests/playwright/core/page.js | 20 +++++++++++--
bigbluebutton-tests/playwright/core/util.js | 11 ++++++++
.../playwright/presentation/presentation.js | 3 +-
.../presentation/presentation.spec.js | 4 +--
.../playwright/user/multiusers.js | 13 +++++++++
.../playwright/user/user.spec.js | 6 ++++
11 files changed, 79 insertions(+), 21 deletions(-)
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx
index f7b292fe93..217b1e6b9a 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx
@@ -281,6 +281,7 @@ class UserOptions extends PureComponent {
// description: ,
onClick: this.onSaveUserNames,
icon: 'download',
+ dataTest: 'downloadUserNamesList',
});
}
diff --git a/bigbluebutton-tests/playwright/chat/chat.js b/bigbluebutton-tests/playwright/chat/chat.js
index 4fa9861643..4a5f18c14a 100644
--- a/bigbluebutton-tests/playwright/chat/chat.js
+++ b/bigbluebutton-tests/playwright/chat/chat.js
@@ -3,6 +3,7 @@ const Page = require('../core/page');
const { openChat } = require('./util');
const p = require('../core/parameters');
const e = require('../core/elements');
+const { checkTextContent } = require('../core/util');
class Chat extends Page {
constructor(browser, page) {
@@ -10,7 +11,7 @@ class Chat extends Page {
}
async sendPublicMessage() {
- await openChat(this.page);
+ await openChat(this);
const message = this.getLocator(e.chatUserMessageText);
await expect(message).toHaveCount(0);
@@ -21,7 +22,7 @@ class Chat extends Page {
}
async clearChat() {
- await openChat(this.page);
+ await openChat(this);
const message = this.getLocator(e.chatUserMessageText);
await this.type(e.chatBox, e.message);
@@ -50,21 +51,30 @@ class Chat extends Page {
await this.waitForSelector(e.chatUserMessageText);
await this.waitAndClick(e.chatCopy);
// enable access to browser context clipboard
- await context.grantPermissions(['clipboard-write', 'clipboard-read'], { origin: process.env.BBB_SERVER_URL });
+ await context.grantPermissions(['clipboard-write', 'clipboard-read'], { origin: process.env.BBB_URL });
const copiedText = await this.page.evaluate(async () => navigator.clipboard.readText());
const check = copiedText.includes(`${p.fullName}: ${e.message}`);
expect(check).toBeTruthy();
}
- async saveChat() {
- await openChat(this.page);
+ async saveChat(testInfo) {
+ await openChat(this);
+ await this.type(e.chatBox, e.message);
+ await this.waitAndClick(e.sendButton);
+ await this.waitForSelector(e.chatUserMessageText);
await this.waitAndClick(e.chatOptions);
- await this.waitAndClick(e.chatSave);
- await this.page.waitForEvent('click');
+ const { content } = await this.handleDownload(e.chatSave, testInfo);
+
+ const dataToCheck = [
+ this.meetingId,
+ this.username,
+ e.message,
+ ];
+ await checkTextContent(content, dataToCheck);
}
async characterLimit() {
- await openChat(this.page);
+ await openChat(this);
const messageLocator = this.getLocator(e.chatUserMessageText);
await this.page.fill(e.chatBox, e.longMessage5000);
@@ -80,7 +90,7 @@ class Chat extends Page {
}
async emptyMessage() {
- await openChat(this.page);
+ await openChat(this);
const messageLocator = this.getLocator(e.chatUserMessageText);
await this.waitAndClick(e.sendButton);
diff --git a/bigbluebutton-tests/playwright/chat/chat.spec.js b/bigbluebutton-tests/playwright/chat/chat.spec.js
index 3f01d9aee5..301821ecc5 100644
--- a/bigbluebutton-tests/playwright/chat/chat.spec.js
+++ b/bigbluebutton-tests/playwright/chat/chat.spec.js
@@ -22,17 +22,16 @@ test.describe.parallel('Chat', () => {
});
test('Copy chat', async ({ browser, context, page }, testInfo) => {
- test.fixme(testInfo.config.projects[0].use.headless, 'Only works in headed mode');
+ test.fixme(testInfo.project.use.headless, 'Only works in headed mode');
const chat = new Chat(browser, page);
await chat.init(true, true);
await chat.copyChat(context);
});
- test.skip('Save chat', async ({ browser, page }) => {
- test.fixme();
+ test('Save chat', async ({ browser, page }, testInfo) => {
const chat = new Chat(browser, page);
await chat.init(true, true);
- await chat.saveChat();
+ await chat.saveChat(testInfo);
});
test('Verify character limit (5000 characters)', async ({ browser, page }) => {
diff --git a/bigbluebutton-tests/playwright/chat/util.js b/bigbluebutton-tests/playwright/chat/util.js
index cefb575443..f5a4ae2776 100644
--- a/bigbluebutton-tests/playwright/chat/util.js
+++ b/bigbluebutton-tests/playwright/chat/util.js
@@ -1,8 +1,8 @@
const e = require('../core/elements');
-async function openChat(page) {
- await page.waitForSelector(e.chatBox);
- await page.waitForSelector(e.chatMessages);
+async function openChat(test) {
+ await test.waitForSelector(e.chatBox);
+ await test.waitForSelector(e.chatMessages);
}
exports.openChat = openChat;
diff --git a/bigbluebutton-tests/playwright/core/elements.js b/bigbluebutton-tests/playwright/core/elements.js
index a19cd02667..a912a09435 100644
--- a/bigbluebutton-tests/playwright/core/elements.js
+++ b/bigbluebutton-tests/playwright/core/elements.js
@@ -217,6 +217,7 @@ exports.connectionStatusOfflineUser = 'div[data-test="offlineUser"]';
exports.connectionDataContainer = 'div[data-test="networkDataContainer"]';
exports.avatarsWrapperAvatar = 'div[data-test="avatarsWrapperAvatar"]';
exports.guestPolicyLabel = 'li[data-test="guestPolicyLabel"]';
+exports.downloadUserNamesList = 'li[data-test="downloadUserNamesList"]';
exports.waitingUsersBtn = 'div[data-test="waitingUsersBtn"]';
exports.joinMeetingDemoPage = 'div[class^="join-meeting"]';
exports.askModerator = 'button[data-test="askModerator"]';
diff --git a/bigbluebutton-tests/playwright/core/page.js b/bigbluebutton-tests/playwright/core/page.js
index f6ad2ac00c..55019bdd4e 100644
--- a/bigbluebutton-tests/playwright/core/page.js
+++ b/bigbluebutton-tests/playwright/core/page.js
@@ -2,7 +2,7 @@ require('dotenv').config();
const { expect } = require('@playwright/test');
const yaml = require('js-yaml');
const path = require('path');
-const fs = require('fs');
+const { readFileSync } = require('fs');
const parameters = require('./parameters');
const helpers = require('./helpers');
const e = require('./elements');
@@ -18,7 +18,7 @@ class Page {
async getSettingsYaml() {
try {
- return yaml.load(fs.readFileSync(path.join(__dirname, '../../../bigbluebutton-html5/private/config/settings.yml'), 'utf8'));
+ return yaml.load(readFileSync(path.join(__dirname, '../../../bigbluebutton-html5/private/config/settings.yml'), 'utf8'));
} catch (err) {
console.log(err);
}
@@ -46,6 +46,22 @@ class Page {
if (shouldCloseAudioModal) await this.closeAudioModal();
}
+ async handleDownload(selector, testInfo, timeout = ELEMENT_WAIT_TIME) {
+ const [download] = await Promise.all([
+ this.page.waitForEvent('download', { timeout }),
+ this.waitAndClick(selector, timeout),
+ ]);
+ await expect(download).toBeTruthy();
+ const path = await download.path();
+ const content = await readFileSync(path, 'utf8');
+ await testInfo.attach('downloaded', { path });
+
+ return {
+ download,
+ content,
+ }
+ }
+
async joinMicrophone() {
await this.waitForSelector(e.audioModal);
await this.waitAndClick(e.microphoneButton);
diff --git a/bigbluebutton-tests/playwright/core/util.js b/bigbluebutton-tests/playwright/core/util.js
index b7ec0a08c6..71d5fea224 100644
--- a/bigbluebutton-tests/playwright/core/util.js
+++ b/bigbluebutton-tests/playwright/core/util.js
@@ -1,3 +1,5 @@
+const { expect } = require("@playwright/test");
+
// Common
function checkElement([element, index = 0]) {
return document.querySelectorAll(element)[index] !== undefined;
@@ -8,5 +10,14 @@ function checkElementLengthEqualTo([element, count]) {
return document.querySelectorAll(element).length == count;
}
+// Text
+async function checkTextContent(baseContent, checkData) {
+ if (typeof checkData === 'string' ) checkData = new Array(checkData);
+
+ const check = checkData.every(word => baseContent.includes(word));
+ await expect(check).toBeTruthy();
+}
+
exports.checkElement = checkElement;
exports.checkElementLengthEqualTo = checkElementLengthEqualTo;
+exports.checkTextContent = checkTextContent;
diff --git a/bigbluebutton-tests/playwright/presentation/presentation.js b/bigbluebutton-tests/playwright/presentation/presentation.js
index 5ddc9fc95c..58e0aee2b9 100644
--- a/bigbluebutton-tests/playwright/presentation/presentation.js
+++ b/bigbluebutton-tests/playwright/presentation/presentation.js
@@ -72,7 +72,7 @@ class Presentation extends MultiUsers {
await expect(userSlides0).not.toEqual(userSlides1);
}
- async allowAndDisallowDownload() {
+ async allowAndDisallowDownload(testInfo) {
// allow the presentation download
await this.modPage.waitForSelector(e.whiteboard, ELEMENT_WAIT_LONGER_TIME);
await this.modPage.waitAndClick(e.actions);
@@ -83,6 +83,7 @@ class Presentation extends MultiUsers {
await this.userPage.waitForSelector(e.toastDownload);
// check download button in presentation after ALLOW it - should be true
await this.userPage.hasElement(e.presentationDownloadBtn);
+ await this.userPage.handleDownload(e.presentationDownloadBtn, testInfo);
// disallow the presentation download
await this.modPage.waitAndClick(e.actions);
diff --git a/bigbluebutton-tests/playwright/presentation/presentation.spec.js b/bigbluebutton-tests/playwright/presentation/presentation.spec.js
index cc951c566e..29c155e834 100644
--- a/bigbluebutton-tests/playwright/presentation/presentation.spec.js
+++ b/bigbluebutton-tests/playwright/presentation/presentation.spec.js
@@ -27,10 +27,10 @@ test.describe.parallel('Presentation', () => {
await presentation.uploadPresentationTest();
});
- test('Allow and disallow presentation download', async ({ browser, context, page }) => {
+ test('Allow and disallow presentation download', async ({ browser, context, page }, testInfo) => {
const presentation = new Presentation(browser, context);
await presentation.initPages(page);
- await presentation.allowAndDisallowDownload();
+ await presentation.allowAndDisallowDownload(testInfo);
});
test('Remove all presentation', async ({ browser, context, page }) => {
diff --git a/bigbluebutton-tests/playwright/user/multiusers.js b/bigbluebutton-tests/playwright/user/multiusers.js
index 9ee13fd256..96c681c2c3 100644
--- a/bigbluebutton-tests/playwright/user/multiusers.js
+++ b/bigbluebutton-tests/playwright/user/multiusers.js
@@ -4,6 +4,7 @@ const e = require('../core/elements');
const { waitAndClearNotification } = require('../notifications/util');
const { sleep } = require('../core/helpers');
const { checkAvatarIcon, checkIsPresenter } = require('./util');
+const { checkTextContent } = require('../core/util');
class MultiUsers {
constructor(browser, context) {
@@ -151,6 +152,18 @@ class MultiUsers {
await this.modPage.hasElement(e.chatButton);
}
+ async saveUserNames(testInfo) {
+ await this.modPage.waitAndClick(e.manageUsers);
+ const { content } = await this.modPage.handleDownload(e.downloadUserNamesList, testInfo);
+
+ const dataToCheck = [
+ this.modPage.username,
+ this.userPage.username,
+ this.modPage.meetingId,
+ ];
+ await checkTextContent(content, dataToCheck);
+ }
+
async selectRandomUser() {
// check with no viewer joined
await this.modPage.waitAndClick(e.actions);
diff --git a/bigbluebutton-tests/playwright/user/user.spec.js b/bigbluebutton-tests/playwright/user/user.spec.js
index c1f99ae0b3..a4ae53b427 100644
--- a/bigbluebutton-tests/playwright/user/user.spec.js
+++ b/bigbluebutton-tests/playwright/user/user.spec.js
@@ -142,6 +142,12 @@ test.describe.parallel('User', () => {
});
});
+ test('Save user names', async ({ browser, context, page }, testInfo) => {
+ const multiusers = new MultiUsers(browser, context);
+ await multiusers.initPages(page);
+ await multiusers.saveUserNames(testInfo);
+ });
+
test('Select random user', async ({ browser, context, page }) => {
const multiusers = new MultiUsers(browser, context);
await multiusers.initModPage(page);
From b4bf27c199f395c6008a469158185206547ee3e9 Mon Sep 17 00:00:00 2001
From: Guilherme Leme
Date: Thu, 24 Mar 2022 15:35:17 -0300
Subject: [PATCH 027/180] [issue-14335]- Refactored endpoints to not need the
password anymore. It is in deprecated state now.
---
.../api/ParamsProcessorUtil.java | 8 ++---
.../org/bigbluebutton/api/domain/Meeting.java | 16 ++++++---
.../api/model/request/EndMeeting.java | 1 -
.../api/model/request/JoinMeeting.java | 1 -
.../api/model/shared/Password.java | 1 -
.../validator/JoinPasswordValidator.java | 8 -----
.../validator/ModeratorPasswordValidator.java | 20 ++++-------
.../model/validator/PasswordValidator.java | 14 +++-----
.../web/controllers/ApiController.groovy | 34 ++++++++++++++++---
.../WEB-INF/freemarker/create-meeting.ftlx | 8 +++--
.../WEB-INF/freemarker/get-meeting-info.ftlx | 8 +++--
.../WEB-INF/freemarker/get-meetings.ftlx | 8 +++--
12 files changed, 73 insertions(+), 54 deletions(-)
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java
index 2b76e1b1a1..be19ad90c9 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java
@@ -351,8 +351,8 @@ public class ParamsProcessorUtil {
String externalMeetingId = params.get(ApiParams.MEETING_ID);
- String viewerPass = processPassword(params.get(ApiParams.ATTENDEE_PW));
- String modPass = processPassword(params.get(ApiParams.MODERATOR_PW));
+ String viewerPass = params.get(ApiParams.ATTENDEE_PW);
+ String modPass = params.get(ApiParams.MODERATOR_PW);
// Get the digits for voice conference for users joining through the
// phone.
@@ -717,10 +717,6 @@ public class ParamsProcessorUtil {
return DigestUtils.sha1Hex(extMeetingId);
}
- public String processPassword(String pass) {
- return StringUtils.isEmpty(pass) ? RandomStringUtils.randomAlphanumeric(8) : pass;
- }
-
public boolean hasChecksumAndQueryString(String checksum, String queryString) {
return (! StringUtils.isEmpty(checksum) && StringUtils.isEmpty(queryString));
}
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 54b8e0ce50..a3b7291ec1 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
@@ -49,8 +49,8 @@ public class Meeting {
private boolean forciblyEnded = false;
private String telVoice;
private String webVoice;
- private String moderatorPass;
- private String viewerPass;
+ private String moderatorPass = "";
+ private String viewerPass = "";
private Boolean learningDashboardEnabled;
private int learningDashboardCleanupDelayInMinutes;
private String learningDashboardAccessToken;
@@ -113,8 +113,16 @@ public class Meeting {
name = builder.name;
extMeetingId = builder.externalId;
intMeetingId = builder.internalId;
- viewerPass = builder.viewerPass;
- moderatorPass = builder.moderatorPass;
+ if (builder.viewerPass == null){
+ viewerPass = "";
+ } else {
+ viewerPass = builder.viewerPass;
+ }
+ if (builder.moderatorPass == null){
+ moderatorPass = "";
+ } else {
+ moderatorPass = builder.moderatorPass;
+ }
learningDashboardEnabled = builder.learningDashboardEnabled;
virtualBackgroundsDisabled = builder.virtualBackgroundsDisabled;
learningDashboardCleanupDelayInMinutes = builder.learningDashboardCleanupDelayInMinutes;
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/model/request/EndMeeting.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/model/request/EndMeeting.java
index 49de69718e..55feff00df 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/model/request/EndMeeting.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/model/request/EndMeeting.java
@@ -29,7 +29,6 @@ public class EndMeeting extends RequestWithChecksum {
private String meetingID;
@PasswordConstraint
- @NotEmpty(message = "You must provide the moderator password")
private String password;
@Valid
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/model/request/JoinMeeting.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/model/request/JoinMeeting.java
index 115c3d232a..b629d9c8bb 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/model/request/JoinMeeting.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/model/request/JoinMeeting.java
@@ -38,7 +38,6 @@ public class JoinMeeting extends RequestWithChecksum {
private String fullName;
@PasswordConstraint
- @NotEmpty(key = "invalidPassword", message = "You must provide either the moderator or attendee password")
private String password;
@IsBooleanConstraint(message = "Guest must be a boolean value (true or false)")
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/model/shared/Password.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/model/shared/Password.java
index 81052e835e..9b44c7b1f0 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/model/shared/Password.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/model/shared/Password.java
@@ -7,7 +7,6 @@ public abstract class Password {
@NotEmpty(message = "You must provide the meeting ID")
protected String meetingID;
- @NotEmpty(message = "You must provide the password for the call")
protected String password;
public String getMeetingID() {
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/model/validator/JoinPasswordValidator.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/model/validator/JoinPasswordValidator.java
index 1a67497495..0bebd3b16d 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/model/validator/JoinPasswordValidator.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/model/validator/JoinPasswordValidator.java
@@ -36,18 +36,10 @@ public class JoinPasswordValidator implements ConstraintValidator 64) {
- log.info("Passwords must be between 2 and 64 characters in length");
- return false;
+ if (password != null && !password.isEmpty()){
+ if (password.length() < 2 || password.length() > 64) {
+ log.info("Passwords must be between 2 and 64 characters in length");
+ return false;
+ }
}
return true;
diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy
index f352040611..a49d3da683 100755
--- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy
+++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy
@@ -264,14 +264,40 @@ class ApiController {
// Now determine if this user is a moderator or a viewer.
String role = null;
- if (meeting.getModeratorPassword().equals(attPW)) {
- role = Meeting.ROLE_MODERATOR
- } else if (meeting.getViewerPassword().equals(attPW)) {
- role = Meeting.ROLE_ATTENDEE
+ if (attPW != null && !attPW.isEmpty()){
+ if ((meeting.getModeratorPassword() != null && !meeting.getModeratorPassword().isEmpty())
+ && (meeting.getViewerPassword() != null && !meeting.getViewerPassword().isEmpty())){
+ if (meeting.getModeratorPassword().equals(attPW)) {
+ role = Meeting.ROLE_MODERATOR
+ } else if (meeting.getViewerPassword().equals(attPW)) {
+ role = Meeting.ROLE_ATTENDEE
+ }
+ } else if (StringUtils.isEmpty(params.role)){
+ log.debug("This meeting doesn't have any password");
+ response.addHeader("Cache-Control", "no-cache")
+ withFormat {
+ xml {
+ render(text: responseBuilder.buildError("Params required", "You must send the 'role' parameter, since " +
+ "this meeting doesn't have any password.", RESP_CODE_FAILED), contentType: "text/xml")
+ }
+ }
+ return
+ }
}
if (!StringUtils.isEmpty(params.role) && roles.containsKey(params.role.toLowerCase())) {
role = roles.get(params.role.toLowerCase());
+ } else if (attPW == null){
+ log.info("You must either send the valid role of the user, or " +
+ "the password, sould the meeting has one");
+ response.addHeader("Cache-Control", "no-cache")
+ withFormat {
+ xml {
+ render(text: responseBuilder.buildError("Params required", "You must either send the valid role of the user, or " +
+ "the password, sould the meeting has one", RESP_CODE_FAILED), contentType: "text/xml")
+ }
+ }
+ return
}
// We preprend "w_" to our internal meeting Id to indicate that this is a web user.
diff --git a/bigbluebutton-web/src/main/webapp/WEB-INF/freemarker/create-meeting.ftlx b/bigbluebutton-web/src/main/webapp/WEB-INF/freemarker/create-meeting.ftlx
index cb0aba7c9d..0d72856afa 100644
--- a/bigbluebutton-web/src/main/webapp/WEB-INF/freemarker/create-meeting.ftlx
+++ b/bigbluebutton-web/src/main/webapp/WEB-INF/freemarker/create-meeting.ftlx
@@ -6,8 +6,12 @@
${meeting.getExternalId()} <#-- Yielded -->
${meeting.getInternalId()} <#-- Yielded -->
${meeting.getParentMeetingId()} <#-- Yielded -->
- ${meeting.getViewerPassword()} <#-- Yielded -->
- ${meeting.getModeratorPassword()} <#-- Yielded -->
+ <#if meeting.getViewerPassword()?? && meeting.getViewerPassword()!="" >
+ ${meeting.getViewerPassword()}
+ #if>
+ <#if meeting.getModeratorPassword()?? && meeting.getModeratorPassword()!="">
+ ${meeting.getModeratorPassword()}
+ #if>
${meeting.getCreateTime()?c}
${meeting.getTelVoice()} <#-- Yielded -->
${meeting.getDialNumber()} <#-- Yielded -->
diff --git a/bigbluebutton-web/src/main/webapp/WEB-INF/freemarker/get-meeting-info.ftlx b/bigbluebutton-web/src/main/webapp/WEB-INF/freemarker/get-meeting-info.ftlx
index 3c461fa7db..57ad73a810 100755
--- a/bigbluebutton-web/src/main/webapp/WEB-INF/freemarker/get-meeting-info.ftlx
+++ b/bigbluebutton-web/src/main/webapp/WEB-INF/freemarker/get-meeting-info.ftlx
@@ -10,8 +10,12 @@
${createdOn}
${meeting.getTelVoice()}
${meeting.getDialNumber()}
- ${meeting.getViewerPassword()}
- ${meeting.getModeratorPassword()}
+ <#if meeting.getViewerPassword()?? && meeting.getViewerPassword()!="" >
+ ${meeting.getViewerPassword()}
+ #if>
+ <#if meeting.getModeratorPassword()?? && meeting.getModeratorPassword()!="">
+ ${meeting.getModeratorPassword()}
+ #if>
${meeting.isRunning()?c}
${meeting.getDuration()?c}
${meeting.hasUserJoined()?c}
diff --git a/bigbluebutton-web/src/main/webapp/WEB-INF/freemarker/get-meetings.ftlx b/bigbluebutton-web/src/main/webapp/WEB-INF/freemarker/get-meetings.ftlx
index 609f328aa7..2de87faba9 100755
--- a/bigbluebutton-web/src/main/webapp/WEB-INF/freemarker/get-meetings.ftlx
+++ b/bigbluebutton-web/src/main/webapp/WEB-INF/freemarker/get-meetings.ftlx
@@ -15,8 +15,12 @@
${meetingDetail.getCreatedOn()}
${meeting.getTelVoice()}
${meeting.getDialNumber()}
- ${meeting.getViewerPassword()}
- ${meeting.getModeratorPassword()}
+ <#if meeting.getViewerPassword()?? && meeting.getViewerPassword()!="" >
+ ${meeting.getViewerPassword()}
+ #if>
+ <#if meeting.getModeratorPassword()?? && meeting.getModeratorPassword()!="">
+ ${meeting.getModeratorPassword()}
+ #if>
${meeting.isRunning()?c}
${meeting.getDuration()?c}
${meeting.hasUserJoined()?c}
From c6653cd4b278631ac58f27c50a59142b56fe2294 Mon Sep 17 00:00:00 2001
From: Guilherme Leme
Date: Fri, 25 Mar 2022 14:12:10 -0300
Subject: [PATCH 028/180] [Issue-14321]- Changes in Review
---
.../presentation/UploadedPresentation.java | 13 ++-----------
.../methods/requestPresentationUploadToken.js | 2 +-
.../presentation/presentation-uploader/service.js | 2 +-
3 files changed, 4 insertions(+), 13 deletions(-)
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/UploadedPresentation.java b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/UploadedPresentation.java
index 110d8ba02b..995f371ff4 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/UploadedPresentation.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/UploadedPresentation.java
@@ -73,17 +73,8 @@ public final class UploadedPresentation {
String authzToken,
Boolean uploadFailed,
ArrayList uploadFailReason) {
- this.podId = podId;
- this.meetingId = meetingId;
- this.id = id;
- this.temporaryPresentationId = "";
- this.name = name;
- this.baseUrl = baseUrl;
- this.isDownloadable = false;
- this.current = current;
- this.authzToken = authzToken;
- this.uploadFailed = uploadFailed;
- this.uploadFailReason = uploadFailReason;
+ this(podId, meetingId, id, "", name, baseUrl,
+ current, authzToken, uploadFailed, uploadFailReason);
}
public File getUploadedFile() {
diff --git a/bigbluebutton-html5/imports/api/presentation-upload-token/server/methods/requestPresentationUploadToken.js b/bigbluebutton-html5/imports/api/presentation-upload-token/server/methods/requestPresentationUploadToken.js
index 65a13433ad..bd5f094654 100644
--- a/bigbluebutton-html5/imports/api/presentation-upload-token/server/methods/requestPresentationUploadToken.js
+++ b/bigbluebutton-html5/imports/api/presentation-upload-token/server/methods/requestPresentationUploadToken.js
@@ -15,7 +15,7 @@ export default function requestPresentationUploadToken(podId, filename, tmpPresI
check(requesterUserId, String);
check(podId, String);
check(filename, String);
- check(tmpPresId, String)
+ check(tmpPresId, String);
const payload = {
podId,
diff --git a/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/service.js b/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/service.js
index cbfa14ba81..31d3f997e8 100644
--- a/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/service.js
+++ b/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/service.js
@@ -169,7 +169,7 @@ const uploadAndConvertPresentation = (
data.append('fileUpload', file);
data.append('conference', meetingId);
data.append('room', meetingId);
- data.append('temporaryPresentationId', tmpPresId)
+ data.append('temporaryPresentationId', tmpPresId);
// TODO: Currently the uploader is not related to a POD so the id is fixed to the default
data.append('pod_id', podId);
From 4e4f8f546464351b455826d5fc401954827c207a Mon Sep 17 00:00:00 2001
From: Ramon Souza
Date: Mon, 28 Mar 2022 11:10:54 -0300
Subject: [PATCH 029/180] fix menu autofocus firefox
---
bigbluebutton-html5/imports/ui/components/menu/component.jsx | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/bigbluebutton-html5/imports/ui/components/menu/component.jsx b/bigbluebutton-html5/imports/ui/components/menu/component.jsx
index 5004959191..b444e5395b 100644
--- a/bigbluebutton-html5/imports/ui/components/menu/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/menu/component.jsx
@@ -105,7 +105,10 @@ class BBBMenu extends React.Component {
{
e.persist();
- this.opts.autoFocus = !(['mouse', 'touch'].includes(e.nativeEvent.pointerType));
+ const firefoxInputSource = !([1, 5].includes(e.nativeEvent.mozInputSource)); // 1 = mouse, 5 = touch (firefox only)
+ const chromeInputSource = !(['mouse', 'touch'].includes(e.nativeEvent.pointerType));
+
+ this.opts.autoFocus = firefoxInputSource && chromeInputSource;
this.handleClick(e);
}}
onKeyPress={(e) => {
From 6eead03fb396837638258eb1e3782bc9a77269ee Mon Sep 17 00:00:00 2001
From: Anton B
Date: Tue, 29 Mar 2022 10:53:07 -0300
Subject: [PATCH 030/180] set dynamic steps by current settings
---
.../video-list/video-list-item/component.jsx | 1 +
bigbluebutton-tests/playwright/audio/audio.js | 27 ++++++----
.../playwright/breakout/join.js | 5 +-
bigbluebutton-tests/playwright/chat/chat.js | 15 ++++--
.../playwright/chat/chat.spec.js | 2 +-
.../playwright/chat/privateChat.js | 10 ++--
bigbluebutton-tests/playwright/chat/util.js | 20 ++++++--
.../playwright/core/elements.js | 3 +-
bigbluebutton-tests/playwright/core/page.js | 21 ++++----
.../playwright/core/settings.js | 51 +++++++++++++++++++
bigbluebutton-tests/playwright/core/util.js | 2 +-
.../customparameters/customparameters.js | 16 +++---
.../playwright/notifications/notifications.js | 4 ++
.../playwright/polling/poll.js | 5 +-
.../playwright/polling/polling.spec.js | 2 +-
.../playwright/polling/util.js | 5 ++
.../playwright/presentation/presentation.js | 16 ++++--
.../playwright/screenshare/screenshare.js | 6 ++-
.../playwright/sharednotes/sharednotes.js | 4 ++
.../playwright/user/multiusers.js | 15 +++---
.../playwright/user/user.spec.js | 4 +-
.../playwright/webcam/webcam.js | 18 +++----
22 files changed, 174 insertions(+), 78 deletions(-)
create mode 100644 bigbluebutton-tests/playwright/core/settings.js
diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/video-list-item/component.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/video-list-item/component.jsx
index aac238c700..0916155d8d 100755
--- a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/video-list-item/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/video-list-item/component.jsx
@@ -146,6 +146,7 @@ const VideoListItem = (props) => {
muted
mirrored={isMirrored}
unhealthyStream={shouldRenderReconnect}
+ data-test={isMirrored ? 'mirroredVideoContainer' : 'videoContainer'}
ref={videoTag}
autoPlay
playsInline
diff --git a/bigbluebutton-tests/playwright/audio/audio.js b/bigbluebutton-tests/playwright/audio/audio.js
index abe75db175..e076195c49 100644
--- a/bigbluebutton-tests/playwright/audio/audio.js
+++ b/bigbluebutton-tests/playwright/audio/audio.js
@@ -1,6 +1,6 @@
const Page = require('../core/page');
const e = require('../core/elements');
-const { ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
+const { ELEMENT_WAIT_LONGER_TIME, ELEMENT_WAIT_TIME } = require('../core/constants');
class Audio extends Page {
constructor(browser, page) {
@@ -8,23 +8,32 @@ class Audio extends Page {
}
async joinAudio() {
+ const { autoJoinAudioModal, listenOnlyCallTimeout } = this.settings;
+ if (!autoJoinAudioModal) await this.waitAndClick(e.joinAudio);
await this.waitAndClick(e.listenOnlyButton);
await this.wasRemoved(e.connecting);
- const parsedSettings = await this.getSettingsYaml();
- const listenOnlyCallTimeout = parseInt(parsedSettings.public.media.listenOnlyCallTimeout);
await this.waitForSelector(e.leaveAudio, listenOnlyCallTimeout);
await this.waitForSelector(e.whiteboard);
await this.hasElement(e.leaveAudio);
}
async joinMicrophone() {
+ const {
+ autoJoinAudioModal,
+ skipEchoTest,
+ skipEchoTestOnJoin,
+ listenOnlyCallTimeout,
+ } = this.settings;
+
+ if (!autoJoinAudioModal) await this.waitAndClick(e.joinAudio);
await this.waitAndClick(e.microphoneButton);
- await this.waitForSelector(e.connectingToEchoTest);
- await this.wasRemoved(e.connectingToEchoTest, ELEMENT_WAIT_LONGER_TIME);
- const parsedSettings = await this.getSettingsYaml();
- const listenOnlyCallTimeout = parseInt(parsedSettings.public.media.listenOnlyCallTimeout);
- await this.waitAndClick(e.echoYesButton, listenOnlyCallTimeout);
- await this.waitForSelector(e.whiteboard);
+ const shouldSkipEchoTest = skipEchoTest || skipEchoTestOnJoin;
+ if (!shouldSkipEchoTest) {
+ await this.waitForSelector(e.connectingToEchoTest);
+ await this.wasRemoved(e.connectingToEchoTest, ELEMENT_WAIT_LONGER_TIME);
+ await this.waitAndClick(e.echoYesButton, listenOnlyCallTimeout);
+ }
+ await this.waitForSelector(e.whiteboard, shouldSkipEchoTest ? listenOnlyCallTimeout : ELEMENT_WAIT_TIME);
await this.hasElement(e.isTalking);
}
}
diff --git a/bigbluebutton-tests/playwright/breakout/join.js b/bigbluebutton-tests/playwright/breakout/join.js
index 6f0ce8d131..313ff8dd6e 100644
--- a/bigbluebutton-tests/playwright/breakout/join.js
+++ b/bigbluebutton-tests/playwright/breakout/join.js
@@ -30,9 +30,8 @@ class Join extends Create {
async joinAndShareWebcam() {
const breakoutPage = await this.joinRoom();
- const parsedSettings = await this.userPage.getSettingsYaml();
- const videoPreviewTimeout = parseInt(parsedSettings.public.kurento.gUMTimeout);
- await breakoutPage.shareWebcam(videoPreviewTimeout);
+ const { videoPreviewTimeout } = breakoutPage.settings;
+ await breakoutPage.shareWebcam(true, videoPreviewTimeout);
await breakoutPage.hasElement(e.presentationPlaceholder);
}
diff --git a/bigbluebutton-tests/playwright/chat/chat.js b/bigbluebutton-tests/playwright/chat/chat.js
index 4a5f18c14a..62fd75f3e7 100644
--- a/bigbluebutton-tests/playwright/chat/chat.js
+++ b/bigbluebutton-tests/playwright/chat/chat.js
@@ -1,9 +1,10 @@
-const { expect } = require('@playwright/test');
+const { expect, default: test } = require('@playwright/test');
const Page = require('../core/page');
const { openChat } = require('./util');
const p = require('../core/parameters');
const e = require('../core/elements');
const { checkTextContent } = require('../core/util');
+const { getSettings } = require('../core/settings');
class Chat extends Page {
constructor(browser, page) {
@@ -40,8 +41,10 @@ class Chat extends Page {
}
async copyChat(context) {
- await openChat(this);
+ const { publicChatOptionsEnabled } = getSettings();
+ test.fail(!publicChatOptionsEnabled, 'Public chat options (save and copy) are disabled');
+ await openChat(this);
// sending a message
await this.type(e.chatBox, e.message);
await this.waitAndClick(e.sendButton);
@@ -58,6 +61,9 @@ class Chat extends Page {
}
async saveChat(testInfo) {
+ const { publicChatOptionsEnabled } = getSettings();
+ test.fail(!publicChatOptionsEnabled, 'Public chat options (save and copy) are disabled');
+
await openChat(this);
await this.type(e.chatBox, e.message);
await this.waitAndClick(e.sendButton);
@@ -77,12 +83,13 @@ class Chat extends Page {
await openChat(this);
const messageLocator = this.getLocator(e.chatUserMessageText);
- await this.page.fill(e.chatBox, e.longMessage5000);
+ const { maxMessageLength } = getSettings();
+ await this.page.fill(e.chatBox, e.uniqueCharacterMessage.repeat(maxMessageLength));
await this.waitAndClick(e.sendButton);
await this.waitForSelector(e.chatUserMessageText);
await expect(messageLocator).toHaveCount(1);
- await this.page.fill(e.chatBox, e.longMessage5001);
+ await this.page.fill(e.chatBox, e.uniqueCharacterMessage.repeat(maxMessageLength + 1));
await this.waitForSelector(e.typingIndicator);
await this.waitAndClick(e.sendButton);
await this.waitForSelector(e.chatUserMessageText);
diff --git a/bigbluebutton-tests/playwright/chat/chat.spec.js b/bigbluebutton-tests/playwright/chat/chat.spec.js
index 301821ecc5..a687955d78 100644
--- a/bigbluebutton-tests/playwright/chat/chat.spec.js
+++ b/bigbluebutton-tests/playwright/chat/chat.spec.js
@@ -34,7 +34,7 @@ test.describe.parallel('Chat', () => {
await chat.saveChat(testInfo);
});
- test('Verify character limit (5000 characters)', async ({ browser, page }) => {
+ test('Verify character limit', async ({ browser, page }) => {
const chat = new Chat(browser, page);
await chat.init(true, true);
await chat.characterLimit();
diff --git a/bigbluebutton-tests/playwright/chat/privateChat.js b/bigbluebutton-tests/playwright/chat/privateChat.js
index 1fbbb20d9d..b624184cb9 100644
--- a/bigbluebutton-tests/playwright/chat/privateChat.js
+++ b/bigbluebutton-tests/playwright/chat/privateChat.js
@@ -2,6 +2,7 @@ const { MultiUsers } = require('../user/multiusers');
const e = require('../core/elements');
const { sleep } = require('../core/helpers');
const { expect } = require('@playwright/test');
+const { openPrivateChat } = require('./util');
class PrivateChat extends MultiUsers {
constructor(browser, context) {
@@ -9,8 +10,7 @@ class PrivateChat extends MultiUsers {
}
async sendPrivateMessage() {
- await this.modPage.waitAndClick(e.userListItem);
- await this.modPage.waitAndClick(e.startPrivateChat);
+ await openPrivateChat(this.modPage);
await this.modPage.waitForSelector(e.hidePrivateChat);
await sleep(500); // prevent a race condition when running on a deployed server
// modPage send message
@@ -31,8 +31,7 @@ class PrivateChat extends MultiUsers {
}
async closeChat() {
- await this.modPage.waitAndClick(e.userListItem);
- await this.modPage.waitAndClick(e.startPrivateChat);
+ await openPrivateChat(this.modPage);
await this.modPage.waitUntilHaveCountSelector(e.chatButton, 2);
const privateChatLocator = this.modPage.getLocatorByIndex(e.chatButton, -1);
expect(privateChatLocator).toContainText(this.userPage.username);
@@ -48,8 +47,7 @@ class PrivateChat extends MultiUsers {
}
async chatDisabledUserLeaves() {
- await this.modPage.waitAndClick(e.userListItem);
- await this.modPage.waitAndClick(e.startPrivateChat);
+ await openPrivateChat(this.modPage);
await this.modPage.waitForSelector(e.sendButton);
await this.userPage.waitAndClick(e.optionsButton);
await this.userPage.waitAndClick(e.logout);
diff --git a/bigbluebutton-tests/playwright/chat/util.js b/bigbluebutton-tests/playwright/chat/util.js
index f5a4ae2776..49141feaf7 100644
--- a/bigbluebutton-tests/playwright/chat/util.js
+++ b/bigbluebutton-tests/playwright/chat/util.js
@@ -1,8 +1,22 @@
+const { default: test } = require('@playwright/test');
const e = require('../core/elements');
+const { getSettings } = require('../core/settings');
-async function openChat(test) {
- await test.waitForSelector(e.chatBox);
- await test.waitForSelector(e.chatMessages);
+async function openChat(testPage) {
+ const { chatEnabled } = getSettings();
+ test.fail(!chatEnabled, 'Chat is disabled');
+
+ await testPage.waitForSelector(e.chatBox);
+ await testPage.waitForSelector(e.chatMessages);
+}
+
+async function openPrivateChat(testPage) {
+ const { chatEnabled } = getSettings();
+ test.fail(!chatEnabled, 'Chat is disabled');
+
+ await testPage.waitAndClick(e.userListItem);
+ await testPage.waitAndClick(e.startPrivateChat);
}
exports.openChat = openChat;
+exports.openPrivateChat = openPrivateChat;
diff --git a/bigbluebutton-tests/playwright/core/elements.js b/bigbluebutton-tests/playwright/core/elements.js
index a912a09435..7066b532d7 100644
--- a/bigbluebutton-tests/playwright/core/elements.js
+++ b/bigbluebutton-tests/playwright/core/elements.js
@@ -72,8 +72,7 @@ exports.message1 = 'Hello User2';
exports.message2 = 'Hello User1';
exports.publicMessage1 = 'This is a Public Message from User1';
exports.publicMessage2 = 'This is a Public Message from User2';
-exports.longMessage5000 = '01234567890123456789012345678901234567890123456789'.repeat(100);
-exports.longMessage5001 = '01234567890123456789012345678901234567890123456789'.repeat(100) + '0';
+exports.uniqueCharacterMessage = 'A';
// CustomParameters
exports.audioOptionsButtons = 'span[data-test="audioModalOptions"] > button';
diff --git a/bigbluebutton-tests/playwright/core/page.js b/bigbluebutton-tests/playwright/core/page.js
index 55019bdd4e..3fa443dfc7 100644
--- a/bigbluebutton-tests/playwright/core/page.js
+++ b/bigbluebutton-tests/playwright/core/page.js
@@ -1,5 +1,5 @@
require('dotenv').config();
-const { expect } = require('@playwright/test');
+const { expect, default: test } = require('@playwright/test');
const yaml = require('js-yaml');
const path = require('path');
const { readFileSync } = require('fs');
@@ -8,6 +8,7 @@ const helpers = require('./helpers');
const e = require('./elements');
const { ELEMENT_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME, VIDEO_LOADING_WAIT_TIME } = require('./constants');
const { checkElement, checkElementLengthEqualTo } = require('./util');
+const { generateSettingsData } = require('./settings');
class Page {
constructor(browser, page) {
@@ -16,14 +17,6 @@ class Page {
this.initParameters = Object.assign({}, parameters);
}
- async getSettingsYaml() {
- try {
- return yaml.load(readFileSync(path.join(__dirname, '../../../bigbluebutton-html5/private/config/settings.yml'), 'utf8'));
- } catch (err) {
- console.log(err);
- }
- }
-
async bringToFront() {
await this.page.bringToFront();
}
@@ -43,7 +36,9 @@ class Page {
this.meetingId = (meetingId) ? meetingId : await helpers.createMeeting(parameters, customParameter);
const joinUrl = helpers.getJoinURL(this.meetingId, this.initParameters, isModerator, customParameter);
await this.page.goto(joinUrl);
- if (shouldCloseAudioModal) await this.closeAudioModal();
+ this.settings = await generateSettingsData(this.page);
+ const { autoJoinAudioModal } = this.settings;
+ if (shouldCloseAudioModal && autoJoinAudioModal) await this.closeAudioModal();
}
async handleDownload(selector, testInfo, timeout = ELEMENT_WAIT_TIME) {
@@ -66,8 +61,7 @@ class Page {
await this.waitForSelector(e.audioModal);
await this.waitAndClick(e.microphoneButton);
await this.waitForSelector(e.connectingToEchoTest);
- const parsedSettings = await this.getSettingsYaml();
- const listenOnlyCallTimeout = parseInt(parsedSettings.public.media.listenOnlyCallTimeout);
+ const { listenOnlyCallTimeout } = this.settings;
await this.waitAndClick(e.echoYesButton, listenOnlyCallTimeout);
await this.waitForSelector(e.isTalking);
}
@@ -83,6 +77,9 @@ class Page {
}
async shareWebcam(shouldConfirmSharing = true, videoPreviewTimeout = ELEMENT_WAIT_TIME) {
+ const { webcamSharingEnabled } = this.settings;
+ test.fail(!webcamSharingEnabled, 'Webcam sharing is disabled');
+
await this.waitAndClick(e.joinVideo);
if (shouldConfirmSharing) {
await this.bringToFront();
diff --git a/bigbluebutton-tests/playwright/core/settings.js b/bigbluebutton-tests/playwright/core/settings.js
new file mode 100644
index 0000000000..bdce8bb133
--- /dev/null
+++ b/bigbluebutton-tests/playwright/core/settings.js
@@ -0,0 +1,51 @@
+let settings;
+
+async function generateSettingsData(page) {
+ if (settings || !page) return settings;
+
+ try {
+ const settingsData = await page.evaluate(() => {
+ return Meteor.settings.public;
+ });
+
+ settings = {
+ raiseHandButton: settingsData.app.raiseHandActionButton.enabled,
+ sharedNotesEnabled: settingsData.notes.enabled,
+ // Audio
+ autoJoinAudioModal: settingsData.app.autoJoin,
+ listenOnlyMode: settingsData.app.listenOnlyMode,
+ forceListenOnly: settingsData.app.forceListenOnly,
+ skipEchoTest: settingsData.app.skipCheck,
+ skipEchoTestOnJoin: settingsData.app.skipCheckOnJoin,
+ // Chat
+ chatEnabled: settingsData.chat.enabled,
+ publicChatOptionsEnabled: settingsData.chat.enableSaveAndCopyPublicChat,
+ maxMessageLength: settingsData.chat.max_message_length,
+ // Polling
+ pollEnabled: settingsData.poll.enabled,
+ pollChatMessage: settingsData.poll.chatMessage,
+ // Presentation
+ presentationDownloadable: settingsData.presentation.allowDownloadable,
+ externalVideoPlayer: settingsData.externalVideoPlayer.enabled,
+ presentationHidden: settingsData.layout.hidePresentation,
+ // Screensharing
+ screensharingEnabled: settingsData.kurento.enableScreensharing,
+ // Timeouts
+ listenOnlyCallTimeout: parseInt(settingsData.media.listenOnlyCallTimeout),
+ videoPreviewTimeout: parseInt(settingsData.kurento.gUMTimeout),
+ // Webcam
+ webcamSharingEnabled: settingsData.kurento.enableVideo,
+ skipVideoPreview: settingsData.kurento.skipVideoPreview,
+ skipVideoPreviewOnFirstJoin: settingsData.kurento.skipVideoPreviewOnFirstJoin,
+ }
+
+ return settings;
+ } catch (err) {
+ console.log(`Unable to get public settings data: ${err}`);
+ }
+}
+
+module.exports = exports = {
+ getSettings: () => settings,
+ generateSettingsData,
+}
diff --git a/bigbluebutton-tests/playwright/core/util.js b/bigbluebutton-tests/playwright/core/util.js
index 71d5fea224..3c6473aced 100644
--- a/bigbluebutton-tests/playwright/core/util.js
+++ b/bigbluebutton-tests/playwright/core/util.js
@@ -12,7 +12,7 @@ function checkElementLengthEqualTo([element, count]) {
// Text
async function checkTextContent(baseContent, checkData) {
- if (typeof checkData === 'string' ) checkData = new Array(checkData);
+ if (typeof checkData === 'string') checkData = new Array(checkData);
const check = checkData.every(word => baseContent.includes(word));
await expect(check).toBeTruthy();
diff --git a/bigbluebutton-tests/playwright/customparameters/customparameters.js b/bigbluebutton-tests/playwright/customparameters/customparameters.js
index a307d6c94e..7396732e44 100644
--- a/bigbluebutton-tests/playwright/customparameters/customparameters.js
+++ b/bigbluebutton-tests/playwright/customparameters/customparameters.js
@@ -4,6 +4,7 @@ const e = require('../core/elements');
const c = require('./constants');
const { VIDEO_LOADING_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
const util = require('./util');
+const { getSettings } = require('../core/settings');
class CustomParameters extends MultiUsers {
constructor(browser, context) {
@@ -136,12 +137,13 @@ class CustomParameters extends MultiUsers {
async forceRestorePresentationOnNewEvents(customParameter) {
await this.initUserPage(true, this.context, { useModMeetingId: true, customParameter });
- await this.userPage.waitAndClick(e.minimizePresentation);
+ const { presentationHidden, pollEnabled } = getSettings();
+ if (!presentationHidden) await this.userPage.waitAndClick(e.minimizePresentation);
const zoomInCase = await util.zoomIn(this.modPage);
await expect(zoomInCase).toBeTruthy();
const zoomOutCase = await util.zoomOut(this.modPage);
await expect(zoomOutCase).toBeTruthy();
- await util.poll(this.modPage, this.userPage);
+ if (pollEnabled) await util.poll(this.modPage, this.userPage);
await util.nextSlide(this.modPage);
await util.previousSlide(this.modPage);
await util.annotation(this.modPage);
@@ -150,8 +152,9 @@ class CustomParameters extends MultiUsers {
async forceRestorePresentationOnNewPollResult(customParameter) {
await this.initUserPage(true, this.context, { useModMeetingId: true, customParameter })
- await this.userPage.waitAndClick(e.minimizePresentation);
- await util.poll(this.modPage, this.userPage);
+ const { presentationHidden,pollEnabled } = getSettings();
+ if (!presentationHidden) await this.userPage.waitAndClick(e.minimizePresentation);
+ if (pollEnabled) await util.poll(this.modPage, this.userPage);
await this.userPage.waitForSelector(e.smallToastMsg);
await this.userPage.checkElement(e.restorePresentation);
}
@@ -168,9 +171,8 @@ class CustomParameters extends MultiUsers {
await this.modPage.shareWebcam(false);
await this.modPage.waitAndClick(e.leaveVideo, VIDEO_LOADING_WAIT_TIME);
await this.modPage.waitForSelector(e.joinVideo);
- const parsedSettings = await this.modPage.getSettingsYaml();
- const videoPreviewTimeout = parseInt(parsedSettings.public.kurento.gUMTimeout);
- await this.modPage.shareWebcam(videoPreviewTimeout);
+ const { videoPreviewTimeout } = this.modPage.settings;
+ await this.modPage.shareWebcam(true, videoPreviewTimeout);
}
async mirrorOwnWebcam() {
diff --git a/bigbluebutton-tests/playwright/notifications/notifications.js b/bigbluebutton-tests/playwright/notifications/notifications.js
index 67f352e252..228d8d72ba 100644
--- a/bigbluebutton-tests/playwright/notifications/notifications.js
+++ b/bigbluebutton-tests/playwright/notifications/notifications.js
@@ -3,6 +3,7 @@ const e = require('../core/elements');
const util = require('./util');
const { openSettings } = require('../settings/util');
const { ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
+const { default: test } = require('@playwright/test');
class Notifications extends MultiUsers {
constructor(browser, context) {
@@ -38,6 +39,9 @@ class Notifications extends MultiUsers {
}
async raiseAndLowerHandNotification() {
+ const { raiseHandButton } = getSettings();
+ test.fail(!raiseHandButton, 'Raise/lower hand button is disabled');
+
await this.modPage.waitAndClick(e.raiseHandBtn);
await this.modPage.waitForSelector(e.smallToastMsg);
await util.checkNotificationText(this.modPage, e.raisingHandToast);
diff --git a/bigbluebutton-tests/playwright/polling/poll.js b/bigbluebutton-tests/playwright/polling/poll.js
index 435b62da97..4f22ab4d06 100644
--- a/bigbluebutton-tests/playwright/polling/poll.js
+++ b/bigbluebutton-tests/playwright/polling/poll.js
@@ -1,4 +1,4 @@
-const { expect } = require('@playwright/test');
+const { expect, default: test } = require('@playwright/test');
const { MultiUsers } = require('../user/multiusers');
const e = require('../core/elements');
const util = require('./util.js');
@@ -64,6 +64,9 @@ class Polling extends MultiUsers {
}
async pollResultsOnChat() {
+ const { pollChatMessage } = getSettings();
+ test.fail(!pollChatMessage, 'Poll results on chat is disabled');
+
await this.modPage.waitForSelector(e.whiteboard, ELEMENT_WAIT_LONGER_TIME);
await util.startPoll(this.modPage, true);
await this.modPage.waitAndClick(e.chatButton);
diff --git a/bigbluebutton-tests/playwright/polling/polling.spec.js b/bigbluebutton-tests/playwright/polling/polling.spec.js
index b5d05df9d3..a6a84722e7 100644
--- a/bigbluebutton-tests/playwright/polling/polling.spec.js
+++ b/bigbluebutton-tests/playwright/polling/polling.spec.js
@@ -12,7 +12,7 @@ test.describe.parallel('Polling', () => {
test('Create anonymous poll', async ({ browser, context, page }) => {
const polling = new Polling(browser, context);
await polling.initPages(page);
- await polling.pollAnonymous()
+ await polling.pollAnonymous();
});
test('Create quick poll - from the slide', async ({ browser, context, page }) => {
diff --git a/bigbluebutton-tests/playwright/polling/util.js b/bigbluebutton-tests/playwright/polling/util.js
index b33b0949bc..c5afb8f5fd 100644
--- a/bigbluebutton-tests/playwright/polling/util.js
+++ b/bigbluebutton-tests/playwright/polling/util.js
@@ -1,6 +1,11 @@
+const { default: test } = require('@playwright/test');
const e = require('../core/elements.js');
+const { getSettings } = require('../core/settings.js');
async function openPoll(test) {
+ const { pollEnabled } = getSettings();
+ test.fail(!pollEnabled, 'Polling is disabled');
+
await test.waitAndClick(e.actions);
await test.waitAndClick(e.polling);
await test.waitForSelector(e.hidePollDesc);
diff --git a/bigbluebutton-tests/playwright/presentation/presentation.js b/bigbluebutton-tests/playwright/presentation/presentation.js
index 58e0aee2b9..4f698ab2c5 100644
--- a/bigbluebutton-tests/playwright/presentation/presentation.js
+++ b/bigbluebutton-tests/playwright/presentation/presentation.js
@@ -1,10 +1,11 @@
-const { expect } = require('@playwright/test');
+const { expect, default: test } = require('@playwright/test');
const { MultiUsers } = require('../user/multiusers');
const Page = require('../core/page');
const e = require('../core/elements');
const { checkSvgIndex, getSvgOuterHtml, uploadPresentation } = require('./util.js');
const { ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
const { sleep } = require('../core/helpers');
+const { getSettings } = require('../core/settings');
class Presentation extends MultiUsers {
constructor(browser, context) {
@@ -31,8 +32,11 @@ class Presentation extends MultiUsers {
}
async hideAndRestorePresentation() {
- await this.modPage.waitForSelector(e.whiteboard);
- await this.modPage.waitAndClick(e.minimizePresentation);
+ const { presentationHidden } = getSettings();
+ if (!presentationHidden) {
+ await this.modPage.waitForSelector(e.whiteboard);
+ await this.modPage.waitAndClick(e.minimizePresentation);
+ }
await this.modPage.wasRemoved(e.presentationContainer);
await this.modPage.waitAndClick(e.restorePresentation);
@@ -40,6 +44,9 @@ class Presentation extends MultiUsers {
}
async startExternalVideo() {
+ const { externalVideoPlayer } = getSettings();
+ test.fail(!externalVideoPlayer, 'External video is disabled');
+
await this.modPage.waitForSelector(e.whiteboard);
await this.modPage.waitAndClick(e.actions);
await this.modPage.waitAndClick(e.shareExternalVideoBtn);
@@ -73,6 +80,9 @@ class Presentation extends MultiUsers {
}
async allowAndDisallowDownload(testInfo) {
+ const { presentationDownloadable } = getSettings();
+ test.fail(!presentationDownloadable, 'Presentation download is disable');
+
// allow the presentation download
await this.modPage.waitForSelector(e.whiteboard, ELEMENT_WAIT_LONGER_TIME);
await this.modPage.waitAndClick(e.actions);
diff --git a/bigbluebutton-tests/playwright/screenshare/screenshare.js b/bigbluebutton-tests/playwright/screenshare/screenshare.js
index 01c4244282..e9dd40aea9 100644
--- a/bigbluebutton-tests/playwright/screenshare/screenshare.js
+++ b/bigbluebutton-tests/playwright/screenshare/screenshare.js
@@ -1,6 +1,8 @@
+const { default: test } = require('@playwright/test');
const Page = require('../core/page');
-const { startScreenshare, getScreenShareBreakoutContainer } = require('./util');
+const { startScreenshare } = require('./util');
const e = require('../core/elements');
+const { getSettings } = require('../core/settings');
class ScreenShare extends Page {
constructor(browser, page) {
@@ -8,6 +10,8 @@ class ScreenShare extends Page {
}
async startSharing() {
+ const { screensharingEnabled } = getSettings();
+ test.fail(!screensharingEnabled, 'Screensharing is disabled');
await startScreenshare(this);
await this.hasElement(e.isSharingScreen);
}
diff --git a/bigbluebutton-tests/playwright/sharednotes/sharednotes.js b/bigbluebutton-tests/playwright/sharednotes/sharednotes.js
index a48ed35108..4da29c242b 100644
--- a/bigbluebutton-tests/playwright/sharednotes/sharednotes.js
+++ b/bigbluebutton-tests/playwright/sharednotes/sharednotes.js
@@ -1,4 +1,6 @@
+const { default: test } = require('@playwright/test');
const Page = require('../core/page');
+const { getSettings } = require('../core/settings');
const { startSharedNotes } = require('./util');
class SharedNotes extends Page {
@@ -7,6 +9,8 @@ class SharedNotes extends Page {
}
async openSharedNotes() {
+ const { sharedNotesEnabled } = getSettings();
+ test.fail(!sharedNotesEnabled, 'Shared notes is disabled');
await startSharedNotes(this);
}
}
diff --git a/bigbluebutton-tests/playwright/user/multiusers.js b/bigbluebutton-tests/playwright/user/multiusers.js
index 96c681c2c3..53151f1835 100644
--- a/bigbluebutton-tests/playwright/user/multiusers.js
+++ b/bigbluebutton-tests/playwright/user/multiusers.js
@@ -1,10 +1,11 @@
-const { expect } = require('@playwright/test');
+const { expect, default: test } = require('@playwright/test');
const Page = require('../core/page');
const e = require('../core/elements');
const { waitAndClearNotification } = require('../notifications/util');
const { sleep } = require('../core/helpers');
const { checkAvatarIcon, checkIsPresenter } = require('./util');
const { checkTextContent } = require('../core/util');
+const { getSettings } = require('../core/settings');
class MultiUsers {
constructor(browser, context) {
@@ -119,20 +120,16 @@ class MultiUsers {
await this.modPage2.wasRemoved(e.manageUsers);
}
- async raiseHandTest() {
+ async raiseAndLowerHand() {
+ const { raiseHandButton } = getSettings();
+ test.fail(!raiseHandButton, 'Raise/lower hand button is disabled');
+
await this.userPage.waitAndClick(e.raiseHandBtn);
await this.userPage.hasElement(e.lowerHandBtn);
- }
-
- async getAvatarColorAndCompareWithUserListItem() {
const getBackgroundColorComputed = (locator) => locator.evaluate((elem) => getComputedStyle(elem).backgroundColor);
-
const avatarInToastElementColor = this.modPage.getLocator(e.avatarsWrapperAvatar);
const avatarInUserListColor = this.modPage.getLocator(`${e.userListItem} > div ${e.userAvatar}`);
await expect(getBackgroundColorComputed(avatarInToastElementColor)).toStrictEqual(getBackgroundColorComputed(avatarInUserListColor));
- }
-
- async lowerHandTest() {
await waitAndClearNotification(this.userPage);
await this.userPage.waitAndClick(e.lowerHandBtn);
await this.userPage.hasElement(e.raiseHandBtn);
diff --git a/bigbluebutton-tests/playwright/user/user.spec.js b/bigbluebutton-tests/playwright/user/user.spec.js
index a4ae53b427..d5dc519108 100644
--- a/bigbluebutton-tests/playwright/user/user.spec.js
+++ b/bigbluebutton-tests/playwright/user/user.spec.js
@@ -12,9 +12,7 @@ test.describe.parallel('User', () => {
test('Raise and lower Hand Toast', async ({ browser, context, page }) => {
const multiusers = new MultiUsers(browser, context);
await multiusers.initPages(page);
- await multiusers.raiseHandTest();
- await multiusers.getAvatarColorAndCompareWithUserListItem();
- await multiusers.lowerHandTest();
+ await multiusers.raiseAndLowerHand();
});
test('Toggle user list', async ({ browser, context, page }) => {
diff --git a/bigbluebutton-tests/playwright/webcam/webcam.js b/bigbluebutton-tests/playwright/webcam/webcam.js
index a4222fc115..051c8b8125 100644
--- a/bigbluebutton-tests/playwright/webcam/webcam.js
+++ b/bigbluebutton-tests/playwright/webcam/webcam.js
@@ -10,20 +10,15 @@ class Webcam extends Page {
}
async share() {
- const parsedSettings = await this.getSettingsYaml();
- const videoPreviewTimeout = parseInt(parsedSettings.public.kurento.gUMTimeout);
- await this.shareWebcam(videoPreviewTimeout);
-
+ const { videoPreviewTimeout, skipVideoPreview, skipVideoPreviewOnFirstJoin } = this.settings;
+ await this.shareWebcam(!(skipVideoPreview || skipVideoPreviewOnFirstJoin), videoPreviewTimeout);
await this.hasElement('video');
}
async checksContent() {
- const parsedSettings = await this.getSettingsYaml();
- const videoPreviewTimeout = parseInt(parsedSettings.public.kurento.gUMTimeout);
-
- await this.shareWebcam(videoPreviewTimeout);
+ const { videoPreviewTimeout, skipVideoPreview, skipVideoPreviewOnFirstJoin } = this.settings;
+ await this.shareWebcam(!(skipVideoPreview || skipVideoPreviewOnFirstJoin), videoPreviewTimeout);
const respUser = await webcamContentCheck(this);
-
await expect(respUser).toBeTruthy();
}
@@ -37,9 +32,8 @@ class Webcam extends Page {
async webcamLayoutStart() {
await this.joinMicrophone();
- const parsedSettings = await this.getSettingsYaml();
- const videoPreviewTimeout = parseInt(parsedSettings.public.kurento.gUMTimeout);
- await this.shareWebcam(videoPreviewTimeout);
+ const { videoPreviewTimeout, skipVideoPreview, skipVideoPreviewOnFirstJoin } = this.settings;
+ await this.shareWebcam(!(skipVideoPreview || skipVideoPreviewOnFirstJoin), videoPreviewTimeout);
}
}
From a6eefb47370b7a529fa0d9430c630b8175d76668 Mon Sep 17 00:00:00 2001
From: Joao Victor
Date: Tue, 29 Mar 2022 14:08:59 -0300
Subject: [PATCH 031/180] fix user feedback upload error message
---
.../imports/ui/components/meeting-ended/component.jsx | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
index 141b4cd666..edaaf02ff9 100755
--- a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
@@ -231,7 +231,13 @@ class MeetingEnded extends PureComponent {
});
fetch(url, options).catch((e) => {
- logger.warn(e);
+ logger.warn({
+ logCode: 'user_feedback_not_sent_error',
+ extraInfo: {
+ errorName: e.name,
+ errorMessage: e.message,
+ },
+ }, `Unable to send feedback: ${e.message}`);
});
}
From 2330ad8a8f38ecbad138e1ebc567ce799cf121e9 Mon Sep 17 00:00:00 2001
From: gabriellpr
Date: Tue, 29 Mar 2022 15:29:58 -0300
Subject: [PATCH 032/180] unmutable external video solved
---
.../imports/ui/components/external-video-player/component.jsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx b/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx
index c9de7e4da2..c904caaecb 100644
--- a/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx
@@ -384,10 +384,10 @@ class VideoPlayer extends Component {
}
getMuted() {
- const { mutedByEchoTest } = this.state;
+ const { mutedByEchoTest, muted } = this.state;
const intPlayer = this.player && this.player.getInternalPlayer();
- return intPlayer && intPlayer.isMuted && intPlayer.isMuted() && !mutedByEchoTest;
+ return (intPlayer && intPlayer?.isMuted && intPlayer?.isMuted?.() && !mutedByEchoTest) || muted;
}
autoPlayBlockDetected() {
From 247794ff45f4f424f45aa3b5eb83287f1f381608 Mon Sep 17 00:00:00 2001
From: Guilherme Leme
Date: Wed, 30 Mar 2022 10:07:46 -0300
Subject: [PATCH 033/180] [issue-14335] - Changes in review, the random
password has been added if a null one is sent while creating the meeting to
avoid crashes in old integrations.
---
.../api/ParamsProcessorUtil.java | 10 +++--
.../ModeratorPasswordConstraint.java | 22 ----------
.../api/model/shared/ModeratorPassword.java | 3 --
.../validator/ModeratorPasswordValidator.java | 44 -------------------
.../web/controllers/ApiController.groovy | 36 ++++++++++-----
5 files changed, 33 insertions(+), 82 deletions(-)
delete mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/model/constraint/ModeratorPasswordConstraint.java
delete mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/model/validator/ModeratorPasswordValidator.java
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java
index be19ad90c9..defc1fc22e 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java
@@ -351,8 +351,8 @@ public class ParamsProcessorUtil {
String externalMeetingId = params.get(ApiParams.MEETING_ID);
- String viewerPass = params.get(ApiParams.ATTENDEE_PW);
- String modPass = params.get(ApiParams.MODERATOR_PW);
+ String viewerPass = processPassword(params.get(ApiParams.ATTENDEE_PW));
+ String modPass = processPassword(params.get(ApiParams.MODERATOR_PW));
// Get the digits for voice conference for users joining through the
// phone.
@@ -717,7 +717,11 @@ public class ParamsProcessorUtil {
return DigestUtils.sha1Hex(extMeetingId);
}
- public boolean hasChecksumAndQueryString(String checksum, String queryString) {
+ public String processPassword(String pass) {
+ return StringUtils.isEmpty(pass) ? RandomStringUtils.randomAlphanumeric(8) : pass;
+ }
+
+ public boolean hasChecksumAndQueryString(String checksum, String queryString) {
return (! StringUtils.isEmpty(checksum) && StringUtils.isEmpty(queryString));
}
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/model/constraint/ModeratorPasswordConstraint.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/model/constraint/ModeratorPasswordConstraint.java
deleted file mode 100755
index c26f01d758..0000000000
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/model/constraint/ModeratorPasswordConstraint.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.bigbluebutton.api.model.constraint;
-
-import org.bigbluebutton.api.model.validator.ModeratorPasswordValidator;
-
-import javax.validation.Constraint;
-import javax.validation.Payload;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-@Constraint(validatedBy = ModeratorPasswordValidator.class)
-@Target(TYPE)
-@Retention(RUNTIME)
-public @interface ModeratorPasswordConstraint {
-
- String key() default "invalidPassword";
- String message() default "The supplied moderator password is incorrect";
- Class>[] groups() default {};
- Class extends Payload>[] payload() default {};
-}
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/model/shared/ModeratorPassword.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/model/shared/ModeratorPassword.java
index 6bffcba57e..01f108cb3d 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/model/shared/ModeratorPassword.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/model/shared/ModeratorPassword.java
@@ -1,6 +1,3 @@
package org.bigbluebutton.api.model.shared;
-import org.bigbluebutton.api.model.constraint.ModeratorPasswordConstraint;
-
-@ModeratorPasswordConstraint
public class ModeratorPassword extends Password {}
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/model/validator/ModeratorPasswordValidator.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/model/validator/ModeratorPasswordValidator.java
deleted file mode 100755
index 362f34172c..0000000000
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/model/validator/ModeratorPasswordValidator.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.bigbluebutton.api.model.validator;
-
-import org.bigbluebutton.api.domain.Meeting;
-import org.bigbluebutton.api.model.constraint.ModeratorPasswordConstraint;
-import org.bigbluebutton.api.model.shared.ModeratorPassword;
-import org.bigbluebutton.api.service.ServiceUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.validation.ConstraintValidator;
-import javax.validation.ConstraintValidatorContext;
-
-public class ModeratorPasswordValidator implements ConstraintValidator {
-
- private static Logger log = LoggerFactory.getLogger(ModeratorPasswordValidator.class);
-
-
- @Override
- public void initialize(ModeratorPasswordConstraint constraintAnnotation) {}
-
- @Override
- public boolean isValid(ModeratorPassword moderatorPassword, ConstraintValidatorContext context) {
- log.info("Validating password {} for meeting with ID {}",
- moderatorPassword.getPassword(), moderatorPassword.getMeetingID());
-
- Meeting meeting = ServiceUtils.findMeetingFromMeetingID(moderatorPassword.getMeetingID());
-
- if(meeting == null) {
- return false;
- }
-
- String actualPassword = meeting.getModeratorPassword();
- String providedPassword = moderatorPassword.getPassword();
- if (providedPassword != null && !providedPassword.isEmpty()){
- log.info("Actual password: {}", actualPassword);
- log.info("Provided password: {}", providedPassword);
-
- if (actualPassword != null && !actualPassword.isEmpty() && !providedPassword.equals(actualPassword)) {
- return false;
- }
- }
- return true;
- }
-}
diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy
index a49d3da683..9f684e442a 100755
--- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy
+++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy
@@ -264,15 +264,34 @@ class ApiController {
// Now determine if this user is a moderator or a viewer.
String role = null;
- if (attPW != null && !attPW.isEmpty()){
+
+ // First Case: send a valid role
+ if (!StringUtils.isEmpty(params.role) && roles.containsKey(params.role.toLowerCase())) {
+ role = roles.get(params.role.toLowerCase());
+
+ // Second case: role is not present or valid BUT there is password
+ } else if (attPW != null && !attPW.isEmpty()){
+ // Check if the meeting has passwords
if ((meeting.getModeratorPassword() != null && !meeting.getModeratorPassword().isEmpty())
- && (meeting.getViewerPassword() != null && !meeting.getViewerPassword().isEmpty())){
+ && (meeting.getViewerPassword() != null && !meeting.getViewerPassword().isEmpty())){
+ // Check which role does the user belong
if (meeting.getModeratorPassword().equals(attPW)) {
role = Meeting.ROLE_MODERATOR
} else if (meeting.getViewerPassword().equals(attPW)) {
role = Meeting.ROLE_ATTENDEE
+ } else {
+ log.debug("Password does not match any of the registered ones");
+ response.addHeader("Cache-Control", "no-cache")
+ withFormat {
+ xml {
+ render(text: responseBuilder.buildError("Params required", "You must enter a valid password",
+ RESP_CODE_FAILED), contentType: "text/xml")
+ }
+ }
+ return
}
- } else if (StringUtils.isEmpty(params.role)){
+ // In this case, the meeting doesn't have any password registered and there is no role param
+ } else {
log.debug("This meeting doesn't have any password");
response.addHeader("Cache-Control", "no-cache")
withFormat {
@@ -283,18 +302,15 @@ class ApiController {
}
return
}
- }
- if (!StringUtils.isEmpty(params.role) && roles.containsKey(params.role.toLowerCase())) {
- role = roles.get(params.role.toLowerCase());
- } else if (attPW == null){
- log.info("You must either send the valid role of the user, or " +
- "the password, sould the meeting has one");
+ // Third case: No valid role + no valid password
+ } else {
+ log.debug("No matching params encountered");
response.addHeader("Cache-Control", "no-cache")
withFormat {
xml {
render(text: responseBuilder.buildError("Params required", "You must either send the valid role of the user, or " +
- "the password, sould the meeting has one", RESP_CODE_FAILED), contentType: "text/xml")
+ "the password, sould the meeting has one.", RESP_CODE_FAILED), contentType: "text/xml")
}
}
return
From 7a476874a4077dfb29fc3942fe7f6760db1c38ba Mon Sep 17 00:00:00 2001
From: gabriellpr
Date: Wed, 30 Mar 2022 10:46:04 -0300
Subject: [PATCH 034/180] getmuted return modified
---
.../imports/ui/components/external-video-player/component.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx b/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx
index c904caaecb..6e32268646 100644
--- a/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx
@@ -387,7 +387,7 @@ class VideoPlayer extends Component {
const { mutedByEchoTest, muted } = this.state;
const intPlayer = this.player && this.player.getInternalPlayer();
- return (intPlayer && intPlayer?.isMuted && intPlayer?.isMuted?.() && !mutedByEchoTest) || muted;
+ return (intPlayer && intPlayer.isMuted && intPlayer.isMuted?.() && !mutedByEchoTest) || muted;
}
autoPlayBlockDetected() {
From a356109795904f752a2692d3f8586f347953406c Mon Sep 17 00:00:00 2001
From: Arthurk12
Date: Mon, 28 Mar 2022 18:50:10 +0000
Subject: [PATCH 035/180] fix(microphone): modal not showing errors
Adds error handling to microphone join.
---
.../audio/audio-modal/component.jsx | 47 ++++++++++---------
1 file changed, 24 insertions(+), 23 deletions(-)
diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx
index e730338798..e2f4bdfabd 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx
@@ -270,28 +270,7 @@ class AudioModal extends Component {
disableActions: false,
});
}).catch((err) => {
- const { type } = err;
- switch (type) {
- case 'MEDIA_ERROR':
- this.setState({
- content: 'help',
- errCode: 0,
- disableActions: false,
- });
- break;
- case 'CONNECTION_ERROR':
- this.setState({
- errCode: 0,
- disableActions: false,
- });
- break;
- default:
- this.setState({
- errCode: 0,
- disableActions: false,
- });
- break;
- }
+ this.handleJoinMicrophoneError(err);
});
}
@@ -345,7 +324,29 @@ class AudioModal extends Component {
this.setState({
disableActions: false,
});
- }).catch(this.handleGoToAudioOptions);
+ }).catch((err) => {
+ this.handleJoinMicrophoneError(err);
+ });
+ }
+
+ handleJoinMicrophoneError(err) {
+ const { type } = err;
+ switch (type) {
+ case 'MEDIA_ERROR':
+ this.setState({
+ content: 'help',
+ errCode: 0,
+ disableActions: false,
+ });
+ break;
+ case 'CONNECTION_ERROR':
+ default:
+ this.setState({
+ errCode: 0,
+ disableActions: false,
+ });
+ break;
+ }
}
setContent(content) {
From 375cf41498701530ed082412fecbe0adbb98f342 Mon Sep 17 00:00:00 2001
From: Ramon Souza
Date: Thu, 17 Mar 2022 11:06:21 -0300
Subject: [PATCH 036/180] fix smart layout invisible video bug
---
.../layout/layout-manager/smartLayout.jsx | 21 ++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/bigbluebutton-html5/imports/ui/components/layout/layout-manager/smartLayout.jsx b/bigbluebutton-html5/imports/ui/components/layout/layout-manager/smartLayout.jsx
index 42bb65ac80..e8bdce29a5 100644
--- a/bigbluebutton-html5/imports/ui/components/layout/layout-manager/smartLayout.jsx
+++ b/bigbluebutton-html5/imports/ui/components/layout/layout-manager/smartLayout.jsx
@@ -120,6 +120,12 @@ class SmartLayout extends Component {
cameraDock: {
numCameras: input.cameraDock.numCameras,
},
+ externalVideo: {
+ hasExternalVideo: input.externalVideo.hasExternalVideo,
+ },
+ screenShare: {
+ hasScreenShare: input.screenShare.hasScreenShare,
+ },
}, INITIAL_INPUT_STATE),
});
}
@@ -343,10 +349,14 @@ class SmartLayout extends Component {
const {
input, fullscreen, isRTL, deviceType,
} = layoutContextState;
- const { presentation } = input;
- const { isOpen } = presentation;
+ const { presentation, externalVideo, screenShare } = input;
+ const { isOpen, currentSlide } = presentation;
const { camerasMargin, presentationToolbarMinWidth } = DEFAULT_VALUES;
+ const { num: currentSlideNumber } = currentSlide;
+ const { hasExternalVideo } = externalVideo;
+ const { hasScreenShare } = screenShare;
+
const cameraDockBounds = {};
cameraDockBounds.isCameraHorizontal = false;
const mediaBoundsWidth = (mediaBounds.width > presentationToolbarMinWidth
@@ -360,7 +370,7 @@ class SmartLayout extends Component {
cameraDockBounds.right = isRTL ? sidebarSize : null;
cameraDockBounds.zIndex = 1;
- if (!isOpen) {
+ if (!isOpen || (currentSlideNumber === 0 && !hasExternalVideo && !hasScreenShare)) {
cameraDockBounds.width = mediaAreaBounds.width;
cameraDockBounds.maxWidth = mediaAreaBounds.width;
cameraDockBounds.height = mediaAreaBounds.height;
@@ -447,13 +457,14 @@ class SmartLayout extends Component {
input, fullscreen, isRTL, deviceType,
} = layoutContextState;
const { presentation, externalVideo, screenShare } = input;
- const { isOpen } = presentation;
+ const { isOpen, currentSlide } = presentation;
+ const { num: currentSlideNumber } = currentSlide;
const { hasExternalVideo } = externalVideo;
const { hasScreenShare } = screenShare;
const mediaBounds = {};
const { element: fullscreenElement } = fullscreen;
- if (!isOpen) {
+ if (!isOpen || (currentSlideNumber === 0 && !hasExternalVideo && !hasScreenShare)) {
mediaBounds.width = 0;
mediaBounds.height = 0;
mediaBounds.top = 0;
From 92688ca93ab142740d40c602152639e6f984439b Mon Sep 17 00:00:00 2001
From: Ramon Souza
Date: Wed, 30 Mar 2022 15:31:19 -0300
Subject: [PATCH 037/180] prevent setSwapLayout race condition
---
bigbluebutton-html5/imports/startup/client/base.jsx | 3 +++
bigbluebutton-html5/imports/ui/components/app/component.jsx | 2 --
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/bigbluebutton-html5/imports/startup/client/base.jsx b/bigbluebutton-html5/imports/startup/client/base.jsx
index c7b664cb8e..2f123858c6 100755
--- a/bigbluebutton-html5/imports/startup/client/base.jsx
+++ b/bigbluebutton-html5/imports/startup/client/base.jsx
@@ -23,6 +23,7 @@ import { LayoutContextFunc } from '../../ui/components/layout/context';
import VideoService from '/imports/ui/components/video-provider/service';
import DebugWindow from '/imports/ui/components/debug-window/component';
import { ACTIONS, PANELS } from '../../ui/components/layout/enums';
+import MediaService from '/imports/ui/components/media/service';
const CHAT_CONFIG = Meteor.settings.public.chat;
const CHAT_ENABLED = CHAT_CONFIG.enabled;
@@ -95,6 +96,8 @@ class Base extends Component {
value: usersVideo.length,
});
+ MediaService.setSwapLayout(layoutContextDispatch);
+
const {
userID: localUserId,
} = Auth;
diff --git a/bigbluebutton-html5/imports/ui/components/app/component.jsx b/bigbluebutton-html5/imports/ui/components/app/component.jsx
index a522a50334..ddd1e455be 100755
--- a/bigbluebutton-html5/imports/ui/components/app/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/app/component.jsx
@@ -20,7 +20,6 @@ import BannerBarContainer from '/imports/ui/components/banner-bar/container';
import WaitingNotifierContainer from '/imports/ui/components/waiting-users/alert/container';
import LockNotifier from '/imports/ui/components/lock-viewers/notify/container';
import StatusNotifier from '/imports/ui/components/status-notifier/container';
-import MediaService from '/imports/ui/components/media/service';
import ManyWebcamsNotifier from '/imports/ui/components/video-provider/many-users-notify/container';
import UploaderContainer from '/imports/ui/components/presentation/presentation-uploader/container';
import RandomUserSelectContainer from '/imports/ui/components/modal/random-user/container';
@@ -171,7 +170,6 @@ class App extends Component {
value: !hidePresentation,
});
- MediaService.setSwapLayout(layoutContextDispatch);
Modal.setAppElement('#app');
const fontSize = isMobile() ? MOBILE_FONT_SIZE : DESKTOP_FONT_SIZE;
From 1994c32b43dd573064c712be5eb3228a711cecba Mon Sep 17 00:00:00 2001
From: Joao Victor
Date: Thu, 31 Mar 2022 13:58:41 -0300
Subject: [PATCH 038/180] Keep user options dropdown open even after updating
user list order
---
.../ui/components/common/menu/component.jsx | 10 ++++++++++
.../user-participants/component.jsx | 2 ++
.../user-list-item/component.jsx | 16 ++++++++++++++--
.../user-list-item/container.jsx | 1 +
4 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/bigbluebutton-html5/imports/ui/components/common/menu/component.jsx b/bigbluebutton-html5/imports/ui/components/common/menu/component.jsx
index a30d2c999a..6aa1bae968 100644
--- a/bigbluebutton-html5/imports/ui/components/common/menu/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/common/menu/component.jsx
@@ -33,6 +33,15 @@ class BBBMenu extends React.Component {
this.handleClose = this.handleClose.bind(this);
}
+ componentDidUpdate() {
+ const { anchorEl } = this.state;
+ if (this.props.open === false && anchorEl) {
+ this.setState({ anchorEl: null });
+ } else if (this.props.open === true && !anchorEl) {
+ this.setState({ anchorEl: this.anchorElRef });
+ }
+ }
+
handleClick(event) {
this.setState({ anchorEl: event.currentTarget });
};
@@ -126,6 +135,7 @@ class BBBMenu extends React.Component {
this.handleClick(e);
}}
accessKey={this.props?.accessKey}
+ ref={(ref) => this.anchorElRef = ref}
>
{trigger}
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx
index b8374ce81f..0017d62731 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx
@@ -81,6 +81,8 @@ class UserParticipants extends Component {
this.handleClickSelectedUser,
);
}
+
+ window.addEventListener('beforeunload', () => Session.set('dropdownOpenUserId', null));
}
shouldComponentUpdate(nextProps) {
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/component.jsx
index 3b1922eb74..bd633bb691 100644
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/component.jsx
@@ -208,6 +208,16 @@ class UserListItem extends PureComponent {
this.seperator = _.uniqueId('action-separator-');
}
+ componentDidUpdate() {
+ const { user, selectedUserId } = this.props;
+ const { selected } = this.state;
+ if (selectedUserId === user.userId && !selected) {
+ this.setState({ selected: true });
+ } else if (selectedUserId !== user.userId && selected) {
+ this.setState({ selected: false });
+ }
+ }
+
handleScroll() {
this.setState({
isActionsOpen: false,
@@ -646,6 +656,7 @@ class UserListItem extends PureComponent {
userLastBreakout,
isMe,
isRTL,
+ selectedUserId,
} = this.props;
const {
@@ -814,7 +825,7 @@ class UserListItem extends PureComponent {
isActionsOpen={isActionsOpen}
selected={selected === true}
tabIndex={-1}
- onClick={() => this.setState({ selected: true })}
+ onClick={() => this.setState({ selected: true }, () => Session.set('dropdownOpenUserId', user.userId))}
onKeyPress={() => { }}
role="button"
>
@@ -824,7 +835,8 @@ class UserListItem extends PureComponent {
}
actions={actions}
selectedEmoji={user.emoji}
- onCloseCallback={() => this.setState({ selected: false, showNestedOptions: false })}
+ onCloseCallback={() => this.setState({ selected: false }, () => Session.set('dropdownOpenUserId', null))}
+ open={selectedUserId === user.userId}
/>
);
}
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/container.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/container.jsx
index dc0a8c393c..e5d93a482a 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/container.jsx
@@ -56,5 +56,6 @@ export default withTracker(({ user }) => {
getEmojiList: UserListService.getEmojiList(),
getEmoji: UserListService.getEmoji(),
usersProp: UserListService.getUsersProp(),
+ selectedUserId: Session.get('dropdownOpenUserId'),
};
})(UserListItemContainer);
From bd445cffd5ccf4e1e71919786e352a3bcb318dce Mon Sep 17 00:00:00 2001
From: Anton Georgiev
Date: Thu, 31 Mar 2022 18:19:47 +0000
Subject: [PATCH 039/180] chore: update minimist for bbb-html5
---
bigbluebutton-html5/package-lock.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/bigbluebutton-html5/package-lock.json b/bigbluebutton-html5/package-lock.json
index 68864ad468..935417a967 100644
--- a/bigbluebutton-html5/package-lock.json
+++ b/bigbluebutton-html5/package-lock.json
@@ -4188,9 +4188,9 @@
}
},
"minimist": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
- "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
+ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
"dev": true
},
"minimist-options": {
From cc112364a540dd7d346062b348a394064aa98425 Mon Sep 17 00:00:00 2001
From: Anton Georgiev
Date: Thu, 31 Mar 2022 18:21:06 +0000
Subject: [PATCH 040/180] chore: update minimist and node-forge on
learning-dashboard
---
bbb-learning-dashboard/package-lock.json | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/bbb-learning-dashboard/package-lock.json b/bbb-learning-dashboard/package-lock.json
index f033257c0e..6a0d503fbb 100644
--- a/bbb-learning-dashboard/package-lock.json
+++ b/bbb-learning-dashboard/package-lock.json
@@ -8044,9 +8044,9 @@
}
},
"minimist": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
- "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
+ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
"mkdirp": {
"version": "0.5.5",
@@ -8105,9 +8105,9 @@
}
},
"node-forge": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz",
- "integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w=="
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
+ "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="
},
"node-int64": {
"version": "0.4.0",
From fd3c74e85af12daff75a3195f80219d02ec962d4 Mon Sep 17 00:00:00 2001
From: Localization Server
Date: Thu, 31 Mar 2022 19:03:42 +0000
Subject: [PATCH 041/180] chore: Pulled the latest 2.4 HTML5 locales from
Transifex
---
bigbluebutton-html5/package-lock.json | 10 +-
bigbluebutton-html5/public/locales/bg_BG.json | 312 +++++++++++++++++-
bigbluebutton-html5/public/locales/es.json | 2 +-
bigbluebutton-html5/public/locales/et.json | 17 +-
bigbluebutton-html5/public/locales/fa_IR.json | 2 +
bigbluebutton-html5/public/locales/fr.json | 8 +-
bigbluebutton-html5/public/locales/it_IT.json | 6 +
bigbluebutton-html5/public/locales/pt_BR.json | 20 ++
bigbluebutton-html5/public/locales/ru.json | 2 +-
9 files changed, 366 insertions(+), 13 deletions(-)
diff --git a/bigbluebutton-html5/package-lock.json b/bigbluebutton-html5/package-lock.json
index 935417a967..d942e40853 100644
--- a/bigbluebutton-html5/package-lock.json
+++ b/bigbluebutton-html5/package-lock.json
@@ -3503,6 +3503,11 @@
"lru-cache": "^6.0.0"
}
},
+ "trim-newlines": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.0.2.tgz",
+ "integrity": "sha512-GJtWyq9InR/2HRiLZgpIKv+ufIKrVrvjQWEj7PxAXNc5dwbNJkqhAUoAGgzRmULAnoOM5EIpveYd3J2VeSAIew=="
+ },
"type-fest": {
"version": "0.18.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz",
@@ -5992,11 +5997,6 @@
"punycode": "^2.1.1"
}
},
- "trim-newlines": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz",
- "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw=="
- },
"triple-beam": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
diff --git a/bigbluebutton-html5/public/locales/bg_BG.json b/bigbluebutton-html5/public/locales/bg_BG.json
index 0f88674f2a..71c070c0b0 100644
--- a/bigbluebutton-html5/public/locales/bg_BG.json
+++ b/bigbluebutton-html5/public/locales/bg_BG.json
@@ -1,6 +1,7 @@
{
"app.home.greeting": "Вашата презентация ще започне всеки момент",
"app.chat.submitLabel": "Изпрати",
+ "app.chat.loading": "Заредени чат съобщения: {0}%",
"app.chat.errorMaxMessageLength": "Съобщението е с {0} знак(а) по-дълго ",
"app.chat.disconnected": "Вие не сте свързан, съобщението не може да бъде изпратено",
"app.chat.locked": "Разговора е заключен, съобщението не може да бъде изпратено",
@@ -24,6 +25,8 @@
"app.chat.multi.typing": "Пишат няколко потребителя",
"app.chat.one.typing": "{0} пише",
"app.chat.two.typing": "{0} и {1} пишат",
+ "app.chat.copySuccess": "Копиране на чата",
+ "app.chat.copyErr": "Проблем с копирането на чата",
"app.captions.label": "Субтитри",
"app.captions.menu.close": "Затвори",
"app.captions.menu.start": "Старт",
@@ -48,10 +51,14 @@
"app.captions.pad.dictationStop": "Спри диктовката",
"app.captions.pad.dictationOnDesc": "Включи гласово разпознаване",
"app.captions.pad.dictationOffDesc": "Изключи гласово разпознаване",
+ "app.captions.pad.speechRecognitionStop": "Разпознаването на реч е прекратено поради несъвместимост в браузъра или период на мълчание",
+ "app.textInput.sendLabel": "Изпращане",
+ "app.title.defaultViewLabel": "Стандартен изглед на презентацията",
"app.note.title": "Споделени бележки",
"app.note.label": "Бележка",
"app.note.hideNoteLabel": "Скрий бележката",
"app.note.tipLabel": "Натиснете Esc за връщане в редактора",
+ "app.note.locked": "Заключен",
"app.user.activityCheck": "Проверка на потребителската активност",
"app.user.activityCheck.label": "Проверка дали потребителя е още в срещата ({0})",
"app.user.activityCheck.check": "Провери",
@@ -67,8 +74,13 @@
"app.userList.byModerator": "от (Модератор)",
"app.userList.label": "Списък на потребителите",
"app.userList.toggleCompactView.label": "Превключи към режим на компактен преглед",
+ "app.userList.moderator": "Модератор",
+ "app.userList.mobile": "Мобилен",
"app.userList.guest": "Гост",
+ "app.userList.sharingWebcam": "Web камера",
"app.userList.menuTitleContext": "Опции",
+ "app.userList.chatListItem.unreadSingular": "Едно ново съобщение",
+ "app.userList.chatListItem.unreadPlural": "{0} нови съобщения",
"app.userList.menu.chat.label": "Започни личен чат",
"app.userList.menu.clearStatus.label": "Изчисти статуса",
"app.userList.menu.removeUser.label": "Изключи потребителя",
@@ -76,6 +88,11 @@
"app.userlist.menu.removeConfirmation.desc": "Предотврати повторно влизане на потребителя",
"app.userList.menu.muteUserAudio.label": "Заглуши",
"app.userList.menu.unmuteUserAudio.label": "включи микрофона",
+ "app.userList.menu.webcamPin.label": "Закачи потребителските камери",
+ "app.userList.menu.webcamUnpin.label": "Откачи потребителските камери",
+ "app.userList.menu.giveWhiteboardAccess.label" : "Предоставя достъп до дъската",
+ "app.userList.menu.removeWhiteboardAccess.label": "Премахва достъп до дъската",
+ "app.userList.menu.ejectUserCameras.label": "Затвори камерите",
"app.userList.userAriaLabel": "{0} {1} {2} Статус {3}",
"app.userList.menu.promoteUser.label": "Повиши до модератор",
"app.userList.menu.demoteUser.label": "Понижи до участник",
@@ -109,25 +126,44 @@
"app.userList.userOptions.enableNote": "Споделените забележки са разрешени",
"app.userList.userOptions.showUserList": "Участниците виждат списъка с потребителите",
"app.userList.userOptions.enableOnlyModeratorWebcam": "Вече можете да включите камерата си, всички ще ви виждат",
+ "app.userList.userOptions.savedNames.title": "Списък на потребителите в срещата {0} от {1}",
+ "app.userList.userOptions.sortedFirstName.heading": "Сортирани по собствено име:",
+ "app.userList.userOptions.sortedLastName.heading": "Сортирани по фамилия:",
"app.media.label": "Медия",
"app.media.autoplayAlertDesc": "Разреши достъпа",
"app.media.screenshare.start": "Споделянето на екрана започна",
"app.media.screenshare.end": "Споделянето на екрана приключи",
+ "app.media.screenshare.endDueToDataSaving": "Споделянето на екран е спряно за икономия на трафик",
"app.media.screenshare.unavailable": "Споделянето на екран не е възможно",
"app.media.screenshare.notSupported": " Споделянето на екран не е възможно с този браузър.",
"app.media.screenshare.autoplayBlockedDesc": "Нуждаем се от Вашето разрешение за да Ви покажем екрана на презентатора",
"app.media.screenshare.autoplayAllowLabel": "Виж споделения екран",
+ "app.screenshare.presenterLoadingLabel": "Вашият споделен екран се зарежда",
+ "app.screenshare.viewerLoadingLabel": "Екрана на презентатора се зарежда",
+ "app.screenshare.presenterSharingLabel": "В момента споделяте екрана си",
+ "app.screenshare.screenshareFinalError": "Код {0}. Невъзможно е споделянето на екрана",
+ "app.screenshare.screenshareRetryError": "Код {0}. Повторен опит за споделяне на екран",
+ "app.screenshare.screenshareRetryOtherEnvError": "Код {0}. Невъзможно е споделянето на екрана. Опитайте да използвате друг браузър или устройство",
+ "app.screenshare.screenshareUnsupportedEnv": "Код {0}. Браузърът не се поддържа. Опитайте да използвате друг браузър или устройство.",
+ "app.screenshare.screensharePermissionError": "Код {0}. Необходимо е разрешение за заснемане на екрана.",
"app.meeting.ended": "Тази сесия приключи",
"app.meeting.meetingTimeRemaining": "Оставащо време до края на срещата: {0}",
"app.meeting.meetingTimeHasEnded": "Времето изтече: Скоро срещата ще приключи",
+ "app.meeting.endedByUserMessage": "Сесията е прекратена от {0}",
+ "app.meeting.endedByNoModeratorMessageSingular": "Срещата е прекратена поради липса на модератор за повече от една минута.",
+ "app.meeting.endedByNoModeratorMessagePlural": "Срещата е прекратена поради липса на модератор за повече от {0} минути.",
"app.meeting.endedMessage": "Ще бъдете пренасочени към началния екран",
"app.meeting.alertMeetingEndsUnderMinutesSingular": "Срещата ще приключи до една минута",
"app.meeting.alertMeetingEndsUnderMinutesPlural": "Срещата ще приключи до {0} минути",
+ "app.meeting.alertBreakoutEndsUnderMinutesPlural": "Екипната стая ще се затвори след {0} минути.",
+ "app.meeting.alertBreakoutEndsUnderMinutesSingular": "Екипната стая ще се затвори след една минута.",
"app.presentation.hide": "Скрий презентацията",
"app.presentation.notificationLabel": "Текуща презентация",
+ "app.presentation.downloadLabel": "Download",
"app.presentation.slideContent": "Съдържание на слайда",
"app.presentation.startSlideContent": "Начало на слайда",
"app.presentation.endSlideContent": "Край на слайда",
+ "app.presentation.changedSlideContent": "Презентацията премина към слайд: {0}",
"app.presentation.emptySlideContent": "Няма съдържание в текущия слайд",
"app.presentation.presentationToolbar.noNextSlideDesc": "Край на презентацията",
"app.presentation.presentationToolbar.noPrevSlideDesc": "Начало на презентацията",
@@ -153,8 +189,10 @@
"app.presentation.presentationToolbar.fitToWidth": "Разшири максимално",
"app.presentation.presentationToolbar.fitToPage": "Запълни страницата",
"app.presentation.presentationToolbar.goToSlide": "Слайд {0}",
+ "app.presentation.placeholder": "Изчакване да бъде качена презентация",
"app.presentationUploder.title": "презентацията",
"app.presentationUploder.message": "Като презентатор имате възможност да качите Office документ или PDF файл. Препоръчваме PDF файл. Моля уверете се, че презентацията е избрана използвайки кръглия чек бокс от дясно.",
+ "app.presentationUploder.extraHint": "ВАЖНО: единичен файл не може да превишава {0} МВ и {1} страници. ",
"app.presentationUploder.uploadLabel": "Качване",
"app.presentationUploder.confirmLabel": "Потвърждаване",
"app.presentationUploder.confirmDesc": "Запазете Вашите промени и стартирайте презентацията",
@@ -167,7 +205,10 @@
"app.presentationUploder.fileToUpload": "да бъде качено...",
"app.presentationUploder.currentBadge": "Текущо",
"app.presentationUploder.rejectedError": "Избрания/те файл(ове) бяха отхвърлени. Моля проверете типа на файла(овете)",
+ "app.presentationUploder.connectionClosedError": "Прекъсване поради лоша връзка. Моля опитайте отново.",
"app.presentationUploder.upload.progress": "Качване ({0}%)",
+ "app.presentationUploder.upload.413": "Файла е прекалено голям, надвишава максумума от {0} МВ",
+ "app.presentationUploder.genericError": "Оопс, нещо се обърка...",
"app.presentationUploder.upload.408": "Времето на заявката за upload токен изтече",
"app.presentationUploder.upload.404": "404: Невалиден токен за upload",
"app.presentationUploder.upload.401": "Неуспешна заявка за презентация",
@@ -176,31 +217,68 @@
"app.presentationUploder.conversion.generatingThumbnail": "Генериране на thumbnails ...",
"app.presentationUploder.conversion.generatedSlides": "Генериране на слайдове ...",
"app.presentationUploder.conversion.generatingSvg": "Генериране на SVG изобажения ...",
+ "app.presentationUploder.conversion.pageCountExceeded": "Страниците надвишават максумума от {0}",
"app.presentationUploder.conversion.officeDocConversionInvalid": "Грешка при обработка на office документа. Моля, по възможност качете PDF файл.",
"app.presentationUploder.conversion.officeDocConversionFailed": "Грешка при обработка на office документа. Моля, по възможност качете PDF файл.",
+ "app.presentationUploder.conversion.pdfHasBigPage": "Проблем с конвертирането на PDF файла, моля опитайте да го оптимизирате. Максимален брой страници {0}",
"app.presentationUploder.conversion.timeout": "Опс, обработката отне много време",
"app.presentationUploder.conversion.pageCountFailed": "Проблем с установяването на броя на страниците.",
+ "app.presentationUploder.conversion.unsupportedDocument": "Неподържано файлово разширение",
+ "app.presentationUploder.isDownloadableLabel": "Свалянето на презентацията не е разрешено - кликнете за предоставяне на разрешение за сваляне ",
+ "app.presentationUploder.isNotDownloadableLabel": "Свалянето на презентацията е разрешено - кликнете за да забраните свалянето",
"app.presentationUploder.removePresentationLabel": "Премахни презентацията",
"app.presentationUploder.setAsCurrentPresentation": "Направи презентацията активна",
"app.presentationUploder.tableHeading.filename": "Име на файла",
"app.presentationUploder.tableHeading.options": "Опции",
"app.presentationUploder.tableHeading.status": "Статус",
+ "app.presentationUploder.uploading": "Качване {0} {1}",
+ "app.presentationUploder.uploadStatus": "{0} от {1} качвания приключиха ",
+ "app.presentationUploder.completed": "{0} качвания приключиха",
+ "app.presentationUploder.item" : "елемент",
+ "app.presentationUploder.itemPlural" : "елементи",
+ "app.presentationUploder.clearErrors": "Изчисти грешките",
+ "app.presentationUploder.clearErrorsDesc": "Изчиства неуспешно качените презентации",
+ "app.presentationUploder.uploadViewTitle": "Качи презентация",
"app.poll.pollPaneTitle": "Проучване",
"app.poll.quickPollTitle": "Бързо проучване",
"app.poll.hidePollDesc": "Скрии панела за гласуване",
"app.poll.quickPollInstruction": "Изберете вариантите по-долу за да стартирате Вашето проучване",
"app.poll.activePollInstruction": "Оставете панела отворен за да виждате в реално време отговорите на Вашето проучване. Когато сте готови, изберете 'Публикувай резултатите'",
+ "app.poll.dragDropPollInstruction": "За да попълните стойностите на анкетата, довлачете текстов файл със стойностите до маркираното поле",
+ "app.poll.customPollTextArea": "Попълнете стойностите на анкетата",
+ "app.poll.publishLabel": "Публикувай анкетата",
+ "app.poll.cancelPollLabel": "Отмяна",
+ "app.poll.backLabel": "Стартирай анкета",
"app.poll.closeLabel": "Затвори",
"app.poll.waitingLabel": "Очакват се отговорите ({0}/{1})",
"app.poll.ariaInputCount": "Опции в проучването {0} от {1}",
"app.poll.customPlaceholder": "Добави вариант в проучването",
"app.poll.noPresentationSelected": "Не е избрана презентация! Моля изберете.",
"app.poll.clickHereToSelect": "Щракнете туз за избор",
+ "app.poll.question.label" : "Напишете Вашия въпрос...",
+ "app.poll.optionalQuestion.label" : "Напишете Вашия въпрос (незадължително)...",
+ "app.poll.userResponse.label" : "Потребителски отговор",
+ "app.poll.responseTypes.label" : "Варианти на отговор",
+ "app.poll.optionDelete.label" : "Изтрий",
+ "app.poll.responseChoices.label" : "Изпор на отговори",
+ "app.poll.typedResponse.desc" : "На потребителите ще бъде показано текстово поле в което да попълнят отговора си.",
+ "app.poll.addItem.label" : "Добави елемент",
+ "app.poll.start.label" : "Стартирай анкета",
+ "app.poll.secretPoll.label" : "Анонимна анкета",
+ "app.poll.secretPoll.isSecretLabel": "Анкетата е анонимна - няма да можете да видите индивидуалните отговори.",
+ "app.poll.questionErr": "Въвеждането на въпрос е задължително.",
+ "app.poll.optionErr": "Въведете опции за анкетата",
+ "app.poll.startPollDesc": "Започнете анкетата",
+ "app.poll.showRespDesc": "Настройка на отговора",
+ "app.poll.addRespDesc": "Добавя възможност за въвеждане на отговр",
+ "app.poll.deleteRespDesc": "Премахва опция {0}",
"app.poll.t": "Вярно",
"app.poll.f": "Невярно",
"app.poll.tf": "Вярно / Невярно",
"app.poll.y": "Да",
"app.poll.n": "Не",
+ "app.poll.abstention": "Въздържал се",
+ "app.poll.yna": "Да / Не / Въздържал се",
"app.poll.a2": "A / B",
"app.poll.a3": "A / B / C",
"app.poll.a4": "A / B / C / D",
@@ -209,6 +287,7 @@
"app.poll.answer.false": "Невярно",
"app.poll.answer.yes": "Да",
"app.poll.answer.no": "Не",
+ "app.poll.answer.abstention": "Въздържал се",
"app.poll.answer.a": "A",
"app.poll.answer.b": "B",
"app.poll.answer.c": "C",
@@ -216,7 +295,16 @@
"app.poll.answer.e": "E",
"app.poll.liveResult.usersTitle": "Потребители",
"app.poll.liveResult.responsesTitle": "Отговор",
+ "app.poll.liveResult.secretLabel": "Това е анонимна анкета. Индивидуалните отговори няма да бъдат показани.",
+ "app.poll.removePollOpt": "Премахната опция {0} от анкетата",
+ "app.poll.emptyPollOpt": "Празно",
"app.polling.pollingTitle": "Опции за проучването",
+ "app.polling.pollQuestionTitle": "Въпрос за гласуване",
+ "app.polling.submitLabel": "Изпрати",
+ "app.polling.submitAriaLabel": "Изпрати отговора",
+ "app.polling.responsePlaceholder": "Въведи отговор",
+ "app.polling.responseSecret": "Анонимна анкета - презентатора няма да вижда Вашия отговор.",
+ "app.polling.responseNotSecret": "Нормална анкета - Презентатора ще вижда вашия отговор.",
"app.polling.pollAnswerLabel": "Отговори на проучването {0}",
"app.polling.pollAnswerDesc": "Изберете тази опция за да гласувате за {0}",
"app.failedMessage": "Съжаляваме, има проблем с връзката до сървъра",
@@ -224,10 +312,14 @@
"app.connectingMessage": "Свързване...",
"app.waitingMessage": "Разкачен. Опит за свързване след {0} секунди ...",
"app.retryNow": "Опитай сега",
+ "app.muteWarning.label": "Кликнете {0} за премахване на заглушаването",
+ "app.muteWarning.disableMessage": "Съобщенията за заглушаване са изключени до премахване на заглушаването",
+ "app.muteWarning.tooltip": "Кликнете за да затворите и изключите предупреждението до следващото премахване на заглушаването",
"app.navBar.settingsDropdown.optionsLabel": "Опции",
"app.navBar.settingsDropdown.fullscreenLabel": "Покажи на цял екран",
"app.navBar.settingsDropdown.settingsLabel": "Настройки",
"app.navBar.settingsDropdown.aboutLabel": "Относно",
+ "app.navBar.settingsDropdown.leaveSessionLabel": "Непускане на срещата",
"app.navBar.settingsDropdown.exitFullscreenLabel": "Изход от цял екран",
"app.navBar.settingsDropdown.fullscreenDesc": "Покажи менюто с настройките на цял екран",
"app.navBar.settingsDropdown.settingsDesc": "Change the general settings",
@@ -242,12 +334,17 @@
"app.navBar.userListToggleBtnLabel": "Показва списъка с потребители",
"app.navBar.toggleUserList.ariaLabel": "Скрий/покажи потребителите и съобщенията",
"app.navBar.toggleUserList.newMessages": "with new message notification",
+ "app.navBar.toggleUserList.newMsgAria": "Ново съобщение от {0} ",
"app.navBar.recording": "Тази сесия се записва",
"app.navBar.recording.on": "Запис",
"app.navBar.recording.off": "Не се записва",
"app.navBar.emptyAudioBrdige": "Няма активен микрофон. Включете микрофона за да има звук към този запис.",
"app.leaveConfirmation.confirmLabel": "Напусни",
"app.leaveConfirmation.confirmDesc": "Напускане на срещата",
+ "app.endMeeting.title": "Край {0}",
+ "app.endMeeting.description": "Ще прекратите сесията на {0} активни потребител/я. Сигурни ли сте, че желаете да прекратите сесията?",
+ "app.endMeeting.noUserDescription": "Сигурни ли сте че желаете да прекратите сесията?",
+ "app.endMeeting.contentWarning": "Съобщенията в чата, споделените бележки, съдържанието на дъската и споделените документи за тази сесия няма да бъдат директно достъпни.",
"app.endMeeting.yesLabel": "Да",
"app.endMeeting.noLabel": "Не",
"app.about.title": "Относно",
@@ -262,9 +359,13 @@
"app.actionsBar.camOffLabel": "Камера изк.",
"app.actionsBar.raiseLabel": "вдигни ръка",
"app.actionsBar.actionsDropdown.restorePresentationLabel": "Покажи презентацията",
+ "app.actionsBar.actionsDropdown.restorePresentationDesc": "Бутон за възстановяване на презентацията след мимимизиране",
+ "app.actionsBar.actionsDropdown.minimizePresentationLabel": "Минимизирай презентацията",
+ "app.actionsBar.actionsDropdown.minimizePresentationDesc": "Бутон за минимизиране на презентацията",
"app.screenshare.screenShareLabel" : "Споделяне на екрана",
"app.submenu.application.applicationSectionTitle": "Приложение",
"app.submenu.application.animationsLabel": "Анимации",
+ "app.submenu.application.audioFilterLabel": "Аудио филтри за микрофона",
"app.submenu.application.fontSizeControlLabel": "Размер на шрифта",
"app.submenu.application.increaseFontBtnLabel": "Увеличи размера на шрифта",
"app.submenu.application.decreaseFontBtnLabel": "Намали размера на шрифта",
@@ -272,11 +373,14 @@
"app.submenu.application.languageLabel": "Език на интерфейса",
"app.submenu.application.languageOptionLabel": "Изберете език",
"app.submenu.application.noLocaleOptionLabel": "Няма активна локализация",
+ "app.submenu.application.paginationEnabledLabel": "Видео страниране",
+ "app.submenu.application.layoutOptionLabel": "Видове изгледи",
"app.submenu.notification.SectionTitle": "Съобщения",
"app.submenu.notification.Desc": "Дефинира как и за какво ще бъдете информирани",
"app.submenu.notification.audioAlertLabel": "Звукови предупреждения",
"app.submenu.notification.pushAlertLabel": "Popup предупреждения",
"app.submenu.notification.messagesLabel": "Чат съобщение",
+ "app.submenu.notification.guestWaitingLabel": "Гост очаква одобрение",
"app.submenu.audio.micSourceLabel": "Микрофон - източник",
"app.submenu.audio.speakerSourceLabel": "Говорители - източник",
"app.submenu.audio.streamVolumeLabel": "Сила на звука",
@@ -300,12 +404,20 @@
"app.settings.dataSavingTab.screenShare": "Разреши споделянето на екрана",
"app.settings.dataSavingTab.description": "За да ограничите използването на данни, определете какво да се показва",
"app.settings.save-notification.label": "Настройките бяха записани",
+ "app.statusNotifier.lowerHands": "Смъкни ръцете",
+ "app.statusNotifier.raisedHandsTitle": "Вдигнати ръце",
+ "app.statusNotifier.raisedHandDesc": "{0} вдигнаха ръка",
+ "app.statusNotifier.raisedHandDescOneUser": "{0} вдигнати ръце",
+ "app.statusNotifier.and": "и",
"app.switch.onLabel": "ВКЛ",
"app.switch.offLabel": "ИЗК",
"app.talkingIndicator.ariaMuteDesc" : "Избери за да заглушиш потребителя",
"app.talkingIndicator.isTalking" : "{0} говори",
+ "app.talkingIndicator.moreThanMaxIndicatorsTalking" : "{0}+ говорят",
+ "app.talkingIndicator.moreThanMaxIndicatorsWereTalking" : "{0}+ говореха",
"app.talkingIndicator.wasTalking" : "{0} спря да говори",
"app.actionsBar.actionsDropdown.actionsLabel": "Действия",
+ "app.actionsBar.actionsDropdown.presentationLabel": "Управление на презентациите",
"app.actionsBar.actionsDropdown.initPollLabel": "Стартирай проучване",
"app.actionsBar.actionsDropdown.desktopShareLabel": "Сподели екрана",
"app.actionsBar.actionsDropdown.lockedDesktopShareLabel": "Споделянето на екрана е забранено",
@@ -323,9 +435,13 @@
"app.actionsBar.actionsDropdown.captionsDesc": "Показва/скрива панела на субтитрите",
"app.actionsBar.actionsDropdown.takePresenter": "Стани презентатор",
"app.actionsBar.actionsDropdown.takePresenterDesc": "Правиш се презентатор",
+ "app.actionsBar.actionsDropdown.selectRandUserLabel": "Избери случаен потребител",
+ "app.actionsBar.actionsDropdown.selectRandUserDesc": "Избери случаен участник",
"app.actionsBar.emojiMenu.statusTriggerLabel": "Статус",
"app.actionsBar.emojiMenu.awayLabel": "Отсъстващ",
"app.actionsBar.emojiMenu.awayDesc": "Промени статуса си на отсъстващ",
+ "app.actionsBar.emojiMenu.raiseHandLabel": "Вдигни ръка",
+ "app.actionsBar.emojiMenu.lowerHandLabel": "Смъкни ръка",
"app.actionsBar.emojiMenu.raiseHandDesc": "Вдигни ръка за да зададеш въпрос",
"app.actionsBar.emojiMenu.neutralLabel": "Неопределено",
"app.actionsBar.emojiMenu.neutralDesc": "Промени статуса си на неопределен",
@@ -371,6 +487,8 @@
"app.audioModal.ariaTitle": "Включи аудиото",
"app.audioModal.microphoneLabel": "Със звук",
"app.audioModal.listenOnlyLabel": "Като слушател",
+ "app.audioModal.microphoneDesc": "Влез в срещата с микрофон",
+ "app.audioModal.listenOnlyDesc": "Влез в срещата като слушател",
"app.audioModal.audioChoiceLabel": "Как желаете да се включите?",
"app.audioModal.iOSBrowser": "Аудио/Видео не се поддържа",
"app.audioModal.iOSErrorDescription": "Към момента Chrome под iOS не поддържа аудио и видео. ",
@@ -396,6 +514,7 @@
"app.audioModal.playAudio.arialabel" : "Пусни аудио",
"app.audioDial.tipIndicator": "Подсказка",
"app.audioDial.tipMessage": "Натиснете `0` на вашия телефон за да вкл/изк микрофона си.",
+ "app.audioModal.connecting": "Установяване на аудио връзка",
"app.audioManager.joinedAudio": "Вие се присъединихте към аудио конференцията",
"app.audioManager.joinedEcho": "Вие се присъединихте към ехо тест",
"app.audioManager.leftAudio": "Вие напуснахте аудио конференцията",
@@ -407,9 +526,14 @@
"app.audioManager.mediaError": "Грешка: Има проблем с досъпа до вашите камера/микрофон",
"app.audio.joinAudio": "Включи аудиото",
"app.audio.leaveAudio": "Изключи аудиото",
+ "app.audio.changeAudioDevice": "Премени аудио устройството",
"app.audio.enterSessionLabel": "Влез в сесията",
"app.audio.playSoundLabel": "Пусни звук",
"app.audio.backLabel": "Назад",
+ "app.audio.loading": "Зареждане",
+ "app.audio.microphones": "Микрофони",
+ "app.audio.speakers": "Говорители",
+ "app.audio.noDeviceFound": "Не е намерено устройство",
"app.audio.audioSettings.titleLabel": "Изберете Ваште аудио настройки",
"app.audio.audioSettings.descriptionLabel": "Моля обърнете внимание, Вашият браузър ще поиска разрешение за споделяне на микрофона",
"app.audio.audioSettings.microphoneSourceLabel": "Микрофон - източник",
@@ -434,18 +558,41 @@
"app.modal.confirm": "Завършено",
"app.modal.newTab": "(отваря нов таб)",
"app.modal.confirm.description": "Запази промяната и затвори избора",
+ "app.modal.randomUser.noViewers.description": "Не са налични участници от които да се избере случаен\n",
+ "app.modal.randomUser.selected.description": "Вие бяхте избран на случаен принцип",
+ "app.modal.randomUser.title": "Случайно избран потребител",
+ "app.modal.randomUser.who": "Кой ще бъде избран..?",
+ "app.modal.randomUser.alone": "Има само един участник",
+ "app.modal.randomUser.reselect.label": "Избери отново",
+ "app.modal.randomUser.ariaLabel.title": "Диалог за случаен избор на потребител",
"app.dropdown.close": "Затвори",
+ "app.dropdown.list.item.activeLabel": "Активен",
"app.error.400": "Грешна заявка",
"app.error.401": "Неауторизиран",
"app.error.403": "Вие бяхте изключен от срещата",
"app.error.404": "Не е намерен",
+ "app.error.408": "Ауторизацията неуспешна",
"app.error.410": "Срещата приключи",
"app.error.500": "Опс, нещо не е наред",
+ "app.error.userLoggedOut": "Потребителя има невалиден sessionToken поради log out ",
+ "app.error.ejectedUser": "Потребителя има невалиден sessionToken поради изхвърляне",
+ "app.error.userBanned": "Потребителя беше изгонен",
"app.error.leaveLabel": "Влезте отново",
"app.error.fallback.presentation.title": "Възникна грешка",
"app.error.fallback.presentation.description": "Влязъл е. Моля презаредете станицата",
"app.error.fallback.presentation.reloadButton": "Презареждане",
"app.guest.waiting": "Изчаква одобрение за вход",
+ "app.guest.errorSeeConsole": "Грешка: Повече подробности в конзолата.",
+ "app.guest.noModeratorResponse": "Няма отговор от модератора.",
+ "app.guest.windowTitle": "BigBlueButton - Лоби за гости",
+ "app.guest.missingMeeting": "Срещата не съществува.",
+ "app.guest.meetingEnded": "Срещата приключи.",
+ "app.guest.guestWait": "Моля изчакайте модератора да одобри участието Ви в срещата.",
+ "app.guest.guestDeny": "Участието на госта в срещата е отказано.",
+ "app.guest.seatWait": "Гост очаква одобрение за участие в срещата.",
+ "app.guest.allow": "Госта е одобрен и препратен към срещата.",
+ "app.guest.guestInvalid": "Госта е невалиден",
+ "app.guest.meetingForciblyEnded": "Не можете да влезете в среща която принудително е приключила",
"app.userList.guest.waitingUsers": "Изчаква потребители",
"app.userList.guest.waitingUsersTitle": "Управление на потребителите",
"app.userList.guest.optionTitle": "Преглеждане на чакващите потребители",
@@ -457,6 +604,10 @@
"app.userList.guest.pendingGuestUsers": "{0} Изчакващи Гост потребители",
"app.userList.guest.pendingGuestAlert": "Присъедини се към сесията и очаква Вашето одобрение",
"app.userList.guest.rememberChoice": "Запомни избора",
+ "app.userList.guest.emptyMessage": "Няма нови съобщения",
+ "app.userList.guest.inputPlaceholder": "Съобщение до гостите в лобито",
+ "app.userList.guest.acceptLabel": "Приемам",
+ "app.userList.guest.denyLabel": "Забранявам",
"app.user-info.title": "Преглед на директорията",
"app.toast.breakoutRoomEnded": "Екипната стая бе затворена. Моля вкючете аудиото.",
"app.toast.chat.public": "Ново съобщение в Общия чат",
@@ -466,13 +617,17 @@
"app.toast.setEmoji.label": "Емоджи стутса е сменен на {0}",
"app.toast.meetingMuteOn.label": "Всички потребители бяха заглушени",
"app.toast.meetingMuteOff.label": "Заглушаването на срещата е изключено",
+ "app.toast.setEmoji.raiseHand": "Вие дигнахте ръка",
+ "app.toast.setEmoji.lowerHand": "Вие свалихте ръка",
"app.notification.recordingStart": "Тази сесия се записва",
"app.notification.recordingStop": "Тази сесия не се записва",
"app.notification.recordingPaused": "Тази сесия вече не се записва",
"app.notification.recordingAriaLabel": "Време на запис",
"app.notification.userJoinPushAlert": "{0} се присъедини към сесията",
+ "app.notification.userLeavePushAlert": "{0} напуснаха сесията",
"app.submenu.notification.raiseHandLabel": "Вдигни ръка",
"app.shortcut-help.title": "Клавишни комбинации",
+ "app.shortcut-help.accessKeyNotAvailable": "Кода за достъп не е достъпен",
"app.shortcut-help.comboLabel": "Комбинация",
"app.shortcut-help.functionLabel": "Функция",
"app.shortcut-help.closeLabel": "Затвори",
@@ -486,6 +641,7 @@
"app.shortcut-help.openActions": "Отвори менюто с възможни действия",
"app.shortcut-help.openStatus": "Отвори статус менюто",
"app.shortcut-help.togglePan": "Активиране на показалеца (Презентатор)",
+ "app.shortcut-help.toggleFullscreen": "Превключи на пълен екран (Презентатор)",
"app.shortcut-help.nextSlideDesc": "Следващ слайд (Презентатор)",
"app.shortcut-help.previousSlideDesc": "Предишен слайд (Презентатор)",
"app.lock-viewers.title": "Задай ограничение",
@@ -503,9 +659,26 @@
"app.lock-viewers.button.cancel": "Отказ",
"app.lock-viewers.locked": "Забранено",
"app.lock-viewers.unlocked": "Разрешено",
+ "app.guest-policy.button.askModerator": "Попитай модератора",
+ "app.guest-policy.button.alwaysAccept": "Винаги приемам",
+ "app.guest-policy.button.alwaysDeny": "Винаги отказвам",
+ "app.connection-status.ariaTitle": "Статус на връзката",
"app.connection-status.title": "Статус на връзката",
"app.connection-status.description": "Статус на връзката на потребителите",
+ "app.connection-status.empty": "Не са докладвани проблеми с връзката",
"app.connection-status.more": "още",
+ "app.connection-status.copy": "Копирай мрежовите данни",
+ "app.connection-status.copied": "Копирани!",
+ "app.connection-status.label": "Статус на връзката",
+ "app.connection-status.no": "Не",
+ "app.connection-status.notification": "Установени са проблеми с връзката Ви",
+ "app.connection-status.offline": "offline",
+ "app.connection-status.lostPackets": "Загуба на пакети",
+ "app.connection-status.usingTurn": "Използване на TURN",
+ "app.connection-status.yes": "Да",
+ "app.learning-dashboard.label": "Табло анализи на участието",
+ "app.learning-dashboard.description": "Отвори таблото с потребителските активности",
+ "app.learning-dashboard.clickHereToOpen": "Отвори таблото са анализи на обучението",
"app.recording.startTitle": "Стартиране на записа",
"app.recording.stopTitle": "Пауза на записа",
"app.recording.resumeTitle": "Продължи записа",
@@ -513,27 +686,45 @@
"app.recording.stopDescription": "Сигурни ли сте че желаете да паузирате записа? Можете да продължите записа при натискане на бутона отново.",
"app.videoPreview.cameraLabel": "Камера",
"app.videoPreview.profileLabel": "Качество",
+ "app.videoPreview.quality.low": "Ниско",
+ "app.videoPreview.quality.medium": "Средно",
+ "app.videoPreview.quality.high": "Високо",
+ "app.videoPreview.quality.hd": "Висока резолюция",
"app.videoPreview.cancelLabel": "Отказ",
"app.videoPreview.closeLabel": "Затвори",
"app.videoPreview.findingWebcamsLabel": "Търсене на камери",
"app.videoPreview.startSharingLabel": "Започване на споделянето",
"app.videoPreview.stopSharingLabel": "Спри споделянето",
+ "app.videoPreview.stopSharingAllLabel": "Спри всички",
"app.videoPreview.sharedCameraLabel": "Тази камера вече е споделена",
"app.videoPreview.webcamOptionLabel": "Избери камера",
"app.videoPreview.webcamPreviewLabel": "Камера преглед",
"app.videoPreview.webcamSettingsTitle": "Настройки на камерата",
+ "app.videoPreview.webcamVirtualBackgroundLabel": "Настройки на виртуалния фон",
+ "app.videoPreview.webcamVirtualBackgroundDisabledLabel": "Това устройство не поддържа виртуален фон",
"app.videoPreview.webcamNotFoundLabel": "Камерата не е намерена",
+ "app.videoPreview.profileNotFoundLabel": "Няма поддържан профил на камера",
"app.video.joinVideo": "Сподели камерата",
+ "app.video.connecting": "Започва споделянето на камерата...",
"app.video.leaveVideo": "Спри споделянето на камерата",
+ "app.video.advancedVideo": "Отвори разширените настройки",
"app.video.iceCandidateError": "Грешка при добавянето на ICE кандидат",
"app.video.iceConnectionStateError": "Проблем с връзката (ICE грешка 1107)",
"app.video.permissionError": "Грешка при споделянето на камерата. Моля проверете разрешенията",
"app.video.sharingError": "Грешка при споделянето на камерата",
+ "app.video.abortError": "Възникна непознат проблем, който възпрепятства използването на камерата",
+ "app.video.overconstrainedError": "Вашата камера не поддържа това качество",
+ "app.video.securityError": "Вашият браузър не позволява използването на камерата. Опитайте с друг браузър ",
+ "app.video.typeError": "Невалиден профил на качеството за камерата. Свържете се с администратор.",
"app.video.notFoundError": "Камерата не беше намерена. Моля проверете дали е свързана",
"app.video.notAllowed": "Липсва разрешение за споделяне на камерата, моля проверете разрешенията на браузъра",
"app.video.notSupportedError": "Може да се споделя видео от камерата само от сигурни източници, проверете валидността на SSL сертификата",
"app.video.notReadableError": "Няма достъп до видеото от камерата. Моля проверете дали друго приложение не я ползва.",
+ "app.video.timeoutError": "Браузърът не отговори навреме",
+ "app.video.genericError": "Възникна непозната грешка с устройството ({0})",
+ "app.video.mediaTimedOutError": "Видеопотока от Вашата камера беше прекъснат. Опитайте да я споделите отново",
"app.video.mediaFlowTimeout1020": "Медията не може да достигне до сървъра (грешка 1020)",
+ "app.video.suggestWebcamLock": "Да се приложи ли настройката за заключване на уеб камерите на участниците?",
"app.video.suggestWebcamLockReason": "(това ще подобри стабилността на срещата)",
"app.video.enable": "Разреши",
"app.video.cancel": "Отказ",
@@ -544,7 +735,23 @@
"app.video.videoMenu": "Видео меню",
"app.video.videoMenuDisabled": "Видео менюто на камерета е изключено в настройките",
"app.video.videoMenuDesc": "Отвори падащото видео меню",
+ "app.video.pagination.prevPage": "Виж предишните видеа",
+ "app.video.pagination.nextPage": "Виж следващите видеа",
+ "app.video.clientDisconnected": "Поради проблеми с връзката камерата не може да бъде споделена",
+ "app.video.virtualBackground.none": "Нищо",
+ "app.video.virtualBackground.blur": "Замъглено",
+ "app.video.virtualBackground.home": "Дом",
+ "app.video.virtualBackground.board": "Дъска",
+ "app.video.virtualBackground.coffeeshop": "Кафене",
+ "app.video.virtualBackground.background": "Фон",
+ "app.video.virtualBackground.genericError": "Проблем с прилагане на ефекта. Опитайте отново",
+ "app.video.virtualBackground.camBgAriaDesc": "Приложи виртуален фон към {0}",
+ "app.video.camCapReached": "Вие не може да споделяте повече камери",
+ "app.video.dropZoneLabel": "Пусни тук",
"app.fullscreenButton.label": "Покажи {0} на цял екран",
+ "app.fullscreenUndoButton.label": "Назад {0} fullscreen",
+ "app.switchButton.expandLabel": "Разпъни видеото на споделения екран",
+ "app.switchButton.shrinkLabel": "Намали видеото на споделения екран",
"app.sfu.mediaServerConnectionError2000": "Връзката тс медия сървъра невъзможна (Грешка 2000)",
"app.sfu.mediaServerOffline2001": "Медия сървъра е offline. Моля опитайте по-късно (грешка 2001)",
"app.sfu.mediaServerNoResources2002": "Медия сървъра няма свободни ресурси (грешка 2002)",
@@ -554,6 +761,9 @@
"app.sfu.invalidSdp2202":"Клиента генерира невалидна медия заявка (SDP грешка 2202)",
"app.sfu.noAvailableCodec2203": "Сървъра на може да намери подходящ кодек (грешка 2203)",
"app.meeting.endNotification.ok.label": "OK",
+ "app.whiteboard.annotations.poll": "Резултатите от анкетата са публикувани",
+ "app.whiteboard.annotations.pollResult": "Резултати от анкетата",
+ "app.whiteboard.annotations.noResponses": "Няма отговор",
"app.whiteboard.toolbar.tools": "Инструменти",
"app.whiteboard.toolbar.tools.hand": "Показалец",
"app.whiteboard.toolbar.tools.pencil": "Молив",
@@ -581,15 +791,23 @@
"app.whiteboard.toolbar.multiUserOn": "Включи многопотребителската бяла дъска",
"app.whiteboard.toolbar.multiUserOff": "Изключи многопотребителската бяла дъска",
"app.whiteboard.toolbar.fontSize": "Размер на шрифта",
+ "app.whiteboard.toolbarAriaLabel": "Инструменти за презентиране",
"app.feedback.title": "Вие излязохте от конференцията",
"app.feedback.subtitle": "Ще се радваме да чуем Вашия опит с BigBlueButton (опционално)",
"app.feedback.textarea": "Как можем да подобрим BigBlueButton?",
"app.feedback.sendFeedback": "Изпрати мнение",
"app.feedback.sendFeedbackDesc": "Изпрати мнение и напусни срещата",
+ "app.videoDock.webcamMirrorLabel": "Огледало",
+ "app.videoDock.webcamMirrorDesc": "Огледален образ на избраната камера",
"app.videoDock.webcamFocusLabel": "Фокус",
"app.videoDock.webcamFocusDesc": "Премести фокуса върху избраната камера",
"app.videoDock.webcamUnfocusLabel": "Премахни фокуса",
"app.videoDock.webcamUnfocusDesc": "Премахни фокуса от избраната камера",
+ "app.videoDock.webcamPinLabel": "Закачи",
+ "app.videoDock.webcamPinDesc": "Закачи избраната камера",
+ "app.videoDock.webcamUnpinLabel": "Разкачи",
+ "app.videoDock.webcamUnpinLabelDisabled": "Само модераторите могат да разкачат участници",
+ "app.videoDock.webcamUnpinDesc": "Разкачи избраната камера",
"app.videoDock.autoplayBlockedDesc": "Нуждаем се от Вашето разрешение да Ви покажем камерите да другите потребители",
"app.videoDock.autoplayAllowLabel": "Прегледай камерите",
"app.invitation.title": "Покана за екипна стая",
@@ -597,17 +815,22 @@
"app.createBreakoutRoom.title": "Екипни стаи",
"app.createBreakoutRoom.ariaTitle": "Скрий екипните стаи",
"app.createBreakoutRoom.breakoutRoomLabel": "Екипни стаи {0}",
+ "app.createBreakoutRoom.askToJoin": "Поискайте да се присъедините",
"app.createBreakoutRoom.generatingURL": "Генериране на URL",
+ "app.createBreakoutRoom.generatingURLMessage": "Генерира се URL за присъединяване към избраната екипна стая. Това ще отнеме няколко секунди...",
"app.createBreakoutRoom.duration": "Продължителност {0}",
"app.createBreakoutRoom.room": "Стая {0}",
"app.createBreakoutRoom.notAssigned": "Неприсъединени ({0})",
"app.createBreakoutRoom.join": "Влез в стаята",
+ "app.createBreakoutRoom.joinAudio": "Включи аудиото",
+ "app.createBreakoutRoom.returnAudio": "Възстанови аудиото",
"app.createBreakoutRoom.alreadyConnected": "Вече сте в стаята",
"app.createBreakoutRoom.confirm": "Създай",
"app.createBreakoutRoom.record": "Запиши",
"app.createBreakoutRoom.numberOfRooms": "Брой на стаите",
"app.createBreakoutRoom.durationInMinutes": "Продължителност (минути)",
"app.createBreakoutRoom.randomlyAssign": "Случайно записване",
+ "app.createBreakoutRoom.randomlyAssignDesc": "Разпредели участниците в екипните стаи на случаен принцип",
"app.createBreakoutRoom.endAllBreakouts": "Затвори всички екипни стаи",
"app.createBreakoutRoom.roomName": "{0} (Стая - {1})",
"app.createBreakoutRoom.doneLabel": "Завършено",
@@ -617,9 +840,17 @@
"app.createBreakoutRoom.addParticipantLabel": "+ Добави участник",
"app.createBreakoutRoom.freeJoin": "Разрешете на потребителите да изберат в коя екипна стая да влязат",
"app.createBreakoutRoom.leastOneWarnBreakout": "Трябва да поставите поне един потребител в екипната стая.",
+ "app.createBreakoutRoom.minimumDurationWarnBreakout": "Минималното време за активност на екипната стая е {0} минути.",
"app.createBreakoutRoom.modalDesc": "Съвет: Можете да влачите потребителските имена до конкретната екипна стая.",
"app.createBreakoutRoom.roomTime": "{0} минути",
"app.createBreakoutRoom.numberOfRoomsError": "Броя на стаите е невалиден",
+ "app.createBreakoutRoom.duplicatedRoomNameError": "Имената на стаите на могат да се дублират",
+ "app.createBreakoutRoom.emptyRoomNameError": "Името на стаята не може да е празно",
+ "app.createBreakoutRoom.extendTimeInMinutes": "Време за удължаване(минути)",
+ "app.createBreakoutRoom.extendTimeLabel": "Удължи",
+ "app.createBreakoutRoom.extendTimeCancel": "Отмени",
+ "app.createBreakoutRoom.extendTimeHigherThanMeetingTimeError": "Времето за активност на екипната стая не може да превишава това на основната среща",
+ "app.createBreakoutRoom.roomNameInputDesc": "Обновява името на екипната стая",
"app.externalVideo.start": "Сподели ново видео",
"app.externalVideo.title": "Сподели външно видео",
"app.externalVideo.input": "Външно видео URl",
@@ -627,12 +858,91 @@
"app.externalVideo.urlError": "Този видеоизточник не се поддържа.",
"app.externalVideo.close": "Затвори",
"app.externalVideo.autoPlayWarning": "Щракнете вълхо видеото за да го пуснете",
+ "app.externalVideo.refreshLabel": "Обнови Video Player",
+ "app.externalVideo.noteLabel": "Бележка: Споделеното външно видео няма да се появи във записа. Поддържат се YouTube, Vimeo, Instructure Media, Twitch, Dailymotion and URLs на медийни файлове (e.g. https://example.com/xy.mp4) .",
"app.actionsBar.actionsDropdown.shareExternalVideo": "Сподели външно видео",
"app.actionsBar.actionsDropdown.stopShareExternalVideo": "Спри споделянето на видео",
"app.iOSWarning.label": "Моля обновете до iOS 12.2 или по-висока",
"app.legacy.unsupportedBrowser": "Вие използвате браузър който не се поддържа.За пълна функционалност, моля използвайте {0} или {1}",
"app.legacy.upgradeBrowser": "Вие използвате стара версия на браузър който се поддържа. За постигане на пълна функционалнаст, моля обновете браузъра си.",
- "app.legacy.criosBrowser": "За пълна функционалност под iOS моля използвайте Safari"
+ "app.legacy.criosBrowser": "За пълна функционалност под iOS моля използвайте Safari",
+ "app.debugWindow.form.button.copy": "Копирай",
+ "app.debugWindow.form.enableAutoarrangeLayoutLabel": "Включи автоматично подреждане на изгледа",
+ "app.debugWindow.form.enableAutoarrangeLayoutDescription": "(ще бъде изключено ако влачите или преоразмерите прозореца на камерата)",
+ "app.debugWindow.form.button.apply": "Приложи",
+ "app.layout.style.custom": "Персонализиран",
+ "app.layout.style.smart": "Умно оформление",
+ "app.layout.style.presentationFocus": "Фокус върху презентацията",
+ "app.layout.style.videoFocus": "Фокус върху видеото",
+ "app.layout.style.customPush": "Персонализиран (налага оформлението на всички)",
+ "app.layout.style.smartPush": "Умно оформление(налага оформлението на всички)",
+ "app.layout.style.presentationFocusPush": "Фокус върху презентацията (налага оформлението на всички)",
+ "app.layout.style.videoFocusPush": "Фокус върху видеото(налага оформлението на всички)",
+ "playback.button.about.aria": "Относно",
+ "playback.button.clear.aria": "Изчисти търсенето",
+ "playback.button.close.aria": "Затвори",
+ "playback.button.fullscreen.aria": "Съдържанието на цял екран",
+ "playback.button.restore.aria": "Възстанови съдържанието",
+ "playback.button.search.aria": "Търси",
+ "playback.button.section.aria": "Странична секция",
+ "playback.button.swap.aria": "Размени съдържанието",
+ "playback.error.wrapper.aria": "Зона за грешки",
+ "playback.loader.wrapper.aria": "Зона за зареждане",
+ "playback.player.wrapper.aria": "Зона на плейъра",
+ "playback.player.about.modal.shortcuts.title": "Пряк път",
+ "playback.player.about.modal.shortcuts.alt": "Alt",
+ "playback.player.about.modal.shortcuts.shift": "Shift",
+ "playback.player.about.modal.shortcuts.fullscreen": "Превключи на цял екран",
+ "playback.player.about.modal.shortcuts.play": "Пусни / Пауза",
+ "playback.player.about.modal.shortcuts.section": "Превключи страничната секция",
+ "playback.player.about.modal.shortcuts.seek.backward": "Търси назад",
+ "playback.player.about.modal.shortcuts.seek.forward": "Търси напред",
+ "playback.player.about.modal.shortcuts.skip.next": "Следващ слайд",
+ "playback.player.about.modal.shortcuts.skip.previous": "Предишен слайд",
+ "playback.player.about.modal.shortcuts.swap": "Размени съдържанието",
+ "playback.player.chat.message.poll.name": "Резултат от анкетата",
+ "playback.player.chat.message.poll.question": "Въпрос",
+ "playback.player.chat.message.poll.options": "Опции",
+ "playback.player.chat.message.poll.option.yes": "Да",
+ "playback.player.chat.message.poll.option.no": "е",
+ "playback.player.chat.message.poll.option.abstention": "Въздържал се",
+ "playback.player.chat.message.poll.option.true": "Вярно",
+ "playback.player.chat.message.poll.option.false": "Невярно",
+ "playback.player.chat.message.video.name": "Външно видео",
+ "playback.player.chat.wrapper.aria": "Зона за чат",
+ "playback.player.notes.wrapper.aria": "Зона за бележки",
+ "playback.player.presentation.wrapper.aria": "Зона за презентация",
+ "playback.player.screenshare.wrapper.aria": "Зона за споделен екран",
+ "playback.player.search.modal.title": "Търсене",
+ "playback.player.search.modal.subtitle": "Намери съдържание в презентацията",
+ "playback.player.thumbnails.wrapper.aria": "Зона с миниатюри",
+ "playback.player.webcams.wrapper.aria": "Зона за камери",
+ "app.learningDashboard.dashboardTitle": "Табло за анализ на обучението",
+ "app.learningDashboard.user": "Потребител",
+ "app.learningDashboard.indicators.meetingStatusEnded": "Приключил",
+ "app.learningDashboard.indicators.meetingStatusActive": "Активен",
+ "app.learningDashboard.indicators.usersOnline": "Активни потребители",
+ "app.learningDashboard.indicators.usersTotal": "Общ брой на участниците",
+ "app.learningDashboard.indicators.polls": "Анкети",
+ "app.learningDashboard.indicators.raiseHand": "Вдикнати ръце",
+ "app.learningDashboard.indicators.activityScore": "Точки на активност",
+ "app.learningDashboard.indicators.duration": "Продължителност",
+ "app.learningDashboard.usersTable.title": "Преглед",
+ "app.learningDashboard.usersTable.colOnline": "Време онлайн",
+ "app.learningDashboard.usersTable.colTalk": "Време за дискусия",
+ "app.learningDashboard.usersTable.colWebcam": "Време с камера",
+ "app.learningDashboard.usersTable.colMessages": "Съобщения",
+ "app.learningDashboard.usersTable.colEmojis": "Емотикони",
+ "app.learningDashboard.usersTable.colRaiseHands": "Вдигнати ръце",
+ "app.learningDashboard.usersTable.colActivityScore": "Точки на активност",
+ "app.learningDashboard.usersTable.colStatus": "Статус",
+ "app.learningDashboard.usersTable.userStatusOnline": "Онлайн",
+ "app.learningDashboard.usersTable.userStatusOffline": "Офлайн",
+ "app.learningDashboard.pollsTable.title": "Анкетиране",
+ "app.learningDashboard.pollsTable.anonymousAnswer": "Анонимна анкета (отговорите в последния ред)",
+ "app.learningDashboard.pollsTable.anonymousRowName": "Анонимен",
+ "app.learningDashboard.statusTimelineTable.title": "Времева линия на статуса ",
+ "app.learningDashboard.errors.dataUnavailable": "Данните вече не са достъпни"
}
diff --git a/bigbluebutton-html5/public/locales/es.json b/bigbluebutton-html5/public/locales/es.json
index 8e70f9e7e2..335ed4e417 100644
--- a/bigbluebutton-html5/public/locales/es.json
+++ b/bigbluebutton-html5/public/locales/es.json
@@ -276,7 +276,7 @@
"app.poll.deleteRespDesc": "Remueve opción {0}",
"app.poll.t": "Verdadero",
"app.poll.f": "Falso",
- "app.poll.tf": "Verdadero / Falseo",
+ "app.poll.tf": "Verdadero / Falso",
"app.poll.y": "Sí",
"app.poll.n": "No",
"app.poll.abstention": "Abstención",
diff --git a/bigbluebutton-html5/public/locales/et.json b/bigbluebutton-html5/public/locales/et.json
index d817ae1958..0d6b4d2524 100644
--- a/bigbluebutton-html5/public/locales/et.json
+++ b/bigbluebutton-html5/public/locales/et.json
@@ -207,6 +207,7 @@
"app.presentationUploder.fileToUpload": "Ootab üleslaadimist...",
"app.presentationUploder.currentBadge": "Aktiivne",
"app.presentationUploder.rejectedError": "Valitud fail(id) lükati tagasi. Palun kontrolli failitüüpi.",
+ "app.presentationUploder.connectionClosedError": "Katkestatud halva ühenduvuse tõttu.",
"app.presentationUploder.upload.progress": "Üleslaadimine ({0}%)",
"app.presentationUploder.upload.413": "Fail on liiga suur, maksimaalne suurus on {0} MB.",
"app.presentationUploder.genericError": "Oih, miskit läks valesti...",
@@ -313,7 +314,7 @@
"app.connectingMessage": "Ühendumine...",
"app.waitingMessage": "Ühendus katkes. Uus ühendumiskatse {0} sekundi pärast ...",
"app.retryNow": "Proovi uuesti kohe",
- "app.muteWarning.label": "Klõpsa {0}, et oma vaigistus eemaldada",
+ "app.muteWarning.label": "Klõpsa {0}, et vaigistus eemaldada",
"app.muteWarning.disableMessage": "Vaigistuse alarmid keelatud kuni vaigistuse eemaldamiseni",
"app.muteWarning.tooltip": "Klõpsa, et sulgeda ja keelata hoiatus kuni vaigistuse eemaldamiseni",
"app.navBar.settingsDropdown.optionsLabel": "Valikud",
@@ -603,6 +604,8 @@
"app.guest.guestDeny": "Külalisel ei lubatud koosolekuga ühineda.",
"app.guest.seatWait": "Külaline ootab kohta koosolekul.",
"app.guest.allow": "Külalisele nõusolek antud, suunamine koosolekule.",
+ "app.guest.guestInvalid": "Külaline on sobimatu",
+ "app.guest.meetingForciblyEnded": "Ei saa liituda koosolekuga, mis on lõpetatud",
"app.userList.guest.waitingUsers": "Kasutajate ootamine",
"app.userList.guest.waitingUsersTitle": "Kasutajate haldus",
"app.userList.guest.optionTitle": "Vaata üle ootel olevad kasutajad",
@@ -764,6 +767,7 @@
"app.video.virtualBackground.background": "Taust",
"app.video.virtualBackground.genericError": "Kaameraefekti rakendamine ebaõnnestus. Proovi uuesti.",
"app.video.virtualBackground.camBgAriaDesc": "Määrab {0} veebikaamera virtuaalseks taustaks",
+ "app.video.camCapReached": "Rohkem kaameraid ei saa jagada",
"app.video.dropZoneLabel": "Aseta siia",
"app.fullscreenButton.label": "Laienda {0} täisekraanile",
"app.fullscreenUndoButton.label": "Loobu {0} täisekraanist",
@@ -915,6 +919,17 @@
"playback.error.wrapper.aria": "Vigade ala",
"playback.loader.wrapper.aria": "Laadija ala",
"playback.player.wrapper.aria": "Mängija ala",
+ "playback.player.about.modal.shortcuts.title": "Kiirklahvid",
+ "playback.player.about.modal.shortcuts.alt": "Alt",
+ "playback.player.about.modal.shortcuts.shift": "Shift",
+ "playback.player.about.modal.shortcuts.fullscreen": "Lülita täisekraaniks",
+ "playback.player.about.modal.shortcuts.play": "Esita/Peata",
+ "playback.player.about.modal.shortcuts.section": "Lülita külgsektsiooniks",
+ "playback.player.about.modal.shortcuts.seek.backward": "Otsi tagasisuunas",
+ "playback.player.about.modal.shortcuts.seek.forward": "Otsi edasisuunas",
+ "playback.player.about.modal.shortcuts.skip.next": "Järgmine slaid",
+ "playback.player.about.modal.shortcuts.skip.previous": "Eelmine slaid",
+ "playback.player.about.modal.shortcuts.swap": "Vaheta sisu",
"playback.player.chat.message.poll.name": "Küsitluse tulemus",
"playback.player.chat.message.poll.question": "Küsimus",
"playback.player.chat.message.poll.options": "Valikud",
diff --git a/bigbluebutton-html5/public/locales/fa_IR.json b/bigbluebutton-html5/public/locales/fa_IR.json
index e254786f10..dc4ad01567 100644
--- a/bigbluebutton-html5/public/locales/fa_IR.json
+++ b/bigbluebutton-html5/public/locales/fa_IR.json
@@ -207,6 +207,7 @@
"app.presentationUploder.fileToUpload": "آماده بارگذاری ...",
"app.presentationUploder.currentBadge": "کنونی",
"app.presentationUploder.rejectedError": "پرونده(های) انتخاب شده رد شدند. لطفا نوع پرونده(ها) را بررسی کنید.",
+ "app.presentationUploder.connectionClosedError": "به دلیل اتصال ضعیف قطع شد. لطفا دوباره تلاش کنید. ",
"app.presentationUploder.upload.progress": "در حال بارگذاری ({0}%)",
"app.presentationUploder.upload.413": "حجم پرونده زیاد است، از حداکثر {0} مگابایت بیشتر است",
"app.presentationUploder.genericError": "آخ، خطای پیش آمده است...",
@@ -766,6 +767,7 @@
"app.video.virtualBackground.background": "پسزمینه",
"app.video.virtualBackground.genericError": "افکت دوربین اعمال نشد. مجددا تلاش کنید.",
"app.video.virtualBackground.camBgAriaDesc": "تنظیم پسزمینه مجازی دوربین به {0}",
+ "app.video.camCapReached": "نمیتوانید دوربینهای بیشتری را به اشتراک بگذارید. ",
"app.video.dropZoneLabel": "اینجا بیندازید",
"app.fullscreenButton.label": "تغییر {0} به تمام صفحه",
"app.fullscreenUndoButton.label": "{0} تمام صفحه را واگرد کنید",
diff --git a/bigbluebutton-html5/public/locales/fr.json b/bigbluebutton-html5/public/locales/fr.json
index 7e14bf4ee6..7b2d0f4854 100644
--- a/bigbluebutton-html5/public/locales/fr.json
+++ b/bigbluebutton-html5/public/locales/fr.json
@@ -833,7 +833,7 @@
"app.videoDock.webcamPinDesc": "Épingler la webcam sélectionnée",
"app.videoDock.webcamUnpinLabel": "Désépingler",
"app.videoDock.webcamUnpinLabelDisabled": "Seuls les modérateurs peuvent désépingler les participants",
- "app.videoDock.webcamUnpinDesc": "Ne plus afficher de préférence la webcam sélectionnée",
+ "app.videoDock.webcamUnpinDesc": "Désépingler la webcam sélectionnée",
"app.videoDock.autoplayBlockedDesc": "Nous avons besoin de votre permission pour vous montrer les webcams des autres utilisateurs.",
"app.videoDock.autoplayAllowLabel": "Voir les webcams",
"app.invitation.title": "Invitation à une réunion privée",
@@ -924,9 +924,9 @@
"playback.player.about.modal.shortcuts.shift": "Maj",
"playback.player.about.modal.shortcuts.fullscreen": "Basculer en plein écran",
"playback.player.about.modal.shortcuts.play": "Lecture/Pause",
- "playback.player.about.modal.shortcuts.section": "Escamotage du volet latéral",
- "playback.player.about.modal.shortcuts.seek.backward": "Rechercher en amont",
- "playback.player.about.modal.shortcuts.seek.forward": "Rechercher en aval",
+ "playback.player.about.modal.shortcuts.section": "Basculer le volet latéral",
+ "playback.player.about.modal.shortcuts.seek.backward": "Rechercher en remontant",
+ "playback.player.about.modal.shortcuts.seek.forward": "Rechercher en avançant",
"playback.player.about.modal.shortcuts.skip.next": "Diapositive suivante",
"playback.player.about.modal.shortcuts.skip.previous": "Diapositive précédente",
"playback.player.about.modal.shortcuts.swap": "Permuter le contenu",
diff --git a/bigbluebutton-html5/public/locales/it_IT.json b/bigbluebutton-html5/public/locales/it_IT.json
index 2de8ff5bd9..6b1e5a3ea1 100644
--- a/bigbluebutton-html5/public/locales/it_IT.json
+++ b/bigbluebutton-html5/public/locales/it_IT.json
@@ -911,6 +911,12 @@
"playback.error.wrapper.aria": "Area errori",
"playback.loader.wrapper.aria": "Area caricatore",
"playback.player.wrapper.aria": "Area riproduttore",
+ "playback.player.about.modal.shortcuts.title": "Scorciatoie",
+ "playback.player.about.modal.shortcuts.alt": "Alt",
+ "playback.player.about.modal.shortcuts.shift": "Shift",
+ "playback.player.about.modal.shortcuts.skip.next": "Prossima slide",
+ "playback.player.about.modal.shortcuts.skip.previous": "Slide precedente",
+ "playback.player.about.modal.shortcuts.swap": "Scambia contenuto",
"playback.player.chat.message.poll.name": "Risultati sondaggio",
"playback.player.chat.message.poll.question": "Domanda",
"playback.player.chat.message.poll.options": "Opzioni",
diff --git a/bigbluebutton-html5/public/locales/pt_BR.json b/bigbluebutton-html5/public/locales/pt_BR.json
index db984d28b7..89654a5c1f 100644
--- a/bigbluebutton-html5/public/locales/pt_BR.json
+++ b/bigbluebutton-html5/public/locales/pt_BR.json
@@ -135,6 +135,7 @@
"app.media.autoplayAlertDesc": "Permitir acesso",
"app.media.screenshare.start": "O compartilhamento de tela foi iniciado",
"app.media.screenshare.end": "O compartilhamento de tela foi encerrado",
+ "app.media.screenshare.endDueToDataSaving": "O compartilhamento de tela foi interrompido devido à economia de dados",
"app.media.screenshare.unavailable": "Compartilhamento de tela indisponível",
"app.media.screenshare.notSupported": "Compartilhamento de tela não suportado neste navegador.",
"app.media.screenshare.autoplayBlockedDesc": "Precisamos da sua permissão para mostrar a tela do apresentador.",
@@ -164,6 +165,7 @@
"app.presentation.slideContent": "Conteúdo do slide",
"app.presentation.startSlideContent": "Início do conteúdo do slide",
"app.presentation.endSlideContent": "Fim do conteúdo do slide",
+ "app.presentation.changedSlideContent": "Apresentação alterada para slide: {0}",
"app.presentation.emptySlideContent": "Nenhum conteúdo no slide atual",
"app.presentation.presentationToolbar.noNextSlideDesc": "Fim da apresentação",
"app.presentation.presentationToolbar.noPrevSlideDesc": "Início da apresentação",
@@ -192,6 +194,7 @@
"app.presentation.placeholder": "Esperando a apresentação ser carregada",
"app.presentationUploder.title": "Apresentação",
"app.presentationUploder.message": "Como apresentador, você pode enviar qualquer documento do Office ou arquivo PDF. Para melhores resultados, recomendamos que se carregue arquivos em PDF. Por favor, certifique-se de que uma apresentação seja selecionada usando a caixa de seleção circular à direita.",
+ "app.presentationUploder.extraHint": "IMPORTANTE: cada arquivo não pode exceder {0} MB e {1} páginas.",
"app.presentationUploder.uploadLabel": "Enviar",
"app.presentationUploder.confirmLabel": "Confirmar",
"app.presentationUploder.confirmDesc": "Salvar as alterações e inicie a apresentação",
@@ -204,6 +207,7 @@
"app.presentationUploder.fileToUpload": "Carregar arquivo...",
"app.presentationUploder.currentBadge": "Atual",
"app.presentationUploder.rejectedError": "Os arquivos selecionados foram rejeitados. Por favor, verifique os tipos de arquivos permitidos.",
+ "app.presentationUploder.connectionClosedError": "Interrompido por conectividade ruim. Por favor, tente novamente.",
"app.presentationUploder.upload.progress": "Carregando ({0}%)",
"app.presentationUploder.upload.413": "O arquivo é muito grande, excedeu o limite de {0} MB",
"app.presentationUploder.genericError": "Ops, algo deu errado ...",
@@ -600,6 +604,8 @@
"app.guest.guestDeny": "Convidado teve sua entrada negada.",
"app.guest.seatWait": "Convidado aguardando uma vaga na reunião.",
"app.guest.allow": "Convidado aprovado e sendo redirecionado para a sessão.",
+ "app.guest.guestInvalid": "O usuário convidado é inválido",
+ "app.guest.meetingForciblyEnded": "Você não pode entrar em uma reunião que já foi encerrada",
"app.userList.guest.waitingUsers": "Aguardando",
"app.userList.guest.waitingUsersTitle": "Convidados",
"app.userList.guest.optionTitle": "Usuários aguardando aprovação",
@@ -761,6 +767,7 @@
"app.video.virtualBackground.background": "Fundo",
"app.video.virtualBackground.genericError": "Falha ao aplicar efeito de câmera. Tente novamente.",
"app.video.virtualBackground.camBgAriaDesc": "Define fundo virtual de camera para {0}",
+ "app.video.camCapReached": "Você não pode compartilhar mais câmeras",
"app.video.dropZoneLabel": "Largar aqui",
"app.fullscreenButton.label": "Alternar {0} para tela cheia",
"app.fullscreenUndoButton.label": "Desfazer {0} tela inteira",
@@ -912,6 +919,17 @@
"playback.error.wrapper.aria": "Área de erro",
"playback.loader.wrapper.aria": "Área do carregador",
"playback.player.wrapper.aria": "Área do player",
+ "playback.player.about.modal.shortcuts.title": "Atalhos",
+ "playback.player.about.modal.shortcuts.alt": "Alt",
+ "playback.player.about.modal.shortcuts.shift": "Shift",
+ "playback.player.about.modal.shortcuts.fullscreen": "Alternar tela cheia",
+ "playback.player.about.modal.shortcuts.play": "Reproduzir/Pausar",
+ "playback.player.about.modal.shortcuts.section": "Alternar seção lateral",
+ "playback.player.about.modal.shortcuts.seek.backward": "Buscar para trás",
+ "playback.player.about.modal.shortcuts.seek.forward": "Procurar para a frente",
+ "playback.player.about.modal.shortcuts.skip.next": "Próximo slide",
+ "playback.player.about.modal.shortcuts.skip.previous": "Slide anterior",
+ "playback.player.about.modal.shortcuts.swap": "Trocar conteúdo",
"playback.player.chat.message.poll.name": "Resultado da enquete",
"playback.player.chat.message.poll.question": "Pergunta",
"playback.player.chat.message.poll.options": "Opções",
@@ -920,6 +938,7 @@
"playback.player.chat.message.poll.option.abstention": "Abstenção",
"playback.player.chat.message.poll.option.true": "Verdadeiro",
"playback.player.chat.message.poll.option.false": "Falso",
+ "playback.player.chat.message.video.name": "Vídeo externo",
"playback.player.chat.wrapper.aria": "Área do bate-papo",
"playback.player.notes.wrapper.aria": "Área das anotações",
"playback.player.presentation.wrapper.aria": "Área da apresentação",
@@ -927,6 +946,7 @@
"playback.player.search.modal.title": "Procurar",
"playback.player.search.modal.subtitle": "Encontre o conteúdo dos slides da apresentação",
"playback.player.thumbnails.wrapper.aria": "Área das miniaturas",
+ "playback.player.webcams.wrapper.aria": "Área de webcams",
"app.learningDashboard.dashboardTitle": "Painel Analítico de Aprendizagem",
"app.learningDashboard.user": "Usuário",
"app.learningDashboard.indicators.meetingStatusEnded": "Finalizado",
diff --git a/bigbluebutton-html5/public/locales/ru.json b/bigbluebutton-html5/public/locales/ru.json
index de49fe29e4..f0f86f1183 100644
--- a/bigbluebutton-html5/public/locales/ru.json
+++ b/bigbluebutton-html5/public/locales/ru.json
@@ -426,7 +426,7 @@
"app.actionsBar.actionsDropdown.actionsLabel": "Действия",
"app.actionsBar.actionsDropdown.presentationLabel": "Управление презентациями",
"app.actionsBar.actionsDropdown.initPollLabel": "Начать голосование",
- "app.actionsBar.actionsDropdown.desktopShareLabel": "Демонстировать ваш экран",
+ "app.actionsBar.actionsDropdown.desktopShareLabel": "Демонстрировать ваш экран",
"app.actionsBar.actionsDropdown.lockedDesktopShareLabel": "Демонстрация экрана заблокирована",
"app.actionsBar.actionsDropdown.stopDesktopShareLabel": "Остановить демонстрацию экрана",
"app.actionsBar.actionsDropdown.presentationDesc": "Загрузите вашу презентацию",
From aec688dfb69a324ab0137a4cf195b4fc86459a45 Mon Sep 17 00:00:00 2001
From: Anton Georgiev
Date: Thu, 31 Mar 2022 15:06:08 -0400
Subject: [PATCH 042/180] Updated the release to 2.4.6
---
bigbluebutton-config/bigbluebutton-release | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bigbluebutton-config/bigbluebutton-release b/bigbluebutton-config/bigbluebutton-release
index 1fc0e0b108..eae9d60cdb 100644
--- a/bigbluebutton-config/bigbluebutton-release
+++ b/bigbluebutton-config/bigbluebutton-release
@@ -1 +1 @@
-BIGBLUEBUTTON_RELEASE=2.4.5
+BIGBLUEBUTTON_RELEASE=2.4.6
From 5d6401828c2f7e14f6d95af8cad588892df16598 Mon Sep 17 00:00:00 2001
From: Guilherme Leme
Date: Thu, 31 Mar 2022 16:37:46 -0300
Subject: [PATCH 043/180] [issue-14304] - Changes in review.
---
record-and-playback/core/lib/recordandplayback.rb | 2 +-
record-and-playback/presentation/scripts/caption/presentation | 2 +-
.../presentation/scripts/process/presentation.rb | 2 +-
.../presentation/scripts/publish/presentation.rb | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/record-and-playback/core/lib/recordandplayback.rb b/record-and-playback/core/lib/recordandplayback.rb
index 4c5cc227c8..07b7c0cac1 100644
--- a/record-and-playback/core/lib/recordandplayback.rb
+++ b/record-and-playback/core/lib/recordandplayback.rb
@@ -240,7 +240,7 @@ module BigBlueButton
def self.read_props
return @props if @props
- filepathRecOverride = "/etc/bigbluebutton/recording.yml"
+ filepathRecOverride = "/etc/bigbluebutton/recording/recording.yml"
hasOverride = File.file?(filepathRecOverride)
filepath = File.join(BigBlueButton.rap_scripts_path, 'bigbluebutton.yml')
diff --git a/record-and-playback/presentation/scripts/caption/presentation b/record-and-playback/presentation/scripts/caption/presentation
index d9be5f6e88..af4546dcf4 100755
--- a/record-and-playback/presentation/scripts/caption/presentation
+++ b/record-and-playback/presentation/scripts/caption/presentation
@@ -47,7 +47,7 @@ presentation_props = File.open(File.expand_path('../presentation.yml', __dir__))
YAML.safe_load(pres_yml)
end
-filepathPresOverride = "/etc/bigbluebutton/presentation.yml"
+filepathPresOverride = "/etc/bigbluebutton/recording/presentation.yml"
isThereOverride = File.file?(filepathPresOverride)
if (isThereOverride)
presOverrideProps = YAML::load(File.open(filepathPresOverride))
diff --git a/record-and-playback/presentation/scripts/process/presentation.rb b/record-and-playback/presentation/scripts/process/presentation.rb
index 72d11768fd..06878e5e26 100755
--- a/record-and-playback/presentation/scripts/process/presentation.rb
+++ b/record-and-playback/presentation/scripts/process/presentation.rb
@@ -41,7 +41,7 @@ meeting_id = opts[:meeting_id]
# This script lives in scripts/archive/steps while properties.yaml lives in scripts/
props = BigBlueButton.read_props
presentation_props = YAML.safe_load(File.open('presentation.yml'))
-filepathPresOverride = "/etc/bigbluebutton/presentation.yml"
+filepathPresOverride = "/etc/bigbluebutton/recording/presentation.yml"
hasOverride = File.file?(filepathPresOverride)
if (hasOverride)
presOverrideProps = YAML::load(File.open(filepathPresOverride))
diff --git a/record-and-playback/presentation/scripts/publish/presentation.rb b/record-and-playback/presentation/scripts/publish/presentation.rb
index 55e14653d8..fff10f5f2c 100755
--- a/record-and-playback/presentation/scripts/publish/presentation.rb
+++ b/record-and-playback/presentation/scripts/publish/presentation.rb
@@ -32,7 +32,7 @@ require 'json'
# This script lives in scripts/archive/steps while properties.yaml lives in scripts/
bbb_props = BigBlueButton.read_props
$presentation_props = YAML::load(File.read('presentation.yml'))
-filepathPresOverride = "/etc/bigbluebutton/presentation.yml"
+filepathPresOverride = "/etc/bigbluebutton/recording/presentation.yml"
hasOverride = File.file?(filepathPresOverride)
if (hasOverride)
presOverrideProps = YAML::load(File.open(filepathPresOverride))
From 06c9cad31fcdbe2b6992c97ac9065df9c51bd272 Mon Sep 17 00:00:00 2001
From: Paulo Lanzarin <4529051+prlanzarin@users.noreply.github.com>
Date: Fri, 1 Apr 2022 15:50:16 -0300
Subject: [PATCH 044/180] build: bump bbb-webrtc-sfu to v2.6.13
---
bbb-webrtc-sfu.placeholder.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bbb-webrtc-sfu.placeholder.sh b/bbb-webrtc-sfu.placeholder.sh
index 3dbf98b019..a87773e6b2 100644
--- a/bbb-webrtc-sfu.placeholder.sh
+++ b/bbb-webrtc-sfu.placeholder.sh
@@ -1 +1 @@
-git clone --branch v2.6.11 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu
+git clone --branch v2.6.13 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu
From 275c0dc51f6e8c698515368e01e67e21303f42a2 Mon Sep 17 00:00:00 2001
From: Joao Victor
Date: Mon, 4 Apr 2022 10:09:40 -0300
Subject: [PATCH 045/180] Automatically redirect viewers after feedback
---
.../imports/ui/components/meeting-ended/component.jsx | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
index edaaf02ff9..6c67425175 100755
--- a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
@@ -230,7 +230,14 @@ class MeetingEnded extends PureComponent {
dispatched: true,
});
- fetch(url, options).catch((e) => {
+ fetch(url, options).then(() => {
+ if (!(this.localUserRole.toLowerCase() === 'moderator')) {
+ const REDIRECT_WAIT_TIME = 5000;
+ setTimeout(() => {
+ logoutRouteHandler();
+ }, REDIRECT_WAIT_TIME);
+ }
+ }).catch((e) => {
logger.warn({
logCode: 'user_feedback_not_sent_error',
extraInfo: {
From a7c9ab18313d5236713dfbc8593ce4321c8887a8 Mon Sep 17 00:00:00 2001
From: Paulo Lanzarin <4529051+prlanzarin@users.noreply.github.com>
Date: Mon, 4 Apr 2022 10:20:18 -0300
Subject: [PATCH 046/180] fix(bbb-conf): remove KMS STUN server checks
Unnecessary in 2.5 (and 2.4 even, but I wont touch that)
---
bigbluebutton-config/bin/bbb-conf | 21 ---------------------
1 file changed, 21 deletions(-)
diff --git a/bigbluebutton-config/bin/bbb-conf b/bigbluebutton-config/bin/bbb-conf
index 5a6d6286c8..223a731f9e 100755
--- a/bigbluebutton-config/bin/bbb-conf
+++ b/bigbluebutton-config/bin/bbb-conf
@@ -1296,19 +1296,6 @@ check_state() {
done
fi
- stunServerAddress=$(cat /etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini | sed -n '/^stunServerAddress/{s/.*=//;p}')
- stunServerPort=$(cat /etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini | sed -n '/^stunServerPort/{s/.*=//;p}')
- if [ ! -z "$stunServerAddress" ]; then
- if stunclient --mode full --localport 30000 $stunServerAddress $stunServerPort | grep -q "fail\|Unable\ to\ resolve"; then
- echo
- echo "#"
- echo "# Warning: Failed to verify STUN server at $stunServerAddress:$stunServerPort with command"
- echo "#"
- echo "# stunclient --mode full --localport 30000 $stunServerAddress $stunServerPort"
- echo "#"
- fi
- fi
-
BBB_LOG="/var/log/bigbluebutton"
if [ "$(stat -c "%U %G" $BBB_LOG)" != "bigbluebutton bigbluebutton" ]; then
echo
@@ -1476,14 +1463,6 @@ if [ $CHECK ]; then
done
fi
- stunServerAddress=$(cat /etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini | sed -n '/^stunServerAddress/{s/.*=//;p}')
- stunServerPort=$(cat /etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini | sed -n '/^stunServerPort/{s/.*=//;p}')
- if [ ! -z "$stunServerAddress" ]; then
- echo
- echo "/etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini (STUN Server)"
- echo " stun: $stunServerAddress:$stunServerPort"
- fi
-
check_state
echo
From f8e6eb669a168fc3bf9446571fede17683d985bb Mon Sep 17 00:00:00 2001
From: Joao Victor
Date: Mon, 4 Apr 2022 11:02:05 -0300
Subject: [PATCH 047/180] Fix user role comparison
---
.../imports/ui/components/meeting-ended/component.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
index 6c67425175..727fb3e6d3 100755
--- a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
@@ -231,7 +231,7 @@ class MeetingEnded extends PureComponent {
});
fetch(url, options).then(() => {
- if (!(this.localUserRole.toLowerCase() === 'moderator')) {
+ if (this.localUserRole === 'VIEWER') {
const REDIRECT_WAIT_TIME = 5000;
setTimeout(() => {
logoutRouteHandler();
From 159a09be49a863543738fd6ed6b9b1625b8a5c25 Mon Sep 17 00:00:00 2001
From: Tainan Felipe
Date: Mon, 4 Apr 2022 14:26:00 -0300
Subject: [PATCH 048/180] Small adjustments
---
.../apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala | 2 --
.../imports/api/users/server/handlers/userLeftFlagUpdated.js | 5 ++++-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala
index 670d3907d4..83b32be73e 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala
@@ -6,7 +6,6 @@ import org.bigbluebutton.core.apps.voice.UserJoinedVoiceConfEvtMsgHdlr
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.Users2x
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, MeetingActor, OutMsgRouter }
-import org.bigbluebutton.core2.message.senders.MsgBuilder
trait UserJoinMeetingAfterReconnectReqMsgHdlr extends HandlerHelpers with UserJoinedVoiceConfEvtMsgHdlr {
this: MeetingActor =>
@@ -21,7 +20,6 @@ trait UserJoinMeetingAfterReconnectReqMsgHdlr extends HandlerHelpers with UserJo
if (reconnectingUser.userLeftFlag.left) {
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
// User has reconnected. Just reset it's flag. ralam Oct 23, 2018
- println(Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId))
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false);
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
}
diff --git a/bigbluebutton-html5/imports/api/users/server/handlers/userLeftFlagUpdated.js b/bigbluebutton-html5/imports/api/users/server/handlers/userLeftFlagUpdated.js
index 17abfb89af..328e2fc78b 100644
--- a/bigbluebutton-html5/imports/api/users/server/handlers/userLeftFlagUpdated.js
+++ b/bigbluebutton-html5/imports/api/users/server/handlers/userLeftFlagUpdated.js
@@ -4,7 +4,10 @@ import userLeftFlag from '../modifiers/userLeftFlagUpdated';
export default function handleUserLeftFlag({ body }, meetingId) {
const user = body;
- check(user, Object);
+ check(user, {
+ intId: String,
+ userLeftFlag: Boolean,
+ });
userLeftFlag(meetingId, user.intId, user.userLeftFlag);
}
From 231a25f0cb516986dc94f37d547776f176f3d2fe Mon Sep 17 00:00:00 2001
From: Tainan Felipe
Date: Mon, 4 Apr 2022 14:40:59 -0300
Subject: [PATCH 049/180] Remove trailling semicolon
---
.../users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala | 2 +-
.../core/apps/users/UserJoinMeetingReqMsgHdlr.scala | 7 +++----
.../core/apps/users/UserLeaveReqMsgHdlr.scala | 2 +-
3 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala
index 83b32be73e..7024da02cf 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingAfterReconnectReqMsgHdlr.scala
@@ -20,7 +20,7 @@ trait UserJoinMeetingAfterReconnectReqMsgHdlr extends HandlerHelpers with UserJo
if (reconnectingUser.userLeftFlag.left) {
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
// User has reconnected. Just reset it's flag. ralam Oct 23, 2018
- sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false);
+ sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false)
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
}
state
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala
index 790bccb72f..ad4d5fbe88 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala
@@ -2,10 +2,9 @@ package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs.UserJoinMeetingReqMsg
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers
-import org.bigbluebutton.core.models.{ Users2x, VoiceUsers }
import org.bigbluebutton.core.domain.MeetingState2x
-import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, MeetingActor, OutMsgRouter }
-import org.bigbluebutton.core2.message.senders.MsgBuilder
+import org.bigbluebutton.core.models.{Users2x, VoiceUsers}
+import org.bigbluebutton.core.running.{HandlerHelpers, LiveMeeting, MeetingActor, OutMsgRouter}
trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
this: MeetingActor =>
@@ -21,7 +20,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
if (reconnectingUser.userLeftFlag.left) {
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
// User has reconnected. Just reset it's flag. ralam Oct 23, 2018
- sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false);
+ sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false)
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala
index 9da0080f5c..6f3b721ca8 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala
@@ -19,7 +19,7 @@ trait UserLeaveReqMsgHdlr extends HandlerHelpers {
// Just flag that user has left as the user might be reconnecting.
// An audit will remove this user if it hasn't rejoined after a certain period of time.
// ralam oct 23, 2018
- sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, true);
+ sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, true)
Users2x.setUserLeftFlag(liveMeeting.users2x, msg.body.userId)
}
From fbf33ba298fd3424e99d08922eae6f0c16ac6b8d Mon Sep 17 00:00:00 2001
From: Bohdan Zhemelinskyi
Date: Mon, 4 Apr 2022 18:24:50 +0000
Subject: [PATCH 050/180] upgrade embedded tomcat
---
bigbluebutton-web/build.gradle | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/bigbluebutton-web/build.gradle b/bigbluebutton-web/build.gradle
index 9c2989b1db..5f9b603458 100755
--- a/bigbluebutton-web/build.gradle
+++ b/bigbluebutton-web/build.gradle
@@ -57,7 +57,7 @@ dependencies {
implementation "org.springframework.boot:spring-boot-autoconfigure"
implementation "org.grails:grails-core"
implementation "org.springframework.boot:spring-boot-starter-actuator"
- implementation "org.springframework.boot:spring-boot-starter-tomcat"
+ implementation "org.springframework.boot:spring-boot-starter-tomcat:2.6.6"
implementation "org.grails:grails-web-boot"
implementation "org.grails:grails-logging"
implementation "org.grails:grails-plugin-rest"
@@ -72,6 +72,10 @@ dependencies {
implementation "org.grails.plugins:scaffolding"
implementation "org.grails.plugins:events"
implementation "org.grails.plugins:gsp"
+ implementation "org.apache.tomcat.embed:tomcat-embed-core:9.0.62"
+ implementation "org.apache.tomcat.embed:tomcat-embed-el:9.0.62"
+ implementation "org.apache.tomcat.embed:tomcat-embed-websocket:9.0.62"
+ implementation "org.apache.tomcat:tomcat-annotations-api:9.0.62"
//--- BigBlueButton Dependencies Start - Transitive dependencies have to be re-defined below
implementation "org.bigbluebutton:bbb-common-message_2.13:0.0.21-SNAPSHOT"
implementation "org.bigbluebutton:bbb-common-web:0.0.3-SNAPSHOT"
From d9dc567da38929eb3675699dec8b38e0e71a99d0 Mon Sep 17 00:00:00 2001
From: Bohdan Zhemelinskyi
Date: Mon, 4 Apr 2022 20:25:57 +0000
Subject: [PATCH 051/180] tomcat embedded version in one place
---
bigbluebutton-web/build.gradle | 8 ++++----
bigbluebutton-web/gradle.properties | 3 ++-
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/bigbluebutton-web/build.gradle b/bigbluebutton-web/build.gradle
index 5f9b603458..a2076b3acf 100755
--- a/bigbluebutton-web/build.gradle
+++ b/bigbluebutton-web/build.gradle
@@ -72,10 +72,10 @@ dependencies {
implementation "org.grails.plugins:scaffolding"
implementation "org.grails.plugins:events"
implementation "org.grails.plugins:gsp"
- implementation "org.apache.tomcat.embed:tomcat-embed-core:9.0.62"
- implementation "org.apache.tomcat.embed:tomcat-embed-el:9.0.62"
- implementation "org.apache.tomcat.embed:tomcat-embed-websocket:9.0.62"
- implementation "org.apache.tomcat:tomcat-annotations-api:9.0.62"
+ implementation "org.apache.tomcat.embed:tomcat-embed-core:${tomcatEmbedVersion}"
+ implementation "org.apache.tomcat.embed:tomcat-embed-el:${tomcatEmbedVersion}"
+ implementation "org.apache.tomcat.embed:tomcat-embed-websocket:${tomcatEmbedVersion}"
+ implementation "org.apache.tomcat:tomcat-annotations-api:${tomcatEmbedVersion}"
//--- BigBlueButton Dependencies Start - Transitive dependencies have to be re-defined below
implementation "org.bigbluebutton:bbb-common-message_2.13:0.0.21-SNAPSHOT"
implementation "org.bigbluebutton:bbb-common-web:0.0.3-SNAPSHOT"
diff --git a/bigbluebutton-web/gradle.properties b/bigbluebutton-web/gradle.properties
index 506fcddc46..535320c838 100644
--- a/bigbluebutton-web/gradle.properties
+++ b/bigbluebutton-web/gradle.properties
@@ -2,4 +2,5 @@ grailsVersion=5.0.1
gormVersion=7.1.0
gradleWrapperVersion=7.3.1
grailsGradlePluginVersion=5.0.0
-groovyVersion=3.0.9
\ No newline at end of file
+groovyVersion=3.0.9
+tomcatEmbedVersion=9.0.62
From 0e29c1fdc3007b51b3c09f1f636ba420f0b30a7f Mon Sep 17 00:00:00 2001
From: Joao Victor
Date: Mon, 4 Apr 2022 16:53:01 -0300
Subject: [PATCH 052/180] Fix link sanitizing in welcome message.
---
.../meetings/server/modifiers/addMeeting.js | 23 ++++++++++++-------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js b/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js
index c82baeb2de..02702537eb 100755
--- a/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js
@@ -139,10 +139,13 @@ export default function addMeeting(meeting) {
const sanitizeTextInChat = original => SanitizeHTML(original, {
allowedTags: ['a', 'b', 'br', 'i', 'img', 'li', 'small', 'span', 'strong', 'u', 'ul'],
allowedAttributes: {
- a: ['href', 'name', 'target'],
+ a: ['href', 'target'],
img: ['src', 'width', 'height'],
},
allowedSchemes: ['https'],
+ allowedSchemesByTag: {
+ a: ['https', 'mailto', 'tel']
+ }
});
const sanitizedWelcomeText = sanitizeTextInChat(welcomeMsg);
@@ -153,14 +156,18 @@ export default function addMeeting(meeting) {
const insertBlankTarget = (s, i) => `${s.substr(0, i)} target="_blank"${s.substr(i)}`;
const linkWithoutTarget = new RegExp('', 'g');
- linkWithoutTarget.test(welcomeMsg);
- if (linkWithoutTarget.lastIndex > 0) {
- welcomeMsg = insertBlankTarget(
- welcomeMsg,
- linkWithoutTarget.lastIndex - 1,
- );
- }
+ do {
+ linkWithoutTarget.test(welcomeMsg);
+
+ if (linkWithoutTarget.lastIndex > 0) {
+ welcomeMsg = insertBlankTarget(
+ welcomeMsg,
+ linkWithoutTarget.lastIndex - 1,
+ );
+ linkWithoutTarget.lastIndex = linkWithoutTarget.lastIndex - 1;
+ }
+ } while (linkWithoutTarget.lastIndex > 0);
newMeeting.welcomeProp.welcomeMsg = welcomeMsg;
From a5601c0c8f08f2b951a338ce947d9376d25512b8 Mon Sep 17 00:00:00 2001
From: Guilherme Leme
Date: Tue, 5 Apr 2022 14:23:17 -0300
Subject: [PATCH 053/180] [Update to nokogiri] - Change in the Gemfile to run
in the new version.
---
record-and-playback/core/Gemfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/record-and-playback/core/Gemfile b/record-and-playback/core/Gemfile
index 9e638aacdb..98082a45ab 100644
--- a/record-and-playback/core/Gemfile
+++ b/record-and-playback/core/Gemfile
@@ -26,7 +26,7 @@ gem 'journald-logger', '~> 3.0'
gem 'jwt', '~> 2.2'
gem 'locale', '~> 2.1'
gem 'loofah', '~> 2.3'
-gem 'nokogiri'
+gem 'nokogiri', '~> 1.13', '>= 1.13.3'
gem 'open4', '~> 1.3'
gem 'rb-inotify', '~> 0.10'
gem 'redis', '~> 4.1'
From a1a8d68b4d48333d7bf037f264cf0478913b0cc8 Mon Sep 17 00:00:00 2001
From: Ramon Souza
Date: Tue, 5 Apr 2022 14:37:52 -0300
Subject: [PATCH 054/180] fix presentation size 0 bug
---
.../imports/ui/components/presentation/component.jsx | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
index 060b2a0338..964e91e39c 100755
--- a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
@@ -158,6 +158,8 @@ class Presentation extends PureComponent {
intl,
} = this.props;
+ const { presentationWidth, presentationHeight } = this.state;
+
const {
numCameras: prevNumCameras,
presentationBounds: prevPresentationBounds,
@@ -235,7 +237,8 @@ class Presentation extends PureComponent {
}
}
- if (presentationBounds !== prevPresentationBounds) this.onResize();
+ if ((presentationBounds !== prevPresentationBounds) ||
+ (!presentationWidth && !presentationHeight)) this.onResize();
}
}
From 021200a800aaf2ba77db6f611b6a09afae081b54 Mon Sep 17 00:00:00 2001
From: Guilherme Leme
Date: Tue, 5 Apr 2022 14:41:03 -0300
Subject: [PATCH 055/180] [Update to nokogiri] - Gemfile.lock
---
record-and-playback/core/Gemfile.lock | 64 +++++++++++++--------------
1 file changed, 32 insertions(+), 32 deletions(-)
diff --git a/record-and-playback/core/Gemfile.lock b/record-and-playback/core/Gemfile.lock
index a869596fb0..b0efb63d69 100644
--- a/record-and-playback/core/Gemfile.lock
+++ b/record-and-playback/core/Gemfile.lock
@@ -2,55 +2,55 @@ GEM
remote: https://rubygems.org/
specs:
absolute_time (1.0.0)
- activesupport (5.2.4.3)
+ activesupport (5.2.7)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
- ast (2.4.0)
+ ast (2.4.2)
bbbevents (1.2.0)
activesupport (~> 5.0, >= 5.0.0.1)
builder (3.2.4)
- concurrent-ruby (1.1.6)
+ concurrent-ruby (1.1.10)
crass (1.0.6)
- fastimage (2.1.7)
- ffi (1.12.1)
- i18n (1.8.3)
+ fastimage (2.2.6)
+ ffi (1.15.5)
+ i18n (1.10.0)
concurrent-ruby (~> 1.0)
jaro_winkler (1.5.4)
java_properties (0.0.4)
- journald-logger (3.0.0)
+ journald-logger (3.1.0)
journald-native (~> 1.0)
- journald-native (1.0.11)
- jwt (2.2.1)
- locale (2.1.2)
- loofah (2.4.0)
+ journald-native (1.0.12)
+ jwt (2.3.0)
+ locale (2.1.3)
+ loofah (2.16.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
- mini_portile2 (2.6.1)
- minitest (5.14.1)
- mono_logger (1.1.0)
- multi_json (1.14.1)
+ mini_portile2 (2.8.0)
+ minitest (5.14.4)
+ mono_logger (1.1.1)
+ multi_json (1.15.0)
mustermann (1.1.1)
ruby2_keywords (~> 0.0.1)
- nokogiri (1.12.5)
- mini_portile2 (~> 2.6.1)
+ nokogiri (1.13.3)
+ mini_portile2 (~> 2.8.0)
racc (~> 1.4)
open4 (1.3.4)
optimist (3.0.1)
- parallel (1.19.1)
- parser (2.7.0.2)
- ast (~> 2.4.0)
+ parallel (1.22.1)
+ parser (3.1.1.0)
+ ast (~> 2.4.1)
racc (1.6.0)
rack (2.2.3)
- rack-protection (2.0.8.1)
+ rack-protection (2.2.0)
rack
- rainbow (3.0.0)
+ rainbow (3.1.1)
rake (13.0.6)
rb-inotify (0.10.1)
ffi (~> 1.0)
- redis (4.1.3)
- redis-namespace (1.7.0)
+ redis (4.6.0)
+ redis-namespace (1.8.2)
redis (>= 3.0.4)
resque (2.0.0)
mono_logger (~> 1.0)
@@ -65,17 +65,17 @@ GEM
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.7)
- ruby-progressbar (1.10.1)
- ruby2_keywords (0.0.2)
- rubyzip (2.0.0)
- sinatra (2.0.8.1)
+ ruby-progressbar (1.11.0)
+ ruby2_keywords (0.0.5)
+ rubyzip (2.3.2)
+ sinatra (2.2.0)
mustermann (~> 1.0)
- rack (~> 2.0)
- rack-protection (= 2.0.8.1)
+ rack (~> 2.2)
+ rack-protection (= 2.2.0)
tilt (~> 2.0)
thread_safe (0.3.6)
tilt (2.0.10)
- tzinfo (1.2.7)
+ tzinfo (1.2.9)
thread_safe (~> 0.1)
unicode-display_width (1.6.1)
vegas (0.1.11)
@@ -95,7 +95,7 @@ DEPENDENCIES
locale (~> 2.1)
loofah (~> 2.3)
minitest (~> 5.14.1)
- nokogiri
+ nokogiri (~> 1.13, >= 1.13.3)
open4 (~> 1.3)
optimist
rake (>= 12.3, < 14)
From fcaf26d1e6ad4a554c62f7f2d536124bb87ace83 Mon Sep 17 00:00:00 2001
From: Ramon Souza
Date: Wed, 6 Apr 2022 10:44:09 -0300
Subject: [PATCH 056/180] fix no presentation bug
---
.../ui/components/presentation/component.jsx | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
index f1a35a5210..6afcd7027a 100755
--- a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
@@ -232,6 +232,20 @@ class Presentation extends PureComponent {
}
if (presentationBounds !== prevPresentationBounds) this.onResize();
+ } else if (slidePosition) {
+ const { width: currWidth, height: currHeight } = slidePosition;
+
+ layoutContextDispatch({
+ type: ACTIONS.SET_PRESENTATION_CURRENT_SLIDE_SIZE,
+ value: {
+ width: currWidth,
+ height: currHeight,
+ },
+ });
+ layoutContextDispatch({
+ type: ACTIONS.SET_PRESENTATION_NUM_CURRENT_SLIDE,
+ value: currentSlide.num,
+ });
}
}
From b62e9dc7d6a41879680bea04583a198b6b1916e1 Mon Sep 17 00:00:00 2001
From: Tainan Felipe
Date: Wed, 6 Apr 2022 14:27:05 -0300
Subject: [PATCH 057/180] Add a simple way to show more than one description in
modal confirmation
---
.../ui/components/common/modal/confirmation/component.jsx | 4 +++-
.../ui/components/common/modal/confirmation/styles.js | 5 +++++
.../ui/components/end-meeting-confirmation/component.jsx | 3 ++-
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/bigbluebutton-html5/imports/ui/components/common/modal/confirmation/component.jsx b/bigbluebutton-html5/imports/ui/components/common/modal/confirmation/component.jsx
index d5d995cafc..a5c54c4257 100644
--- a/bigbluebutton-html5/imports/ui/components/common/modal/confirmation/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/common/modal/confirmation/component.jsx
@@ -71,7 +71,9 @@ class ConfirmationModal extends Component {
-
+
+ {description}
+
{ hasCheckbox ? (