Merge pull request #19927 from antobinary/merge-dev-alpha5
chore: Merge 3.0.0-alpha.5 into develop
This commit is contained in:
commit
99dc23a6f8
2
.gitignore
vendored
2
.gitignore
vendored
@ -23,3 +23,5 @@ cache/*
|
|||||||
artifacts/*
|
artifacts/*
|
||||||
bbb-presentation-video.zip
|
bbb-presentation-video.zip
|
||||||
bbb-presentation-video
|
bbb-presentation-video
|
||||||
|
bbb-graphql-actions-adapter-server/
|
||||||
|
bigbluebutton-html5/public/locales/index.json
|
||||||
|
@ -75,5 +75,6 @@ daemonUser in Linux := user
|
|||||||
daemonGroup in Linux := group
|
daemonGroup in Linux := group
|
||||||
|
|
||||||
javaOptions in Universal ++= Seq("-J-Xms130m", "-J-Xmx256m", "-Dconfig.file=/etc/bigbluebutton/bbb-apps-akka.conf", "-Dlogback.configurationFile=conf/logback.xml")
|
javaOptions in Universal ++= Seq("-J-Xms130m", "-J-Xmx256m", "-Dconfig.file=/etc/bigbluebutton/bbb-apps-akka.conf", "-Dlogback.configurationFile=conf/logback.xml")
|
||||||
|
javaOptions in reStart ++= Seq("-Dconfig.file=/etc/bigbluebutton/bbb-apps-akka.conf", "-Dlogback.configurationFile=conf/logback.xml")
|
||||||
|
|
||||||
debianPackageDependencies in Debian ++= Seq("java17-runtime-headless", "bash")
|
debianPackageDependencies in Debian ++= Seq("java17-runtime-headless", "bash")
|
||||||
|
@ -16,7 +16,7 @@ object Dependencies {
|
|||||||
val pekkoHttpVersion = "1.0.0"
|
val pekkoHttpVersion = "1.0.0"
|
||||||
val gson = "2.8.9"
|
val gson = "2.8.9"
|
||||||
val jackson = "2.13.5"
|
val jackson = "2.13.5"
|
||||||
val logback = "1.2.11"
|
val logback = "1.2.13"
|
||||||
val quicklens = "1.7.5"
|
val quicklens = "1.7.5"
|
||||||
val spray = "1.3.6"
|
val spray = "1.3.6"
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import org.bigbluebutton.core.bus._
|
|||||||
import org.bigbluebutton.core.pubsub.senders.ReceivedJsonMsgHandlerActor
|
import org.bigbluebutton.core.pubsub.senders.ReceivedJsonMsgHandlerActor
|
||||||
import org.bigbluebutton.core2.AnalyticsActor
|
import org.bigbluebutton.core2.AnalyticsActor
|
||||||
import org.bigbluebutton.core2.FromAkkaAppsMsgSenderActor
|
import org.bigbluebutton.core2.FromAkkaAppsMsgSenderActor
|
||||||
import org.bigbluebutton.endpoint.redis.{AppsRedisSubscriberActor, ExportAnnotationsActor, GraphqlActionsActor, LearningDashboardActor, RedisRecorderActor}
|
import org.bigbluebutton.endpoint.redis.{AppsRedisSubscriberActor, ExportAnnotationsActor, GraphqlConnectionsActor, LearningDashboardActor, RedisRecorderActor}
|
||||||
import org.bigbluebutton.common2.bus.IncomingJsonMessageBus
|
import org.bigbluebutton.common2.bus.IncomingJsonMessageBus
|
||||||
import org.bigbluebutton.service.{HealthzService, MeetingInfoActor, MeetingInfoService}
|
import org.bigbluebutton.service.{HealthzService, MeetingInfoActor, MeetingInfoService}
|
||||||
|
|
||||||
@ -67,9 +67,9 @@ object Boot extends App with SystemConfiguration {
|
|||||||
"LearningDashboardActor"
|
"LearningDashboardActor"
|
||||||
)
|
)
|
||||||
|
|
||||||
val graphqlActionsActor = system.actorOf(
|
val graphqlConnectionsActor = system.actorOf(
|
||||||
GraphqlActionsActor.props(system),
|
GraphqlConnectionsActor.props(system, eventBus, outGW),
|
||||||
"GraphqlActionsActor"
|
"GraphqlConnectionsActor"
|
||||||
)
|
)
|
||||||
|
|
||||||
ClientSettings.loadClientSettingsFromFile()
|
ClientSettings.loadClientSettingsFromFile()
|
||||||
@ -89,8 +89,8 @@ object Boot extends App with SystemConfiguration {
|
|||||||
outBus2.subscribe(learningDashboardActor, outBbbMsgMsgChannel)
|
outBus2.subscribe(learningDashboardActor, outBbbMsgMsgChannel)
|
||||||
bbbMsgBus.subscribe(learningDashboardActor, analyticsChannel)
|
bbbMsgBus.subscribe(learningDashboardActor, analyticsChannel)
|
||||||
|
|
||||||
eventBus.subscribe(graphqlActionsActor, meetingManagerChannel)
|
eventBus.subscribe(graphqlConnectionsActor, meetingManagerChannel)
|
||||||
bbbMsgBus.subscribe(graphqlActionsActor, analyticsChannel)
|
bbbMsgBus.subscribe(graphqlConnectionsActor, analyticsChannel)
|
||||||
|
|
||||||
val bbbActor = system.actorOf(BigBlueButtonActor.props(system, eventBus, bbbMsgBus, outGW, healthzService), "bigbluebutton-actor")
|
val bbbActor = system.actorOf(BigBlueButtonActor.props(system, eventBus, bbbMsgBus, outGW, healthzService), "bigbluebutton-actor")
|
||||||
eventBus.subscribe(bbbActor, meetingManagerChannel)
|
eventBus.subscribe(bbbActor, meetingManagerChannel)
|
||||||
|
@ -38,6 +38,9 @@ object ClientSettings extends SystemConfiguration {
|
|||||||
Map[String, Object]()
|
Map[String, Object]()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//Remove `:private` once it's used only by Meteor internal configs
|
||||||
|
clientSettingsFromFile -= "private"
|
||||||
}
|
}
|
||||||
|
|
||||||
def getClientSettingsWithOverride(clientSettingsOverrideJson: String): Map[String, Object] = {
|
def getClientSettingsWithOverride(clientSettingsOverrideJson: String): Map[String, Object] = {
|
||||||
@ -52,6 +55,33 @@ object ClientSettings extends SystemConfiguration {
|
|||||||
} else clientSettingsFromFile
|
} else clientSettingsFromFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def getConfigPropertyValueByPathAsIntOrElse(map: Map[String, Any], path: String, alternativeValue: Int): Int = {
|
||||||
|
getConfigPropertyValueByPath(map, path) match {
|
||||||
|
case Some(configValue: Int) => configValue
|
||||||
|
case _ =>
|
||||||
|
logger.debug(s"Config `$path` with type Integer not found in clientSettings.")
|
||||||
|
alternativeValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def getConfigPropertyValueByPathAsStringOrElse(map: Map[String, Any], path: String, alternativeValue: String): String = {
|
||||||
|
getConfigPropertyValueByPath(map, path) match {
|
||||||
|
case Some(configValue: String) => configValue
|
||||||
|
case _ =>
|
||||||
|
logger.debug(s"Config `$path` with type String not found in clientSettings.")
|
||||||
|
alternativeValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def getConfigPropertyValueByPathAsBooleanOrElse(map: Map[String, Any], path: String, alternativeValue: Boolean): Boolean = {
|
||||||
|
getConfigPropertyValueByPath(map, path) match {
|
||||||
|
case Some(configValue: Boolean) => configValue
|
||||||
|
case _ =>
|
||||||
|
logger.debug(s"Config `$path` with type Boolean found in clientSettings.")
|
||||||
|
alternativeValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def getConfigPropertyValueByPath(map: Map[String, Any], path: String): Option[Any] = {
|
def getConfigPropertyValueByPath(map: Map[String, Any], path: String): Option[Any] = {
|
||||||
val keys = path.split("\\.")
|
val keys = path.split("\\.")
|
||||||
|
|
||||||
@ -90,14 +120,38 @@ object ClientSettings extends SystemConfiguration {
|
|||||||
for {
|
for {
|
||||||
dataChannel <- dataChannels
|
dataChannel <- dataChannels
|
||||||
} yield {
|
} yield {
|
||||||
if (dataChannel.contains("name") && dataChannel.contains("writePermission")) {
|
if (dataChannel.contains("name")) {
|
||||||
val channelName = dataChannel("name").toString
|
val channelName = dataChannel("name").toString
|
||||||
val writePermission = dataChannel("writePermission")
|
val writePermission = {
|
||||||
writePermission match {
|
if (dataChannel.contains("writePermission")) {
|
||||||
case wPerm: List[String] => pluginDataChannels += (channelName -> DataChannel(channelName, wPerm))
|
dataChannel("writePermission") match {
|
||||||
case _ => logger.warn(s"Invalid writePermission for channel $channelName in plugin $pluginName")
|
case wPerm: List[String] => wPerm
|
||||||
|
case _ => {
|
||||||
|
logger.warn(s"Invalid writePermission for channel $channelName in plugin $pluginName")
|
||||||
|
List()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn(s"Missing config writePermission for channel $channelName in plugin $pluginName")
|
||||||
|
List()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val deletePermission = {
|
||||||
|
if (dataChannel.contains("deletePermission")) {
|
||||||
|
dataChannel("deletePermission") match {
|
||||||
|
case dPerm: List[String] => dPerm
|
||||||
|
case _ => {
|
||||||
|
logger.warn(s"Invalid deletePermission for channel $channelName in plugin $pluginName")
|
||||||
|
List()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
List()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginDataChannels += (channelName -> DataChannel(channelName, writePermission, deletePermission))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case _ => logger.warn(s"Plugin $pluginName has an invalid dataChannels format")
|
case _ => logger.warn(s"Plugin $pluginName has an invalid dataChannels format")
|
||||||
}
|
}
|
||||||
@ -112,7 +166,7 @@ object ClientSettings extends SystemConfiguration {
|
|||||||
pluginsFromConfig
|
pluginsFromConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
case class DataChannel(name: String, writePermission: List[String])
|
case class DataChannel(name: String, writePermission: List[String], deletePermission: List[String])
|
||||||
case class Plugin(name: String, url: String, dataChannels: Map[String, DataChannel])
|
case class Plugin(name: String, url: String, dataChannels: Map[String, DataChannel])
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ trait SystemConfiguration {
|
|||||||
lazy val bbbWebPort = Try(config.getInt("services.bbbWebPort")).getOrElse(8888)
|
lazy val bbbWebPort = Try(config.getInt("services.bbbWebPort")).getOrElse(8888)
|
||||||
lazy val bbbWebAPI = Try(config.getString("services.bbbWebAPI")).getOrElse("localhost")
|
lazy val bbbWebAPI = Try(config.getString("services.bbbWebAPI")).getOrElse("localhost")
|
||||||
lazy val bbbWebSharedSecret = Try(config.getString("services.sharedSecret")).getOrElse("changeme")
|
lazy val bbbWebSharedSecret = Try(config.getString("services.sharedSecret")).getOrElse("changeme")
|
||||||
|
lazy val checkSumAlgorithmForBreakouts = Try(config.getString("services.checkSumAlgorithmForBreakouts")).getOrElse("sha256")
|
||||||
lazy val bbbWebModeratorPassword = Try(config.getString("services.moderatorPassword")).getOrElse("changeme")
|
lazy val bbbWebModeratorPassword = Try(config.getString("services.moderatorPassword")).getOrElse("changeme")
|
||||||
lazy val bbbWebViewerPassword = Try(config.getString("services.viewerPassword")).getOrElse("changeme")
|
lazy val bbbWebViewerPassword = Try(config.getString("services.viewerPassword")).getOrElse("changeme")
|
||||||
lazy val keysExpiresInSec = Try(config.getInt("redis.keyExpiry")).getOrElse(14 * 86400) // 14 days
|
lazy val keysExpiresInSec = Try(config.getInt("redis.keyExpiry")).getOrElse(14 * 86400) // 14 days
|
||||||
|
@ -14,6 +14,7 @@ import org.bigbluebutton.SystemConfiguration
|
|||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.db.{ DatabaseConnection, MeetingDAO }
|
import org.bigbluebutton.core.db.{ DatabaseConnection, MeetingDAO }
|
||||||
|
import org.bigbluebutton.core.domain.MeetingEndReason
|
||||||
import org.bigbluebutton.core.running.RunningMeeting
|
import org.bigbluebutton.core.running.RunningMeeting
|
||||||
import org.bigbluebutton.core.util.ColorPicker
|
import org.bigbluebutton.core.util.ColorPicker
|
||||||
import org.bigbluebutton.core2.RunningMeetings
|
import org.bigbluebutton.core2.RunningMeetings
|
||||||
@ -57,6 +58,9 @@ class BigBlueButtonActor(
|
|||||||
override def preStart() {
|
override def preStart() {
|
||||||
bbbMsgBus.subscribe(self, meetingManagerChannel)
|
bbbMsgBus.subscribe(self, meetingManagerChannel)
|
||||||
DatabaseConnection.initialize()
|
DatabaseConnection.initialize()
|
||||||
|
|
||||||
|
//Terminate all previous meetings, as they will not function following the akka-apps restart
|
||||||
|
MeetingDAO.setAllMeetingsEnded(MeetingEndReason.ENDED_DUE_TO_SERVICE_INTERRUPTION, "system")
|
||||||
}
|
}
|
||||||
|
|
||||||
override def postStop() {
|
override def postStop() {
|
||||||
@ -83,6 +87,7 @@ class BigBlueButtonActor(
|
|||||||
case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m)
|
case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m)
|
||||||
case _: UserGraphqlConnectionEstablishedSysMsg => //Ignore
|
case _: UserGraphqlConnectionEstablishedSysMsg => //Ignore
|
||||||
case _: UserGraphqlConnectionClosedSysMsg => //Ignore
|
case _: UserGraphqlConnectionClosedSysMsg => //Ignore
|
||||||
|
case _: CheckGraphqlMiddlewareAlivePongSysMsg => //Ignore
|
||||||
case _ => log.warning("Cannot handle " + msg.envelope.name)
|
case _ => log.warning("Cannot handle " + msg.envelope.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,9 +194,10 @@ class BigBlueButtonActor(
|
|||||||
context.stop(m.actorRef)
|
context.stop(m.actorRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
MeetingDAO.delete(msg.meetingId)
|
// MeetingDAO.delete(msg.meetingId)
|
||||||
|
// MeetingDAO.setMeetingEnded(msg.meetingId)
|
||||||
// Removing the meeting is enough, all other tables has "ON DELETE CASCADE"
|
// Removing the meeting is enough, all other tables has "ON DELETE CASCADE"
|
||||||
// UserDAO.deleteAllFromMeeting(msg.meetingId)
|
// UserDAO.softDeleteAllFromMeeting(msg.meetingId)
|
||||||
// MeetingRecordingDAO.updateStopped(msg.meetingId, "")
|
// MeetingRecordingDAO.updateStopped(msg.meetingId, "")
|
||||||
|
|
||||||
//Remove ColorPicker idx of the meeting
|
//Remove ColorPicker idx of the meeting
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.bigbluebutton.core.api
|
package org.bigbluebutton.core.api
|
||||||
|
|
||||||
|
import org.bigbluebutton.core.apps.users.UserEstablishedGraphqlConnectionInternalMsgHdlr
|
||||||
import org.bigbluebutton.core.domain.{ BreakoutUser, BreakoutVoiceUser }
|
import org.bigbluebutton.core.domain.{ BreakoutUser, BreakoutVoiceUser }
|
||||||
import spray.json.JsObject
|
import spray.json.JsObject
|
||||||
case class InMessageHeader(name: String)
|
case class InMessageHeader(name: String)
|
||||||
@ -126,6 +127,18 @@ case class SetPresenterInDefaultPodInternalMsg(presenterId: String) extends InMe
|
|||||||
*/
|
*/
|
||||||
case class CaptureSharedNotesReqInternalMsg(breakoutId: String, filename: String) extends InMessage
|
case class CaptureSharedNotesReqInternalMsg(breakoutId: String, filename: String) extends InMessage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent by GraphqlActionsActor to inform MeetingActor that user disconnected
|
||||||
|
* @param userId
|
||||||
|
*/
|
||||||
|
case class UserClosedAllGraphqlConnectionsInternalMsg(userId: String) extends InMessage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent by GraphqlActionsActor to inform MeetingActor that user came back from disconnection
|
||||||
|
* @param userId
|
||||||
|
*/
|
||||||
|
case class UserEstablishedGraphqlConnectionInternalMsg(userId: String) extends InMessage
|
||||||
|
|
||||||
// DeskShare
|
// DeskShare
|
||||||
case class DeskShareStartedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
|
case class DeskShareStartedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
|
||||||
case class DeskShareStoppedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
|
case class DeskShareStoppedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
|
||||||
|
@ -45,6 +45,14 @@ object TimerModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def setRunning(model: TimerModel, running: Boolean): Unit = {
|
def setRunning(model: TimerModel, running: Boolean): Unit = {
|
||||||
|
|
||||||
|
//If it is running and will stop, calculate new Accumulated
|
||||||
|
if(getRunning(model) && !running) {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
val accumulated = getAccumulated(model) + Math.abs(now - getStartedAt(model)).toInt
|
||||||
|
this.setAccumulated(model, accumulated)
|
||||||
|
}
|
||||||
|
|
||||||
model.running = running
|
model.running = running
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,18 +47,30 @@ class WhiteboardModel extends SystemConfiguration {
|
|||||||
}).toMap
|
}).toMap
|
||||||
|
|
||||||
def addAnnotations(wbId: String, userId: String, annotations: Array[AnnotationVO], isPresenter: Boolean, isModerator: Boolean): Array[AnnotationVO] = {
|
def addAnnotations(wbId: String, userId: String, annotations: Array[AnnotationVO], isPresenter: Boolean, isModerator: Boolean): Array[AnnotationVO] = {
|
||||||
var annotationsAdded = Array[AnnotationVO]()
|
|
||||||
val wb = getWhiteboard(wbId)
|
val wb = getWhiteboard(wbId)
|
||||||
|
|
||||||
|
var annotationsAdded = Array[AnnotationVO]()
|
||||||
var newAnnotationsMap = wb.annotationsMap
|
var newAnnotationsMap = wb.annotationsMap
|
||||||
|
|
||||||
for (annotation <- annotations) {
|
for (annotation <- annotations) {
|
||||||
val oldAnnotation = wb.annotationsMap.get(annotation.id)
|
val oldAnnotation = wb.annotationsMap.get(annotation.id)
|
||||||
if (!oldAnnotation.isEmpty) {
|
if (!oldAnnotation.isEmpty) {
|
||||||
val hasPermission = isPresenter || isModerator || oldAnnotation.get.userId == userId
|
val hasPermission = isPresenter || isModerator || oldAnnotation.get.userId == userId
|
||||||
if (hasPermission) {
|
if (hasPermission) {
|
||||||
val newAnnotation = oldAnnotation.get.copy(annotationInfo = deepMerge(oldAnnotation.get.annotationInfo, annotation.annotationInfo))
|
// Merge old and new annotation properties
|
||||||
|
val mergedAnnotationInfo = deepMerge(oldAnnotation.get.annotationInfo, annotation.annotationInfo)
|
||||||
|
|
||||||
|
// Apply cleaning if it's an arrow annotation
|
||||||
|
val finalAnnotationInfo = if (annotation.annotationInfo.get("type").contains("arrow")) {
|
||||||
|
cleanArrowAnnotationProps(mergedAnnotationInfo)
|
||||||
|
} else {
|
||||||
|
mergedAnnotationInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
val newAnnotation = oldAnnotation.get.copy(annotationInfo = finalAnnotationInfo)
|
||||||
newAnnotationsMap += (annotation.id -> newAnnotation)
|
newAnnotationsMap += (annotation.id -> newAnnotation)
|
||||||
annotationsAdded :+= annotation
|
annotationsAdded :+= newAnnotation
|
||||||
PresAnnotationDAO.insertOrUpdate(newAnnotation, annotation)
|
PresAnnotationDAO.insertOrUpdate(newAnnotation, newAnnotation)
|
||||||
println(s"Updated annotation on page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
|
println(s"Updated annotation on page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
|
||||||
} else {
|
} else {
|
||||||
println(s"User $userId doesn't have permission to edit annotation ${annotation.id}, ignoring...")
|
println(s"User $userId doesn't have permission to edit annotation ${annotation.id}, ignoring...")
|
||||||
@ -69,40 +81,67 @@ class WhiteboardModel extends SystemConfiguration {
|
|||||||
PresAnnotationDAO.insertOrUpdate(annotation, annotation)
|
PresAnnotationDAO.insertOrUpdate(annotation, annotation)
|
||||||
println(s"Adding annotation to page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
|
println(s"Adding annotation to page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
|
||||||
} else {
|
} else {
|
||||||
println(s"New annotation [${annotation.id}] with no type, ignoring (probably received a remove message before and now the shape is incomplete, ignoring...")
|
println(s"New annotation [${annotation.id}] with no type, ignoring...")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val newWb = wb.copy(annotationsMap = newAnnotationsMap)
|
val newWb = wb.copy(annotationsMap = newAnnotationsMap)
|
||||||
saveWhiteboard(newWb)
|
saveWhiteboard(newWb)
|
||||||
annotationsAdded
|
annotationsAdded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def cleanArrowAnnotationProps(annotationInfo: Map[String, _]): Map[String, _] = {
|
||||||
|
annotationInfo.get("props") match {
|
||||||
|
case Some(props: Map[String, _]) =>
|
||||||
|
val cleanedProps = props.map {
|
||||||
|
case ("end", endProps: Map[String, _]) => "end" -> cleanEndOrStartProps(endProps)
|
||||||
|
case ("start", startProps: Map[String, _]) => "start" -> cleanEndOrStartProps(startProps)
|
||||||
|
case other => other
|
||||||
|
}
|
||||||
|
annotationInfo + ("props" -> cleanedProps)
|
||||||
|
case _ => annotationInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def cleanEndOrStartProps(props: Map[String, _]): Map[String, _] = {
|
||||||
|
props.get("type") match {
|
||||||
|
case Some("binding") => props - ("x", "y") // Remove 'x' and 'y' for 'binding' type
|
||||||
|
case Some("point") => props - ("boundShapeId", "normalizedAnchor", "isExact") // Remove unwanted properties for 'point' type
|
||||||
|
case _ => props
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def getHistory(wbId: String): Array[AnnotationVO] = {
|
def getHistory(wbId: String): Array[AnnotationVO] = {
|
||||||
val wb = getWhiteboard(wbId)
|
val wb = getWhiteboard(wbId)
|
||||||
wb.annotationsMap.values.toArray
|
wb.annotationsMap.values.toArray
|
||||||
}
|
}
|
||||||
|
|
||||||
def deleteAnnotations(wbId: String, userId: String, annotationsIds: Array[String], isPresenter: Boolean, isModerator: Boolean): Array[String] = {
|
def deleteAnnotations(wbId: String, userId: String, annotationsIds: Array[String], isPresenter: Boolean, isModerator: Boolean): Array[String] = {
|
||||||
var annotationsIdsRemoved = Array[String]()
|
|
||||||
val wb = getWhiteboard(wbId)
|
val wb = getWhiteboard(wbId)
|
||||||
|
|
||||||
|
var annotationsIdsRemoved = Array[String]()
|
||||||
var newAnnotationsMap = wb.annotationsMap
|
var newAnnotationsMap = wb.annotationsMap
|
||||||
|
|
||||||
for (annotationId <- annotationsIds) {
|
for (annotationId <- annotationsIds) {
|
||||||
val annotation = wb.annotationsMap.get(annotationId)
|
val annotation = wb.annotationsMap.get(annotationId)
|
||||||
|
|
||||||
if (!annotation.isEmpty) {
|
if (annotation.isDefined) {
|
||||||
val hasPermission = isPresenter || isModerator || annotation.get.userId == userId
|
val hasPermission = isPresenter || isModerator || annotation.get.userId == userId
|
||||||
if (hasPermission) {
|
if (hasPermission) {
|
||||||
newAnnotationsMap -= annotationId
|
newAnnotationsMap -= annotationId
|
||||||
println("Removing annotation on page [" + wb.id + "]. After numAnnotations=[" + newAnnotationsMap.size + "].")
|
println(s"Removed annotation $annotationId on page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
|
||||||
annotationsIdsRemoved :+= annotationId
|
annotationsIdsRemoved :+= annotationId
|
||||||
} else {
|
} else {
|
||||||
println("User doesn't have permission to remove this annotation, ignoring...")
|
println(s"User $userId doesn't have permission to remove annotation $annotationId, ignoring...")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println(s"Annotation $annotationId not found while trying to delete it.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
val newWb = wb.copy(annotationsMap = newAnnotationsMap)
|
// Update whiteboard and save
|
||||||
saveWhiteboard(newWb)
|
val updatedWb = wb.copy(annotationsMap = newAnnotationsMap)
|
||||||
|
saveWhiteboard(updatedWb)
|
||||||
|
|
||||||
annotationsIdsRemoved.map(PresAnnotationDAO.delete(wbId, userId, _))
|
annotationsIdsRemoved.map(PresAnnotationDAO.delete(wbId, userId, _))
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import org.apache.pekko.actor.ActorContext
|
|||||||
|
|
||||||
class AudioCaptionsApp2x(implicit val context: ActorContext)
|
class AudioCaptionsApp2x(implicit val context: ActorContext)
|
||||||
extends UpdateTranscriptPubMsgHdlr
|
extends UpdateTranscriptPubMsgHdlr
|
||||||
|
with TranscriptionProviderErrorMsgHdlr
|
||||||
with AudioFloorChangedVoiceConfEvtMsgHdlr {
|
with AudioFloorChangedVoiceConfEvtMsgHdlr {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package org.bigbluebutton.core.apps.audiocaptions
|
||||||
|
|
||||||
|
import org.bigbluebutton.common2.msgs._
|
||||||
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
|
import org.bigbluebutton.core.db.UserTranscriptionErrorDAO
|
||||||
|
import org.bigbluebutton.core.models.AudioCaptions
|
||||||
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
|
trait TranscriptionProviderErrorMsgHdlr {
|
||||||
|
this: AudioCaptionsApp2x =>
|
||||||
|
|
||||||
|
def handleTranscriptionProviderErrorMsg(msg: TranscriptionProviderErrorMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||||
|
val meetingId = liveMeeting.props.meetingProp.intId
|
||||||
|
|
||||||
|
def broadcastEvent(userId: String, errorCode: String, errorMessage: String): Unit = {
|
||||||
|
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, "nodeJSapp")
|
||||||
|
val envelope = BbbCoreEnvelope(TranscriptionProviderErrorEvtMsg.NAME, routing)
|
||||||
|
val header = BbbClientMsgHeader(TranscriptionProviderErrorEvtMsg.NAME, meetingId, userId)
|
||||||
|
val body = TranscriptionProviderErrorEvtMsgBody(errorCode, errorMessage)
|
||||||
|
val event = TranscriptionProviderErrorEvtMsg(header, body)
|
||||||
|
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||||
|
|
||||||
|
bus.outGW.send(msgEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastEvent(msg.header.userId, msg.body.errorCode, msg.body.errorMessage)
|
||||||
|
|
||||||
|
UserTranscriptionErrorDAO.insert(msg.header.userId, msg.header.meetingId, msg.body.errorCode, msg.body.errorMessage)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,13 @@
|
|||||||
package org.bigbluebutton.core.apps.audiocaptions
|
package org.bigbluebutton.core.apps.audiocaptions
|
||||||
|
|
||||||
|
import org.bigbluebutton.ClientSettings.getConfigPropertyValueByPathAsStringOrElse
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.CaptionDAO
|
import org.bigbluebutton.core.db.CaptionDAO
|
||||||
import org.bigbluebutton.core.models.{AudioCaptions, Users2x}
|
import org.bigbluebutton.core.models.{AudioCaptions, UserState, Users2x}
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
import java.time.LocalDateTime
|
||||||
import java.sql.Timestamp
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
trait UpdateTranscriptPubMsgHdlr {
|
trait UpdateTranscriptPubMsgHdlr {
|
||||||
this: AudioCaptionsApp2x =>
|
this: AudioCaptionsApp2x =>
|
||||||
@ -25,6 +26,17 @@ trait UpdateTranscriptPubMsgHdlr {
|
|||||||
bus.outGW.send(msgEvent)
|
bus.outGW.send(msgEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def sendPadUpdatePubMsg(userId: String, defaultPad: String, text: String, transcript: Boolean): Unit = {
|
||||||
|
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, "nodeJSapp")
|
||||||
|
val envelope = BbbCoreEnvelope(PadUpdatePubMsg.NAME, routing)
|
||||||
|
val header = BbbClientMsgHeader(PadUpdatePubMsg.NAME, meetingId, userId)
|
||||||
|
val body = PadUpdatePubMsgBody(defaultPad, text, transcript)
|
||||||
|
val event = PadUpdatePubMsg(header, body)
|
||||||
|
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||||
|
|
||||||
|
bus.outGW.send(msgEvent)
|
||||||
|
}
|
||||||
|
|
||||||
// Adapt to the current captions' recording process
|
// Adapt to the current captions' recording process
|
||||||
def editTranscript(
|
def editTranscript(
|
||||||
userId: String,
|
userId: String,
|
||||||
@ -80,6 +92,28 @@ trait UpdateTranscriptPubMsgHdlr {
|
|||||||
msg.body.locale,
|
msg.body.locale,
|
||||||
msg.body.result,
|
msg.body.result,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(msg.body.result) {
|
||||||
|
val userName = Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId).get match {
|
||||||
|
case u: UserState => u.name
|
||||||
|
case _ => "???"
|
||||||
|
}
|
||||||
|
|
||||||
|
val now = LocalDateTime.now()
|
||||||
|
val formatter = DateTimeFormatter.ofPattern("HH:mm:ss")
|
||||||
|
val formattedTime = now.format(formatter)
|
||||||
|
|
||||||
|
val userSpoke = s"\n $userName ($formattedTime): $transcript"
|
||||||
|
|
||||||
|
val defaultPad = getConfigPropertyValueByPathAsStringOrElse(
|
||||||
|
liveMeeting.clientSettings,
|
||||||
|
"public.captions.defaultPad",
|
||||||
|
alternativeValue = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
sendPadUpdatePubMsg(msg.header.userId, defaultPad, userSpoke, transcript = true)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import org.bigbluebutton.core.running.MeetingActor
|
|||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
import scala.collection.SortedSet
|
import scala.collection.SortedSet
|
||||||
import org.apache.commons.codec.digest.DigestUtils
|
import org.apache.commons.codec.digest.DigestUtils
|
||||||
|
import org.bigbluebutton.SystemConfiguration
|
||||||
|
|
||||||
trait BreakoutApp2x extends BreakoutRoomCreatedMsgHdlr
|
trait BreakoutApp2x extends BreakoutRoomCreatedMsgHdlr
|
||||||
with BreakoutRoomsListMsgHdlr
|
with BreakoutRoomsListMsgHdlr
|
||||||
@ -26,7 +27,7 @@ trait BreakoutApp2x extends BreakoutRoomCreatedMsgHdlr
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object BreakoutRoomsUtil {
|
object BreakoutRoomsUtil extends SystemConfiguration {
|
||||||
def createMeetingIds(id: String, index: Int): (String, String) = {
|
def createMeetingIds(id: String, index: Int): (String, String) = {
|
||||||
val timeStamp = System.currentTimeMillis()
|
val timeStamp = System.currentTimeMillis()
|
||||||
val externalHash = DigestUtils.sha1Hex(id.concat("-").concat(timeStamp.toString()).concat("-").concat(index.toString()))
|
val externalHash = DigestUtils.sha1Hex(id.concat("-").concat(timeStamp.toString()).concat("-").concat(index.toString()))
|
||||||
@ -48,7 +49,13 @@ object BreakoutRoomsUtil {
|
|||||||
//checksum() -- Return a checksum based on SHA-1 digest
|
//checksum() -- Return a checksum based on SHA-1 digest
|
||||||
//
|
//
|
||||||
def checksum(s: String): String = {
|
def checksum(s: String): String = {
|
||||||
DigestUtils.sha256Hex(s);
|
checkSumAlgorithmForBreakouts match {
|
||||||
|
case "sha1" => DigestUtils.sha1Hex(s);
|
||||||
|
case "sha256" => DigestUtils.sha256Hex(s);
|
||||||
|
case "sha384" => DigestUtils.sha384Hex(s);
|
||||||
|
case "sha512" => DigestUtils.sha512Hex(s);
|
||||||
|
case _ => DigestUtils.sha256Hex(s); // default
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def calculateChecksum(apiCall: String, baseString: String, sharedSecret: String): String = {
|
def calculateChecksum(apiCall: String, baseString: String, sharedSecret: String): String = {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.bigbluebutton.core.apps.breakout
|
package org.bigbluebutton.core.apps.breakout
|
||||||
|
|
||||||
|
import org.bigbluebutton.ClientSettings.{getConfigPropertyValueByPath, getConfigPropertyValueByPathAsIntOrElse}
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.apps.{BreakoutModel, PermissionCheck, RightsManagementTrait}
|
import org.bigbluebutton.core.apps.{BreakoutModel, PermissionCheck, RightsManagementTrait}
|
||||||
import org.bigbluebutton.core.db.BreakoutRoomDAO
|
import org.bigbluebutton.core.db.BreakoutRoomDAO
|
||||||
@ -16,6 +17,10 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
|||||||
|
|
||||||
def handleCreateBreakoutRoomsCmdMsg(msg: CreateBreakoutRoomsCmdMsg, state: MeetingState2x): MeetingState2x = {
|
def handleCreateBreakoutRoomsCmdMsg(msg: CreateBreakoutRoomsCmdMsg, state: MeetingState2x): MeetingState2x = {
|
||||||
|
|
||||||
|
|
||||||
|
val minOfRooms = 2
|
||||||
|
val maxOfRooms = getConfigPropertyValueByPathAsIntOrElse(liveMeeting.clientSettings, "public.app.breakouts.breakoutRoomLimit", 16)
|
||||||
|
|
||||||
if (liveMeeting.props.meetingProp.disabledFeatures.contains("breakoutRooms")) {
|
if (liveMeeting.props.meetingProp.disabledFeatures.contains("breakoutRooms")) {
|
||||||
val meetingId = liveMeeting.props.meetingProp.intId
|
val meetingId = liveMeeting.props.meetingProp.intId
|
||||||
val reason = "Breakout rooms is disabled for this meeting."
|
val reason = "Breakout rooms is disabled for this meeting."
|
||||||
@ -27,6 +32,15 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
|||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId,
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId,
|
||||||
reason, outGW, liveMeeting)
|
reason, outGW, liveMeeting)
|
||||||
state
|
state
|
||||||
|
} else if(msg.body.rooms.length > maxOfRooms || msg.body.rooms.length < minOfRooms) {
|
||||||
|
log.warning(
|
||||||
|
"Attempt to create breakout rooms with invalid number of rooms (rooms: {}, max: {}, min: {}) in meeting {}",
|
||||||
|
msg.body.rooms.size,
|
||||||
|
maxOfRooms,
|
||||||
|
minOfRooms,
|
||||||
|
liveMeeting.props.meetingProp.intId
|
||||||
|
)
|
||||||
|
state
|
||||||
} else {
|
} else {
|
||||||
state.breakout match {
|
state.breakout match {
|
||||||
case Some(breakout) =>
|
case Some(breakout) =>
|
||||||
|
@ -30,7 +30,7 @@ trait EjectUserFromBreakoutInternalMsgHdlr {
|
|||||||
)
|
)
|
||||||
|
|
||||||
//TODO inform reason
|
//TODO inform reason
|
||||||
UserDAO.delete(registeredUser.id)
|
UserDAO.softDelete(registeredUser.id)
|
||||||
|
|
||||||
// send a system message to force disconnection
|
// send a system message to force disconnection
|
||||||
Sender.sendDisconnectClientSysMsg(msg.breakoutId, registeredUser.id, msg.ejectedBy, msg.reasonCode, outGW)
|
Sender.sendDisconnectClientSysMsg(msg.breakoutId, registeredUser.id, msg.ejectedBy, msg.reasonCode, outGW)
|
||||||
|
@ -18,8 +18,8 @@ trait SendMessageToBreakoutRoomInternalMsgHdlr {
|
|||||||
sender <- GroupChatApp.findGroupChatUser(SystemUser.ID, liveMeeting.users2x)
|
sender <- GroupChatApp.findGroupChatUser(SystemUser.ID, liveMeeting.users2x)
|
||||||
chat <- state.groupChats.find(GroupChatApp.MAIN_PUBLIC_CHAT)
|
chat <- state.groupChats.find(GroupChatApp.MAIN_PUBLIC_CHAT)
|
||||||
} yield {
|
} yield {
|
||||||
val groupChatMsgFromUser = GroupChatMsgFromUser(sender.id, sender.copy(name = msg.senderName), true, msg.msg)
|
val groupChatMsgFromUser = GroupChatMsgFromUser(sender.id, sender.copy(name = msg.senderName), msg.msg)
|
||||||
val gcm = GroupChatApp.toGroupChatMessage(sender.copy(name = msg.senderName), groupChatMsgFromUser)
|
val gcm = GroupChatApp.toGroupChatMessage(sender.copy(name = msg.senderName), groupChatMsgFromUser, emphasizedText = true)
|
||||||
val gcs = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm, GroupChatMessageType.BREAKOUTROOM_MOD_MSG)
|
val gcs = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm, GroupChatMessageType.BREAKOUTROOM_MOD_MSG)
|
||||||
|
|
||||||
val event = buildGroupChatMessageBroadcastEvtMsg(
|
val event = buildGroupChatMessageBroadcastEvtMsg(
|
||||||
|
@ -59,7 +59,7 @@ trait CreateGroupChatReqMsgHdlr extends SystemConfiguration {
|
|||||||
val newState = for {
|
val newState = for {
|
||||||
createdBy <- GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x)
|
createdBy <- GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x)
|
||||||
} yield {
|
} yield {
|
||||||
val msgs = msg.body.msg.map(m => GroupChatApp.toGroupChatMessage(createdBy, m))
|
val msgs = msg.body.msg.map(m => GroupChatApp.toGroupChatMessage(createdBy, m, emphasizedText = false))
|
||||||
val users = {
|
val users = {
|
||||||
if (msg.body.access == GroupChatAccess.PRIVATE) {
|
if (msg.body.access == GroupChatAccess.PRIVATE) {
|
||||||
val cu = msg.body.users.toSet + msg.header.userId
|
val cu = msg.body.users.toSet + msg.header.userId
|
||||||
|
@ -20,10 +20,10 @@ object GroupChatApp {
|
|||||||
GroupChatFactory.create(gcId, access, createBy, users, msgs)
|
GroupChatFactory.create(gcId, access, createBy, users, msgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
def toGroupChatMessage(sender: GroupChatUser, msg: GroupChatMsgFromUser): GroupChatMessage = {
|
def toGroupChatMessage(sender: GroupChatUser, msg: GroupChatMsgFromUser, emphasizedText: Boolean): GroupChatMessage = {
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
val id = GroupChatFactory.genId()
|
val id = GroupChatFactory.genId()
|
||||||
GroupChatMessage(id, now, msg.correlationId, now, now, sender, msg.chatEmphasizedText, msg.message)
|
GroupChatMessage(id, now, msg.correlationId, now, now, sender, emphasizedText, msg.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
def toMessageToUser(msg: GroupChatMessage): GroupChatMsgToUser = {
|
def toMessageToUser(msg: GroupChatMessage): GroupChatMsgToUser = {
|
||||||
@ -80,8 +80,8 @@ object GroupChatApp {
|
|||||||
sender <- GroupChatApp.findGroupChatUser(userId, liveMeeting.users2x)
|
sender <- GroupChatApp.findGroupChatUser(userId, liveMeeting.users2x)
|
||||||
chat <- state.groupChats.find(chatId)
|
chat <- state.groupChats.find(chatId)
|
||||||
} yield {
|
} yield {
|
||||||
|
val emphasizedText = sender.role == Roles.MODERATOR_ROLE
|
||||||
val gcm1 = GroupChatApp.toGroupChatMessage(sender, msg)
|
val gcm1 = GroupChatApp.toGroupChatMessage(sender, msg, emphasizedText)
|
||||||
val gcs1 = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm1)
|
val gcs1 = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm1)
|
||||||
state.update(gcs1)
|
state.update(gcs1)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.bigbluebutton.core.apps.groupchats
|
package org.bigbluebutton.core.apps.groupchats
|
||||||
|
|
||||||
|
import org.bigbluebutton.ClientSettings.{ getConfigPropertyValueByPath, getConfigPropertyValueByPathAsBooleanOrElse }
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.apps.PermissionCheck
|
import org.bigbluebutton.core.apps.PermissionCheck
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
@ -48,7 +49,17 @@ trait SendGroupChatMessageMsgHdlr extends HandlerHelpers {
|
|||||||
val userIsAParticipant = chat.users.filter(u => u.id == sender.id).length > 0;
|
val userIsAParticipant = chat.users.filter(u => u.id == sender.id).length > 0;
|
||||||
|
|
||||||
if ((chatIsPrivate && userIsAParticipant) || !chatIsPrivate) {
|
if ((chatIsPrivate && userIsAParticipant) || !chatIsPrivate) {
|
||||||
val gcm = GroupChatApp.toGroupChatMessage(sender, msg.body.msg)
|
val moderatorChatEmphasizedEnabled = getConfigPropertyValueByPathAsBooleanOrElse(
|
||||||
|
liveMeeting.clientSettings,
|
||||||
|
"public.chat.moderatorChatEmphasized",
|
||||||
|
alternativeValue = true
|
||||||
|
)
|
||||||
|
|
||||||
|
val emphasizedText = moderatorChatEmphasizedEnabled &&
|
||||||
|
!chatIsPrivate &&
|
||||||
|
sender.role == Roles.MODERATOR_ROLE
|
||||||
|
|
||||||
|
val gcm = GroupChatApp.toGroupChatMessage(sender, msg.body.msg, emphasizedText)
|
||||||
val gcs = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm)
|
val gcs = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm)
|
||||||
|
|
||||||
val event = buildGroupChatMessageBroadcastEvtMsg(
|
val event = buildGroupChatMessageBroadcastEvtMsg(
|
||||||
|
@ -6,6 +6,7 @@ import org.bigbluebutton.core.running.OutMsgRouter
|
|||||||
import org.bigbluebutton.core2.MeetingStatus2x
|
import org.bigbluebutton.core2.MeetingStatus2x
|
||||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.db.LayoutDAO
|
import org.bigbluebutton.core.db.LayoutDAO
|
||||||
|
import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
|
||||||
|
|
||||||
trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
|
trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
|
||||||
this: LayoutApp2x =>
|
this: LayoutApp2x =>
|
||||||
@ -60,5 +61,18 @@ trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
|
|||||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||||
|
|
||||||
outGW.send(msgEvent)
|
outGW.send(msgEvent)
|
||||||
|
|
||||||
|
if (body.pushLayout) {
|
||||||
|
val notifyEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
|
||||||
|
fromUserId,
|
||||||
|
liveMeeting.props.meetingProp.intId,
|
||||||
|
"info",
|
||||||
|
"user",
|
||||||
|
"app.layoutUpdate.label",
|
||||||
|
"Notification to when the presenter changes size of cams",
|
||||||
|
Vector()
|
||||||
|
)
|
||||||
|
outGW.send(notifyEvent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ trait PadUpdatePubMsgHdlr {
|
|||||||
bus.outGW.send(msgEvent)
|
bus.outGW.send(msgEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Pads.hasAccess(liveMeeting, msg.body.externalId, msg.header.userId)) {
|
if (Pads.hasAccess(liveMeeting, msg.body.externalId, msg.header.userId) || msg.body.transcript == true) {
|
||||||
Pads.getGroup(liveMeeting.pads, msg.body.externalId) match {
|
Pads.getGroup(liveMeeting.pads, msg.body.externalId) match {
|
||||||
case Some(group) => broadcastEvent(group.groupId, msg.body.externalId, msg.body.text)
|
case Some(group) => broadcastEvent(group.groupId, msg.body.externalId, msg.body.text)
|
||||||
case _ =>
|
case _ =>
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
package org.bigbluebutton.core.apps.plugin
|
||||||
|
|
||||||
|
import org.bigbluebutton.ClientSettings
|
||||||
|
import org.bigbluebutton.common2.msgs.PluginDataChannelDeleteMessageMsg
|
||||||
|
import org.bigbluebutton.core.db.PluginDataChannelMessageDAO
|
||||||
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
|
import org.bigbluebutton.core.models.{ Roles, Users2x }
|
||||||
|
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
|
||||||
|
|
||||||
|
trait PluginDataChannelDeleteMessageMsgHdlr extends HandlerHelpers {
|
||||||
|
|
||||||
|
def handle(msg: PluginDataChannelDeleteMessageMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = {
|
||||||
|
val pluginsDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("plugins")
|
||||||
|
val meetingId = liveMeeting.props.meetingProp.intId
|
||||||
|
|
||||||
|
for {
|
||||||
|
_ <- if (!pluginsDisabled) Some(()) else None
|
||||||
|
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||||
|
} yield {
|
||||||
|
val pluginsConfig = ClientSettings.getPluginsFromConfig(ClientSettings.clientSettingsFromFile)
|
||||||
|
|
||||||
|
if (!pluginsConfig.contains(msg.body.pluginName)) {
|
||||||
|
println(s"Plugin '${msg.body.pluginName}' not found.")
|
||||||
|
} else if (!pluginsConfig(msg.body.pluginName).dataChannels.contains(msg.body.dataChannel)) {
|
||||||
|
println(s"Data channel '${msg.body.dataChannel}' not found in plugin '${msg.body.pluginName}'.")
|
||||||
|
} else {
|
||||||
|
val hasPermission = for {
|
||||||
|
deletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.dataChannel).deletePermission
|
||||||
|
} yield {
|
||||||
|
deletePermission.toLowerCase match {
|
||||||
|
case "all" => true
|
||||||
|
case "moderator" => user.role == Roles.MODERATOR_ROLE
|
||||||
|
case "presenter" => user.presenter
|
||||||
|
case "sender" => {
|
||||||
|
val senderUserId = PluginDataChannelMessageDAO.getMessageSender(
|
||||||
|
meetingId,
|
||||||
|
msg.body.pluginName,
|
||||||
|
msg.body.dataChannel,
|
||||||
|
msg.body.messageId
|
||||||
|
)
|
||||||
|
senderUserId == msg.header.userId
|
||||||
|
}
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasPermission.contains(true)) {
|
||||||
|
println(s"No permission to delete in plugin: '${msg.body.pluginName}', data channel: '${msg.body.dataChannel}'.")
|
||||||
|
} else {
|
||||||
|
PluginDataChannelMessageDAO.delete(
|
||||||
|
meetingId,
|
||||||
|
msg.body.pluginName,
|
||||||
|
msg.body.dataChannel,
|
||||||
|
msg.body.messageId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,9 +7,9 @@ import org.bigbluebutton.core.domain.MeetingState2x
|
|||||||
import org.bigbluebutton.core.models.{ Roles, Users2x }
|
import org.bigbluebutton.core.models.{ Roles, Users2x }
|
||||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
|
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
|
||||||
|
|
||||||
trait DispatchPluginDataChannelMessageMsgHdlr extends HandlerHelpers {
|
trait PluginDataChannelDispatchMessageMsgHdlr extends HandlerHelpers {
|
||||||
|
|
||||||
def handle(msg: DispatchPluginDataChannelMessageMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = {
|
def handle(msg: PluginDataChannelDispatchMessageMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = {
|
||||||
val pluginsDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("plugins")
|
val pluginsDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("plugins")
|
||||||
val meetingId = liveMeeting.props.meetingProp.intId
|
val meetingId = liveMeeting.props.meetingProp.intId
|
||||||
|
|
@ -0,0 +1,50 @@
|
|||||||
|
package org.bigbluebutton.core.apps.plugin
|
||||||
|
|
||||||
|
import org.bigbluebutton.ClientSettings
|
||||||
|
import org.bigbluebutton.common2.msgs.PluginDataChannelResetMsg
|
||||||
|
import org.bigbluebutton.core.db.PluginDataChannelMessageDAO
|
||||||
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
|
import org.bigbluebutton.core.models.{ Roles, Users2x }
|
||||||
|
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
|
||||||
|
|
||||||
|
trait PluginDataChannelResetMsgHdlr extends HandlerHelpers {
|
||||||
|
|
||||||
|
def handle(msg: PluginDataChannelResetMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = {
|
||||||
|
val pluginsDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("plugins")
|
||||||
|
val meetingId = liveMeeting.props.meetingProp.intId
|
||||||
|
|
||||||
|
for {
|
||||||
|
_ <- if (!pluginsDisabled) Some(()) else None
|
||||||
|
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||||
|
} yield {
|
||||||
|
val pluginsConfig = ClientSettings.getPluginsFromConfig(ClientSettings.clientSettingsFromFile)
|
||||||
|
|
||||||
|
if (!pluginsConfig.contains(msg.body.pluginName)) {
|
||||||
|
println(s"Plugin '${msg.body.pluginName}' not found.")
|
||||||
|
} else if (!pluginsConfig(msg.body.pluginName).dataChannels.contains(msg.body.dataChannel)) {
|
||||||
|
println(s"Data channel '${msg.body.dataChannel}' not found in plugin '${msg.body.pluginName}'.")
|
||||||
|
} else {
|
||||||
|
val hasPermission = for {
|
||||||
|
deletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.dataChannel).deletePermission
|
||||||
|
} yield {
|
||||||
|
deletePermission.toLowerCase match {
|
||||||
|
case "all" => true
|
||||||
|
case "moderator" => user.role == Roles.MODERATOR_ROLE
|
||||||
|
case "presenter" => user.presenter
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasPermission.contains(true)) {
|
||||||
|
println(s"No permission to delete (reset) in plugin: '${msg.body.pluginName}', data channel: '${msg.body.dataChannel}'.")
|
||||||
|
} else {
|
||||||
|
PluginDataChannelMessageDAO.reset(
|
||||||
|
meetingId,
|
||||||
|
msg.body.pluginName,
|
||||||
|
msg.body.dataChannel
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,9 @@ import org.apache.pekko.actor.ActorContext
|
|||||||
import org.apache.pekko.event.Logging
|
import org.apache.pekko.event.Logging
|
||||||
|
|
||||||
class PluginHdlrs(implicit val context: ActorContext)
|
class PluginHdlrs(implicit val context: ActorContext)
|
||||||
extends DispatchPluginDataChannelMessageMsgHdlr {
|
extends PluginDataChannelDispatchMessageMsgHdlr
|
||||||
|
with PluginDataChannelDeleteMessageMsgHdlr
|
||||||
|
with PluginDataChannelResetMsgHdlr {
|
||||||
|
|
||||||
val log = Logging(context.system, getClass)
|
val log = Logging(context.system, getClass)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
package org.bigbluebutton.core.apps.polls
|
||||||
|
|
||||||
|
import org.bigbluebutton.common2.domain.SimplePollResultOutVO
|
||||||
|
import org.bigbluebutton.common2.msgs.{ BbbClientMsgHeader, BbbCommonEnvCoreMsg, BbbCoreEnvelope, MessageTypes, PollUpdatedEvtMsg, PollUpdatedEvtMsgBody, Routing, UserRespondedToPollRecordMsg, UserRespondedToPollRecordMsgBody, UserRespondedToPollRespMsg, UserRespondedToPollRespMsgBody, UserRespondedToTypedPollRespMsg, UserRespondedToTypedPollRespMsgBody }
|
||||||
|
import org.bigbluebutton.core.running.OutMsgRouter
|
||||||
|
|
||||||
|
object PollHdlrHelpers {
|
||||||
|
|
||||||
|
def broadcastPollUpdatedEvent(outGW: OutMsgRouter, meetingId: String, userId: String, pollId: String, poll: SimplePollResultOutVO): Unit = {
|
||||||
|
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
|
||||||
|
val envelope = BbbCoreEnvelope(PollUpdatedEvtMsg.NAME, routing)
|
||||||
|
val header = BbbClientMsgHeader(PollUpdatedEvtMsg.NAME, meetingId, userId)
|
||||||
|
|
||||||
|
val body = PollUpdatedEvtMsgBody(pollId, poll)
|
||||||
|
val event = PollUpdatedEvtMsg(header, body)
|
||||||
|
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||||
|
outGW.send(msgEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
def broadcastUserRespondedToTypedPollRespMsg(outGW: OutMsgRouter, meetingId: String, userId: String,
|
||||||
|
pollId: String, answer: String, sendToId: String): Unit = {
|
||||||
|
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, sendToId)
|
||||||
|
val envelope = BbbCoreEnvelope(UserRespondedToTypedPollRespMsg.NAME, routing)
|
||||||
|
val header = BbbClientMsgHeader(UserRespondedToTypedPollRespMsg.NAME, meetingId, sendToId)
|
||||||
|
|
||||||
|
val body = UserRespondedToTypedPollRespMsgBody(pollId, userId, answer)
|
||||||
|
val event = UserRespondedToTypedPollRespMsg(header, body)
|
||||||
|
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||||
|
outGW.send(msgEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
def broadcastUserRespondedToPollRecordMsg(outGW: OutMsgRouter, meetingId: String, userId: String,
|
||||||
|
pollId: String, answerId: Int, answer: String, isSecret: Boolean): Unit = {
|
||||||
|
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
|
||||||
|
val envelope = BbbCoreEnvelope(UserRespondedToPollRecordMsg.NAME, routing)
|
||||||
|
val header = BbbClientMsgHeader(UserRespondedToPollRecordMsg.NAME, meetingId, userId)
|
||||||
|
|
||||||
|
val body = UserRespondedToPollRecordMsgBody(pollId, answerId, answer, isSecret)
|
||||||
|
val event = UserRespondedToPollRecordMsg(header, body)
|
||||||
|
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||||
|
outGW.send(msgEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
def broadcastUserRespondedToPollRespMsg(outGW: OutMsgRouter, meetingId: String, userId: String,
|
||||||
|
pollId: String, answerIds: Seq[Int], sendToId: String): Unit = {
|
||||||
|
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, sendToId)
|
||||||
|
val envelope = BbbCoreEnvelope(UserRespondedToPollRespMsg.NAME, routing)
|
||||||
|
val header = BbbClientMsgHeader(UserRespondedToPollRespMsg.NAME, meetingId, sendToId)
|
||||||
|
|
||||||
|
val body = UserRespondedToPollRespMsgBody(pollId, userId, answerIds)
|
||||||
|
val event = UserRespondedToPollRespMsg(header, body)
|
||||||
|
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||||
|
outGW.send(msgEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -4,7 +4,7 @@ import org.bigbluebutton.common2.domain.SimplePollResultOutVO
|
|||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.models.Polls
|
import org.bigbluebutton.core.models.Polls
|
||||||
import org.bigbluebutton.core.running.{ LiveMeeting }
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
import org.bigbluebutton.core.models.Users2x
|
import org.bigbluebutton.core.models.Users2x
|
||||||
|
|
||||||
trait RespondToPollReqMsgHdlr {
|
trait RespondToPollReqMsgHdlr {
|
||||||
@ -12,45 +12,12 @@ trait RespondToPollReqMsgHdlr {
|
|||||||
|
|
||||||
def handle(msg: RespondToPollReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
def handle(msg: RespondToPollReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||||
|
|
||||||
def broadcastPollUpdatedEvent(msg: RespondToPollReqMsg, pollId: String, poll: SimplePollResultOutVO): Unit = {
|
if (!Polls.hasUserAlreadyResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls)) {
|
||||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
|
||||||
val envelope = BbbCoreEnvelope(PollUpdatedEvtMsg.NAME, routing)
|
|
||||||
val header = BbbClientMsgHeader(PollUpdatedEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
|
||||||
|
|
||||||
val body = PollUpdatedEvtMsgBody(pollId, poll)
|
|
||||||
val event = PollUpdatedEvtMsg(header, body)
|
|
||||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
|
||||||
bus.outGW.send(msgEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
def broadcastUserRespondedToPollRecordMsg(msg: RespondToPollReqMsg, pollId: String, answerId: Int, answer: String, isSecret: Boolean): Unit = {
|
|
||||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
|
||||||
val envelope = BbbCoreEnvelope(UserRespondedToPollRecordMsg.NAME, routing)
|
|
||||||
val header = BbbClientMsgHeader(UserRespondedToPollRecordMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
|
||||||
|
|
||||||
val body = UserRespondedToPollRecordMsgBody(pollId, answerId, answer, isSecret)
|
|
||||||
val event = UserRespondedToPollRecordMsg(header, body)
|
|
||||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
|
||||||
bus.outGW.send(msgEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
def broadcastUserRespondedToPollRespMsg(msg: RespondToPollReqMsg, pollId: String, answerIds: Seq[Int], sendToId: String): Unit = {
|
|
||||||
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, sendToId)
|
|
||||||
val envelope = BbbCoreEnvelope(UserRespondedToPollRespMsg.NAME, routing)
|
|
||||||
val header = BbbClientMsgHeader(UserRespondedToPollRespMsg.NAME, liveMeeting.props.meetingProp.intId, sendToId)
|
|
||||||
|
|
||||||
val body = UserRespondedToPollRespMsgBody(pollId, msg.header.userId, answerIds)
|
|
||||||
val event = UserRespondedToPollRespMsg(header, body)
|
|
||||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
|
||||||
bus.outGW.send(msgEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Polls.checkUserResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls) == false) {
|
|
||||||
for {
|
for {
|
||||||
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToPollReqMsg(msg.header.userId, msg.body.pollId,
|
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToPollReqMsg(msg.header.userId, msg.body.pollId,
|
||||||
msg.body.questionId, msg.body.answerIds, liveMeeting)
|
msg.body.questionId, msg.body.answerIds, liveMeeting)
|
||||||
} yield {
|
} yield {
|
||||||
broadcastPollUpdatedEvent(msg, pollId, updatedPoll)
|
PollHdlrHelpers.broadcastPollUpdatedEvent(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, updatedPoll)
|
||||||
for {
|
for {
|
||||||
poll <- Polls.getPoll(pollId, liveMeeting.polls)
|
poll <- Polls.getPoll(pollId, liveMeeting.polls)
|
||||||
} yield {
|
} yield {
|
||||||
@ -58,14 +25,14 @@ trait RespondToPollReqMsgHdlr {
|
|||||||
answerId <- msg.body.answerIds
|
answerId <- msg.body.answerIds
|
||||||
} yield {
|
} yield {
|
||||||
val answerText = poll.questions(0).answers.get(answerId).key
|
val answerText = poll.questions(0).answers.get(answerId).key
|
||||||
broadcastUserRespondedToPollRecordMsg(msg, pollId, answerId, answerText, poll.isSecret)
|
PollHdlrHelpers.broadcastUserRespondedToPollRecordMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, answerId, answerText, poll.isSecret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
presenter <- Users2x.findPresenter(liveMeeting.users2x)
|
presenter <- Users2x.findPresenter(liveMeeting.users2x)
|
||||||
} yield {
|
} yield {
|
||||||
broadcastUserRespondedToPollRespMsg(msg, pollId, msg.body.answerIds, presenter.intId)
|
PollHdlrHelpers.broadcastUserRespondedToPollRespMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, msg.body.answerIds, presenter.intId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package org.bigbluebutton.core.apps.polls
|
package org.bigbluebutton.core.apps.polls
|
||||||
|
|
||||||
|
import org.bigbluebutton.ClientSettings.getConfigPropertyValueByPathAsIntOrElse
|
||||||
import org.bigbluebutton.common2.domain.SimplePollResultOutVO
|
import org.bigbluebutton.common2.domain.SimplePollResultOutVO
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.models.Polls
|
import org.bigbluebutton.core.models.Polls
|
||||||
import org.bigbluebutton.core.running.{ LiveMeeting }
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
import org.bigbluebutton.core.models.Users2x
|
import org.bigbluebutton.core.models.Users2x
|
||||||
|
|
||||||
trait RespondToTypedPollReqMsgHdlr {
|
trait RespondToTypedPollReqMsgHdlr {
|
||||||
@ -12,43 +13,60 @@ trait RespondToTypedPollReqMsgHdlr {
|
|||||||
|
|
||||||
def handle(msg: RespondToTypedPollReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
def handle(msg: RespondToTypedPollReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||||
|
|
||||||
def broadcastPollUpdatedEvent(msg: RespondToTypedPollReqMsg, pollId: String, poll: SimplePollResultOutVO): Unit = {
|
|
||||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
|
||||||
val envelope = BbbCoreEnvelope(PollUpdatedEvtMsg.NAME, routing)
|
|
||||||
val header = BbbClientMsgHeader(PollUpdatedEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
|
||||||
|
|
||||||
val body = PollUpdatedEvtMsgBody(pollId, poll)
|
|
||||||
val event = PollUpdatedEvtMsg(header, body)
|
|
||||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
|
||||||
bus.outGW.send(msgEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
def broadcastUserRespondedToTypedPollRespMsg(msg: RespondToTypedPollReqMsg, pollId: String, answer: String, sendToId: String): Unit = {
|
|
||||||
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, sendToId)
|
|
||||||
val envelope = BbbCoreEnvelope(UserRespondedToTypedPollRespMsg.NAME, routing)
|
|
||||||
val header = BbbClientMsgHeader(UserRespondedToTypedPollRespMsg.NAME, liveMeeting.props.meetingProp.intId, sendToId)
|
|
||||||
|
|
||||||
val body = UserRespondedToTypedPollRespMsgBody(pollId, msg.header.userId, answer)
|
|
||||||
val event = UserRespondedToTypedPollRespMsg(header, body)
|
|
||||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
|
||||||
bus.outGW.send(msgEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Polls.isResponsePollType(msg.body.pollId, liveMeeting.polls) &&
|
if (Polls.isResponsePollType(msg.body.pollId, liveMeeting.polls) &&
|
||||||
Polls.checkUserResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls) == false &&
|
!Polls.hasUserAlreadyResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls) &&
|
||||||
Polls.checkUserAddedQuestion(msg.body.pollId, msg.header.userId, liveMeeting.polls) == false) {
|
!Polls.hasUserAlreadyAddedTypedAnswer(msg.body.pollId, msg.header.userId, liveMeeting.polls)) {
|
||||||
|
|
||||||
|
//Truncate answer case it is longer than `maxTypedAnswerLength`
|
||||||
|
val maxTypedAnswerLength = getConfigPropertyValueByPathAsIntOrElse(liveMeeting.clientSettings, "public.poll.maxTypedAnswerLength", 45)
|
||||||
|
val answer = msg.body.answer.substring(0, Math.min(msg.body.answer.length, maxTypedAnswerLength))
|
||||||
|
|
||||||
|
val answerExists = Polls.findAnswerWithText(msg.body.pollId, msg.body.questionId, answer, liveMeeting.polls)
|
||||||
|
|
||||||
|
//Create answer if it doesn't exist
|
||||||
|
answerExists match {
|
||||||
|
case None => {
|
||||||
for {
|
for {
|
||||||
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToTypedPollReqMsg(msg.header.userId, msg.body.pollId,
|
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToTypedPollReqMsg(msg.header.userId, msg.body.pollId,
|
||||||
msg.body.questionId, msg.body.answer, liveMeeting)
|
msg.body.questionId, answer, liveMeeting)
|
||||||
} yield {
|
} yield {
|
||||||
broadcastPollUpdatedEvent(msg, pollId, updatedPoll)
|
PollHdlrHelpers.broadcastPollUpdatedEvent(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, updatedPoll)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
presenter <- Users2x.findPresenter(liveMeeting.users2x)
|
presenter <- Users2x.findPresenter(liveMeeting.users2x)
|
||||||
} yield {
|
} yield {
|
||||||
broadcastUserRespondedToTypedPollRespMsg(msg, pollId, msg.body.answer, presenter.intId)
|
PollHdlrHelpers.broadcastUserRespondedToTypedPollRespMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, answer, presenter.intId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
case _ => //Do nothing, answer with same text exists already
|
||||||
|
}
|
||||||
|
|
||||||
|
//Submit the answer
|
||||||
|
Polls.findAnswerWithText(msg.body.pollId, msg.body.questionId, answer, liveMeeting.polls) match {
|
||||||
|
case Some(answerId) => {
|
||||||
|
for {
|
||||||
|
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToPollReqMsg(msg.header.userId, msg.body.pollId,
|
||||||
|
msg.body.questionId, Seq(answerId), liveMeeting)
|
||||||
|
} yield {
|
||||||
|
PollHdlrHelpers.broadcastPollUpdatedEvent(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, updatedPoll)
|
||||||
|
for {
|
||||||
|
poll <- Polls.getPoll(pollId, liveMeeting.polls)
|
||||||
|
} yield {
|
||||||
|
val answerText = poll.questions(0).answers.get(answerId).key
|
||||||
|
PollHdlrHelpers.broadcastUserRespondedToPollRecordMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, answerId, answerText, poll.isSecret)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
presenter <- Users2x.findPresenter(liveMeeting.users2x)
|
||||||
|
} yield {
|
||||||
|
PollHdlrHelpers.broadcastUserRespondedToPollRespMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, Seq(answerId), presenter.intId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case None => log.error("Error while trying to answer the poll {} in meeting {}: Answer not found or something went wrong while trying to create the answer.", msg.body.pollId, msg.header.meetingId)
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log.info("Ignoring typed answer from user {} once user already added an answer to this poll {} in meeting {}", msg.header.userId, msg.body.pollId, msg.header.meetingId)
|
log.info("Ignoring typed answer from user {} once user already added an answer to this poll {} in meeting {}", msg.header.userId, msg.body.pollId, msg.header.meetingId)
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
def buildNewPresFileAvailable(annotatedFileURI: String, originalFileURI: String, convertedFileURI: String,
|
def buildNewPresFileAvailable(annotatedFileURI: String, originalFileURI: String, convertedFileURI: String,
|
||||||
presId: String, fileStateType: String): NewPresFileAvailableMsg = {
|
presId: String, fileStateType: String): NewPresFileAvailableMsg = {
|
||||||
val header = BbbClientMsgHeader(NewPresFileAvailableMsg.NAME, "not-used", "not-used")
|
val header = BbbClientMsgHeader(NewPresFileAvailableMsg.NAME, "not-used", "not-used")
|
||||||
val body = NewPresFileAvailableMsgBody(annotatedFileURI, originalFileURI, convertedFileURI, presId, fileStateType)
|
val body = NewPresFileAvailableMsgBody(annotatedFileURI, originalFileURI, convertedFileURI, presId, fileStateType, "")
|
||||||
|
|
||||||
NewPresFileAvailableMsg(header, body)
|
NewPresFileAvailableMsg(header, body)
|
||||||
}
|
}
|
||||||
@ -160,7 +160,7 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
val pages: List[Int] = m.body.pages // Desired presentation pages for export
|
val pages: List[Int] = m.body.pages // Desired presentation pages for export
|
||||||
val pagesRange: List[Int] = if (allPages) (1 to pageCount).toList else pages
|
val pagesRange: List[Int] = if (allPages) (1 to pageCount).toList else pages
|
||||||
|
|
||||||
val exportJob: ExportJob = new ExportJob(jobId, JobTypes.DOWNLOAD, "annotated_slides", presId, presLocation, allPages, pagesRange, meetingId, "");
|
val exportJob: ExportJob = new ExportJob(jobId, JobTypes.DOWNLOAD, currentPres.get.name, "annotated_slides", presId, presLocation, allPages, pagesRange, meetingId, "");
|
||||||
val storeAnnotationPages: List[PresentationPageForExport] = getPresentationPagesForExport(pagesRange, pageCount, presId, currentPres, liveMeeting);
|
val storeAnnotationPages: List[PresentationPageForExport] = getPresentationPagesForExport(pagesRange, pageCount, presId, currentPres, liveMeeting);
|
||||||
|
|
||||||
val isPresentationOriginalOrConverted = m.body.fileStateType == "Original" || m.body.fileStateType == "Converted"
|
val isPresentationOriginalOrConverted = m.body.fileStateType == "Original" || m.body.fileStateType == "Converted"
|
||||||
@ -226,7 +226,7 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
val currentPage: PresentationPage = PresentationInPod.getCurrentPage(currentPres.get).get
|
val currentPage: PresentationPage = PresentationInPod.getCurrentPage(currentPres.get).get
|
||||||
val pagesRange: List[Int] = if (allPages) (1 to pageCount).toList else List(currentPage.num)
|
val pagesRange: List[Int] = if (allPages) (1 to pageCount).toList else List(currentPage.num)
|
||||||
|
|
||||||
val exportJob: ExportJob = ExportJob(jobId, JobTypes.CAPTURE_PRESENTATION, filename, presId, presLocation, allPages, pagesRange, parentMeetingId, presentationUploadToken)
|
val exportJob: ExportJob = ExportJob(jobId, JobTypes.CAPTURE_PRESENTATION, filename, filename, presId, presLocation, allPages, pagesRange, parentMeetingId, presentationUploadToken)
|
||||||
val storeAnnotationPages: List[PresentationPageForExport] = getPresentationPagesForExport(pagesRange, pageCount, presId, currentPres, liveMeeting);
|
val storeAnnotationPages: List[PresentationPageForExport] = getPresentationPagesForExport(pagesRange, pageCount, presId, currentPres, liveMeeting);
|
||||||
|
|
||||||
val annotationCount: Int = storeAnnotationPages.map(_.annotations.size).sum
|
val annotationCount: Int = storeAnnotationPages.map(_.annotations.size).sum
|
||||||
@ -252,11 +252,10 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
liveMeeting.props.meetingProp.intId, m.body.presId
|
liveMeeting.props.meetingProp.intId, m.body.presId
|
||||||
)
|
)
|
||||||
|
|
||||||
//TODO let frontend choose the name in favor of internationalization
|
|
||||||
if (m.body.fileStateType == "Annotated") {
|
if (m.body.fileStateType == "Annotated") {
|
||||||
val presentationDownloadInfo = Map(
|
val presentationDownloadInfo = Map(
|
||||||
"fileURI" -> m.body.annotatedFileURI,
|
"fileURI" -> m.body.annotatedFileURI,
|
||||||
"filename" -> "annotated_slides.pdf"
|
"filename" -> m.body.fileName
|
||||||
)
|
)
|
||||||
ChatMessageDAO.insertSystemMsg(liveMeeting.props.meetingProp.intId, GroupChatApp.MAIN_PUBLIC_CHAT, "", GroupChatMessageType.PRESENTATION, presentationDownloadInfo, "")
|
ChatMessageDAO.insertSystemMsg(liveMeeting.props.meetingProp.intId, GroupChatApp.MAIN_PUBLIC_CHAT, "", GroupChatMessageType.PRESENTATION, presentationDownloadInfo, "")
|
||||||
} else if (m.body.fileStateType == "Converted") {
|
} else if (m.body.fileStateType == "Converted") {
|
||||||
@ -295,7 +294,7 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
|
|
||||||
bus.outGW.send(buildPresentationUploadTokenSysPubMsg(m.body.parentMeetingId, userId, presentationUploadToken, filename, presentationId))
|
bus.outGW.send(buildPresentationUploadTokenSysPubMsg(m.body.parentMeetingId, userId, presentationUploadToken, filename, presentationId))
|
||||||
|
|
||||||
val exportJob = new ExportJob(jobId, JobTypes.CAPTURE_NOTES, filename, m.body.padId, "", true, List(), m.body.parentMeetingId, presentationUploadToken)
|
val exportJob = new ExportJob(jobId, JobTypes.CAPTURE_NOTES, filename, filename, m.body.padId, "", true, List(), m.body.parentMeetingId, presentationUploadToken)
|
||||||
val job = buildStoreExportJobInRedisSysMsg(exportJob, liveMeeting)
|
val job = buildStoreExportJobInRedisSysMsg(exportJob, liveMeeting)
|
||||||
|
|
||||||
bus.outGW.send(job)
|
bus.outGW.send(job)
|
||||||
|
@ -30,7 +30,10 @@ trait DeactivateTimerReqMsgHdlr extends RightsManagementTrait {
|
|||||||
val reason = "You need to be the presenter or moderator to deactivate timer"
|
val reason = "You need to be the presenter or moderator to deactivate timer"
|
||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||||
} else {
|
} else {
|
||||||
TimerModel.setIsActive(liveMeeting.timerModel, false)
|
TimerModel.setRunning(liveMeeting.timerModel, running = false)
|
||||||
|
TimerModel.setIsActive(liveMeeting.timerModel, active = false)
|
||||||
|
TimerModel.setStopwatch(liveMeeting.timerModel, stopwatch = true)
|
||||||
|
TimerModel.reset(liveMeeting.timerModel)
|
||||||
TimerDAO.update(liveMeeting.props.meetingProp.intId, liveMeeting.timerModel)
|
TimerDAO.update(liveMeeting.props.meetingProp.intId, liveMeeting.timerModel)
|
||||||
broadcastEvent()
|
broadcastEvent()
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ trait StartTimerReqMsgHdlr extends RightsManagementTrait {
|
|||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||||
} else {
|
} else {
|
||||||
TimerModel.setStartedAt(liveMeeting.timerModel, System.currentTimeMillis())
|
TimerModel.setStartedAt(liveMeeting.timerModel, System.currentTimeMillis())
|
||||||
TimerModel.setRunning(liveMeeting.timerModel, true)
|
TimerModel.setRunning(liveMeeting.timerModel, running = true)
|
||||||
TimerDAO.update(liveMeeting.props.meetingProp.intId, liveMeeting.timerModel)
|
TimerDAO.update(liveMeeting.props.meetingProp.intId, liveMeeting.timerModel)
|
||||||
broadcastEvent()
|
broadcastEvent()
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,9 @@ trait StopTimerReqMsgHdlr extends RightsManagementTrait {
|
|||||||
val reason = "You need to be the presenter or moderator to stop timer"
|
val reason = "You need to be the presenter or moderator to stop timer"
|
||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||||
} else {
|
} else {
|
||||||
TimerModel.setAccumulated(liveMeeting.timerModel, msg.body.accumulated)
|
TimerModel.setRunning(liveMeeting.timerModel, running = false)
|
||||||
TimerModel.setRunning(liveMeeting.timerModel, false)
|
|
||||||
TimerDAO.update(liveMeeting.props.meetingProp.intId, liveMeeting.timerModel)
|
TimerDAO.update(liveMeeting.props.meetingProp.intId, liveMeeting.timerModel)
|
||||||
broadcastEvent(msg.body.accumulated)
|
broadcastEvent(TimerModel.getAccumulated(liveMeeting.timerModel))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ trait SwitchTimerReqMsgHdlr extends RightsManagementTrait {
|
|||||||
} else {
|
} else {
|
||||||
if (TimerModel.getStopwatch(liveMeeting.timerModel) != msg.body.stopwatch) {
|
if (TimerModel.getStopwatch(liveMeeting.timerModel) != msg.body.stopwatch) {
|
||||||
TimerModel.setStopwatch(liveMeeting.timerModel, msg.body.stopwatch)
|
TimerModel.setStopwatch(liveMeeting.timerModel, msg.body.stopwatch)
|
||||||
|
TimerModel.setRunning(liveMeeting.timerModel, running = false)
|
||||||
TimerModel.reset(liveMeeting.timerModel) //Reset on switch Stopwatch/Timer
|
TimerModel.reset(liveMeeting.timerModel) //Reset on switch Stopwatch/Timer
|
||||||
if (msg.body.stopwatch) {
|
if (msg.body.stopwatch) {
|
||||||
TimerModel.setTrack(liveMeeting.timerModel, "noTrack")
|
TimerModel.setTrack(liveMeeting.timerModel, "noTrack")
|
||||||
|
@ -9,7 +9,7 @@ import org.bigbluebutton.core.running.OutMsgRouter
|
|||||||
import org.bigbluebutton.core.running.MeetingActor
|
import org.bigbluebutton.core.running.MeetingActor
|
||||||
import org.bigbluebutton.core2.MeetingStatus2x
|
import org.bigbluebutton.core2.MeetingStatus2x
|
||||||
import org.bigbluebutton.core2.Permissions
|
import org.bigbluebutton.core2.Permissions
|
||||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
|
||||||
|
|
||||||
trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
|
trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
|
||||||
this: MeetingActor =>
|
this: MeetingActor =>
|
||||||
@ -237,6 +237,16 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
|
|||||||
)
|
)
|
||||||
|
|
||||||
outGW.send(BbbCommonEnvCoreMsg(envelope, LockSettingsInMeetingChangedEvtMsg(header, body)))
|
outGW.send(BbbCommonEnvCoreMsg(envelope, LockSettingsInMeetingChangedEvtMsg(header, body)))
|
||||||
|
|
||||||
|
//Refresh graphql session for all locked viewers
|
||||||
|
for {
|
||||||
|
user <- Users2x.findAll(liveMeeting.users2x)
|
||||||
|
if user.locked
|
||||||
|
if user.role == Roles.VIEWER_ROLE
|
||||||
|
regUser <- RegisteredUsers.findWithUserId(user.intId, liveMeeting.registeredUsers)
|
||||||
|
} yield {
|
||||||
|
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, "lockSettings_changed", outGW)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package org.bigbluebutton.core.apps.users
|
package org.bigbluebutton.core.apps.users
|
||||||
|
|
||||||
import org.bigbluebutton.ClientSettings.getConfigPropertyValueByPath
|
import org.bigbluebutton.ClientSettings.{ getConfigPropertyValueByPath, getConfigPropertyValueByPathAsIntOrElse }
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.apps.RightsManagementTrait
|
import org.bigbluebutton.core.apps.RightsManagementTrait
|
||||||
import org.bigbluebutton.core.models.{ UserState, Users2x }
|
import org.bigbluebutton.core.models.{ UserState, Users2x }
|
||||||
@ -31,13 +31,7 @@ trait ChangeUserReactionEmojiReqMsgHdlr extends RightsManagementTrait {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Get durationInSeconds from Client config
|
//Get durationInSeconds from Client config
|
||||||
val userReactionExpire =
|
val userReactionExpire = getConfigPropertyValueByPathAsIntOrElse(liveMeeting.clientSettings, "public.userReaction.expire", 30)
|
||||||
getConfigPropertyValueByPath(liveMeeting.clientSettings, "public.userReaction.expire") match {
|
|
||||||
case Some(durationInSeconds: Int) => durationInSeconds
|
|
||||||
case _ =>
|
|
||||||
log.debug("Config `public.userReaction.expire` not found.")
|
|
||||||
30
|
|
||||||
}
|
|
||||||
for {
|
for {
|
||||||
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
|
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
|
||||||
newUserState <- Users2x.setReactionEmoji(liveMeeting.users2x, user.intId, msg.body.reactionEmoji, userReactionExpire)
|
newUserState <- Users2x.setReactionEmoji(liveMeeting.users2x, user.intId, msg.body.reactionEmoji, userReactionExpire)
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
package org.bigbluebutton.core.apps.users
|
|
||||||
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
|
||||||
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
|
||||||
import org.bigbluebutton.core.models.{ UserState, Users2x }
|
|
||||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
|
||||||
import org.bigbluebutton.core2.MeetingStatus2x
|
|
||||||
import org.bigbluebutton.SystemConfiguration
|
|
||||||
import scala.util.Random
|
|
||||||
|
|
||||||
trait SelectRandomViewerReqMsgHdlr extends RightsManagementTrait {
|
|
||||||
this: UsersApp =>
|
|
||||||
|
|
||||||
val outGW: OutMsgRouter
|
|
||||||
|
|
||||||
def handleSelectRandomViewerReqMsg(msg: SelectRandomViewerReqMsg): Unit = {
|
|
||||||
log.debug("Received SelectRandomViewerReqMsg {}", SelectRandomViewerReqMsg)
|
|
||||||
|
|
||||||
def broadcastEvent(msg: SelectRandomViewerReqMsg, users: Vector[String], choice: String): Unit = {
|
|
||||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
|
||||||
val envelope = BbbCoreEnvelope(SelectRandomViewerRespMsg.NAME, routing)
|
|
||||||
val header = BbbClientMsgHeader(SelectRandomViewerRespMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
|
||||||
|
|
||||||
val body = SelectRandomViewerRespMsgBody(msg.header.userId, users, choice)
|
|
||||||
val event = SelectRandomViewerRespMsg(header, body)
|
|
||||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
|
||||||
outGW.send(msgEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (permissionFailed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
|
|
||||||
val meetingId = liveMeeting.props.meetingProp.intId
|
|
||||||
val reason = "No permission to select random user."
|
|
||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
|
||||||
} else {
|
|
||||||
val users = Users2x.getRandomlyPickableUsers(liveMeeting.users2x, false)
|
|
||||||
|
|
||||||
val usersPicked = Users2x.getRandomlyPickableUsers(liveMeeting.users2x, reduceDuplicatedPick)
|
|
||||||
|
|
||||||
val randNum = new scala.util.Random
|
|
||||||
var pickedUser = if (usersPicked.size == 0) "" else usersPicked(randNum.nextInt(usersPicked.size)).intId
|
|
||||||
|
|
||||||
if (reduceDuplicatedPick) {
|
|
||||||
if (usersPicked.size <= 1) {
|
|
||||||
// Initialise the exemption
|
|
||||||
val usersToUnexempt = Users2x.findAll(liveMeeting.users2x)
|
|
||||||
usersToUnexempt foreach { u =>
|
|
||||||
Users2x.setUserExempted(liveMeeting.users2x, u.intId, false)
|
|
||||||
}
|
|
||||||
if (usersPicked.size == 0) {
|
|
||||||
// Pick again
|
|
||||||
val usersRepicked = Users2x.getRandomlyPickableUsers(liveMeeting.users2x, reduceDuplicatedPick)
|
|
||||||
pickedUser = if (usersRepicked.size == 0) "" else usersRepicked(randNum.nextInt(usersRepicked.size)).intId
|
|
||||||
Users2x.setUserExempted(liveMeeting.users2x, pickedUser, true)
|
|
||||||
}
|
|
||||||
} else if (usersPicked.size > 1) {
|
|
||||||
Users2x.setUserExempted(liveMeeting.users2x, pickedUser, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val userIds = users.map { case (v) => v.intId }
|
|
||||||
broadcastEvent(msg, userIds, pickedUser)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,41 @@
|
|||||||
|
package org.bigbluebutton.core.apps.users
|
||||||
|
|
||||||
|
import org.bigbluebutton.common2.msgs._
|
||||||
|
import org.bigbluebutton.core.models.{ UserState, Users2x }
|
||||||
|
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||||
|
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
|
|
||||||
|
trait SetUserSpeechOptionsMsgHdlr extends RightsManagementTrait {
|
||||||
|
this: UsersApp =>
|
||||||
|
|
||||||
|
val liveMeeting: LiveMeeting
|
||||||
|
val outGW: OutMsgRouter
|
||||||
|
|
||||||
|
def handleSetUserSpeechOptionsReqMsg(msg: SetUserSpeechOptionsReqMsg): Unit = {
|
||||||
|
log.info("handleSetUserSpeechOptionsReqMsg: partialUtterances={} minUtteranceLength={} userId={}", msg.body.partialUtterances, msg.body.minUtteranceLength, msg.header.userId)
|
||||||
|
|
||||||
|
def broadcastUserSpeechOptionsChanged(user: UserState, partialUtterances: Boolean, minUtteranceLength: Int): Unit = {
|
||||||
|
val routingChange = Routing.addMsgToClientRouting(
|
||||||
|
MessageTypes.BROADCAST_TO_MEETING,
|
||||||
|
liveMeeting.props.meetingProp.intId, user.intId
|
||||||
|
)
|
||||||
|
val envelopeChange = BbbCoreEnvelope(UserSpeechOptionsChangedEvtMsg.NAME, routingChange)
|
||||||
|
val headerChange = BbbClientMsgHeader(UserSpeechOptionsChangedEvtMsg.NAME, liveMeeting.props.meetingProp.intId, user.intId)
|
||||||
|
|
||||||
|
val bodyChange = UserSpeechOptionsChangedEvtMsgBody(partialUtterances, minUtteranceLength)
|
||||||
|
val eventChange = UserSpeechOptionsChangedEvtMsg(headerChange, bodyChange)
|
||||||
|
val msgEventChange = BbbCommonEnvCoreMsg(envelopeChange, eventChange)
|
||||||
|
outGW.send(msgEventChange)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||||
|
} yield {
|
||||||
|
var changeLocale: Option[UserState] = None;
|
||||||
|
//changeLocale = Users2x.setUserSpeechLocale(liveMeeting.users2x, msg.header.userId, msg.body.locale)
|
||||||
|
broadcastUserSpeechOptionsChanged(user, msg.body.partialUtterances, msg.body.minUtteranceLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package org.bigbluebutton.core.apps.users
|
||||||
|
|
||||||
|
import org.bigbluebutton.common2.msgs._
|
||||||
|
import org.bigbluebutton.core.apps.RightsManagementTrait
|
||||||
|
import org.bigbluebutton.core.db.UserConnectionStatusDAO
|
||||||
|
import org.bigbluebutton.core.models.{ UserState, Users2x }
|
||||||
|
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||||
|
|
||||||
|
trait UserConnectionAliveReqMsgHdlr extends RightsManagementTrait {
|
||||||
|
this: UsersApp =>
|
||||||
|
|
||||||
|
val liveMeeting: LiveMeeting
|
||||||
|
val outGW: OutMsgRouter
|
||||||
|
|
||||||
|
def handleUserConnectionAliveReqMsg(msg: UserConnectionAliveReqMsg): Unit = {
|
||||||
|
log.info("handleUserConnectionAliveReqMsg: userId={}", msg.body.userId)
|
||||||
|
|
||||||
|
for {
|
||||||
|
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
|
||||||
|
} yield {
|
||||||
|
UserConnectionStatusDAO.updateUserAlive(user.intId)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package org.bigbluebutton.core.apps.users
|
||||||
|
|
||||||
|
import org.bigbluebutton.common2.msgs._
|
||||||
|
import org.bigbluebutton.core.apps.RightsManagementTrait
|
||||||
|
import org.bigbluebutton.core.db.UserConnectionStatusDAO
|
||||||
|
import org.bigbluebutton.core.models.{ UserState, Users2x }
|
||||||
|
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||||
|
|
||||||
|
trait UserConnectionUpdateRttReqMsgHdlr extends RightsManagementTrait {
|
||||||
|
this: UsersApp =>
|
||||||
|
|
||||||
|
val liveMeeting: LiveMeeting
|
||||||
|
val outGW: OutMsgRouter
|
||||||
|
|
||||||
|
def handleUserConnectionUpdateRttReqMsg(msg: UserConnectionUpdateRttReqMsg): Unit = {
|
||||||
|
log.info("handleUserConnectionUpdateRttReqMsg: networkRttInMs={} userId={}", msg.body.networkRttInMs, msg.body.userId)
|
||||||
|
|
||||||
|
for {
|
||||||
|
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
|
||||||
|
} yield {
|
||||||
|
UserConnectionStatusDAO.updateUserRtt(user.intId, msg.body.networkRttInMs)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package org.bigbluebutton.core.apps.users
|
||||||
|
|
||||||
|
import org.bigbluebutton.core.api.UserEstablishedGraphqlConnectionInternalMsg
|
||||||
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
|
import org.bigbluebutton.core.models.Users2x
|
||||||
|
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, MeetingActor, OutMsgRouter }
|
||||||
|
|
||||||
|
trait UserEstablishedGraphqlConnectionInternalMsgHdlr extends HandlerHelpers {
|
||||||
|
this: MeetingActor =>
|
||||||
|
|
||||||
|
val liveMeeting: LiveMeeting
|
||||||
|
val outGW: OutMsgRouter
|
||||||
|
|
||||||
|
def handleUserEstablishedGraphqlConnectionInternalMsg(msg: UserEstablishedGraphqlConnectionInternalMsg, state: MeetingState2x): MeetingState2x = {
|
||||||
|
log.info("Received user established a graphql connection. user {} meetingId={}", msg.userId, liveMeeting.props.meetingProp.intId)
|
||||||
|
Users2x.findWithIntId(liveMeeting.users2x, msg.userId) match {
|
||||||
|
case Some(reconnectingUser) =>
|
||||||
|
if (reconnectingUser.userLeftFlag.left) {
|
||||||
|
log.info("Resetting flag that user left meeting. user {}", msg.userId)
|
||||||
|
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.userId, leftFlag = false)
|
||||||
|
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.userId)
|
||||||
|
}
|
||||||
|
state
|
||||||
|
case None =>
|
||||||
|
state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,7 +20,7 @@ trait UserJoinMeetingAfterReconnectReqMsgHdlr extends HandlerHelpers with UserJo
|
|||||||
if (reconnectingUser.userLeftFlag.left) {
|
if (reconnectingUser.userLeftFlag.left) {
|
||||||
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
|
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
|
||||||
// User has reconnected. Just reset it's flag. ralam Oct 23, 2018
|
// 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, leftFlag = false)
|
||||||
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
|
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
|
||||||
}
|
}
|
||||||
state
|
state
|
||||||
|
@ -71,7 +71,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
|
|||||||
|
|
||||||
private def resetUserLeftFlag(msg: UserJoinMeetingReqMsg) = {
|
private def resetUserLeftFlag(msg: UserJoinMeetingReqMsg) = {
|
||||||
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
|
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
|
||||||
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false)
|
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, leftFlag = false)
|
||||||
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
|
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.bigbluebutton.core.apps.users
|
package org.bigbluebutton.core.apps.users
|
||||||
|
|
||||||
import org.bigbluebutton.common2.msgs.UserLeaveReqMsg
|
import org.bigbluebutton.common2.msgs.UserLeaveReqMsg
|
||||||
|
import org.bigbluebutton.core.api.{ UserClosedAllGraphqlConnectionsInternalMsg }
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.models.{ RegisteredUsers, Users2x }
|
import org.bigbluebutton.core.models.{ RegisteredUsers, Users2x }
|
||||||
import org.bigbluebutton.core.running.{ HandlerHelpers, MeetingActor, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ HandlerHelpers, MeetingActor, OutMsgRouter }
|
||||||
@ -12,23 +13,33 @@ trait UserLeaveReqMsgHdlr extends HandlerHelpers {
|
|||||||
val outGW: OutMsgRouter
|
val outGW: OutMsgRouter
|
||||||
|
|
||||||
def handleUserLeaveReqMsg(msg: UserLeaveReqMsg, state: MeetingState2x): MeetingState2x = {
|
def handleUserLeaveReqMsg(msg: UserLeaveReqMsg, state: MeetingState2x): MeetingState2x = {
|
||||||
Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) match {
|
handleUserLeaveReq(msg.body.userId, msg.header.meetingId, msg.body.loggedOut, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleUserClosedAllGraphqlConnectionsInternalMsg(msg: UserClosedAllGraphqlConnectionsInternalMsg, state: MeetingState2x): MeetingState2x = {
|
||||||
|
log.info("Received user closed all graphql connections. user {} meetingId={}", msg.userId, liveMeeting.props.meetingProp.intId)
|
||||||
|
|
||||||
|
handleUserLeaveReq(msg.userId, liveMeeting.props.meetingProp.intId, loggedOut = false, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleUserLeaveReq(userId: String, meetingId: String, loggedOut: Boolean, state: MeetingState2x): MeetingState2x = {
|
||||||
|
Users2x.findWithIntId(liveMeeting.users2x, userId) match {
|
||||||
case Some(reconnectingUser) =>
|
case Some(reconnectingUser) =>
|
||||||
log.info("Received user left meeting. user {} meetingId={}", msg.body.userId, msg.header.meetingId)
|
log.info("Received user left meeting. user {} meetingId={}", userId, meetingId)
|
||||||
if (!reconnectingUser.userLeftFlag.left) {
|
if (!reconnectingUser.userLeftFlag.left) {
|
||||||
log.info("Setting user left flag. user {} meetingId={}", msg.body.userId, msg.header.meetingId)
|
log.info("Setting user left flag. user {} meetingId={}", userId, meetingId)
|
||||||
// Just flag that user has left as the user might be reconnecting.
|
// 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.
|
// An audit will remove this user if it hasn't rejoined after a certain period of time.
|
||||||
// ralam oct 23, 2018
|
// ralam oct 23, 2018
|
||||||
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, true)
|
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, userId, leftFlag = true)
|
||||||
|
|
||||||
Users2x.setUserLeftFlag(liveMeeting.users2x, msg.body.userId)
|
Users2x.setUserLeftFlag(liveMeeting.users2x, userId)
|
||||||
}
|
}
|
||||||
if (msg.body.loggedOut) {
|
if (loggedOut) {
|
||||||
log.info("Setting user logged out flag. user {} meetingId={}", msg.body.userId, msg.header.meetingId)
|
log.info("Setting user logged out flag. user {} meetingId={}", userId, meetingId)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
ru <- RegisteredUsers.findWithUserId(msg.body.userId, liveMeeting.registeredUsers)
|
ru <- RegisteredUsers.findWithUserId(userId, liveMeeting.registeredUsers)
|
||||||
} yield {
|
} yield {
|
||||||
RegisteredUsers.setUserLoggedOutFlag(liveMeeting.registeredUsers, ru)
|
RegisteredUsers.setUserLoggedOutFlag(liveMeeting.registeredUsers, ru)
|
||||||
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, ru.id, ru.sessionToken, "user_loggedout", outGW)
|
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, ru.id, ru.sessionToken, "user_loggedout", outGW)
|
||||||
@ -39,4 +50,5 @@ trait UserLeaveReqMsgHdlr extends HandlerHelpers {
|
|||||||
state
|
state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -158,16 +158,18 @@ class UsersApp(
|
|||||||
with RegisterUserReqMsgHdlr
|
with RegisterUserReqMsgHdlr
|
||||||
with ChangeUserRoleCmdMsgHdlr
|
with ChangeUserRoleCmdMsgHdlr
|
||||||
with SetUserSpeechLocaleMsgHdlr
|
with SetUserSpeechLocaleMsgHdlr
|
||||||
|
with SetUserSpeechOptionsMsgHdlr
|
||||||
with SyncGetUsersMeetingRespMsgHdlr
|
with SyncGetUsersMeetingRespMsgHdlr
|
||||||
with LogoutAndEndMeetingCmdMsgHdlr
|
with LogoutAndEndMeetingCmdMsgHdlr
|
||||||
with SetRecordingStatusCmdMsgHdlr
|
with SetRecordingStatusCmdMsgHdlr
|
||||||
with RecordAndClearPreviousMarkersCmdMsgHdlr
|
with RecordAndClearPreviousMarkersCmdMsgHdlr
|
||||||
with SendRecordingTimerInternalMsgHdlr
|
with SendRecordingTimerInternalMsgHdlr
|
||||||
with GetRecordingStatusReqMsgHdlr
|
with GetRecordingStatusReqMsgHdlr
|
||||||
with SelectRandomViewerReqMsgHdlr
|
|
||||||
with AssignPresenterReqMsgHdlr
|
with AssignPresenterReqMsgHdlr
|
||||||
with ChangeUserPinStateReqMsgHdlr
|
with ChangeUserPinStateReqMsgHdlr
|
||||||
with ChangeUserMobileFlagReqMsgHdlr
|
with ChangeUserMobileFlagReqMsgHdlr
|
||||||
|
with UserConnectionAliveReqMsgHdlr
|
||||||
|
with UserConnectionUpdateRttReqMsgHdlr
|
||||||
with ChangeUserReactionEmojiReqMsgHdlr
|
with ChangeUserReactionEmojiReqMsgHdlr
|
||||||
with ChangeUserRaiseHandReqMsgHdlr
|
with ChangeUserRaiseHandReqMsgHdlr
|
||||||
with ChangeUserAwayReqMsgHdlr
|
with ChangeUserAwayReqMsgHdlr
|
||||||
|
@ -58,7 +58,6 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
|
|||||||
avatar = "",
|
avatar = "",
|
||||||
color = userColor,
|
color = userColor,
|
||||||
clientType = if (isDialInUser) "dial-in-user" else "",
|
clientType = if (isDialInUser) "dial-in-user" else "",
|
||||||
pickExempted = false,
|
|
||||||
userLeftFlag = UserLeftFlag(false, 0)
|
userLeftFlag = UserLeftFlag(false, 0)
|
||||||
)
|
)
|
||||||
Users2x.add(liveMeeting.users2x, newUser)
|
Users2x.add(liveMeeting.users2x, newUser)
|
||||||
|
@ -40,7 +40,7 @@ trait UserLeftVoiceConfEvtMsgHdlr {
|
|||||||
UsersApp.guestWaitingLeft(liveMeeting, user.intId, outGW)
|
UsersApp.guestWaitingLeft(liveMeeting, user.intId, outGW)
|
||||||
}
|
}
|
||||||
Users2x.remove(liveMeeting.users2x, user.intId)
|
Users2x.remove(liveMeeting.users2x, user.intId)
|
||||||
UserDAO.delete(user.intId)
|
UserDAO.softDelete(user.intId)
|
||||||
VoiceApp.removeUserFromVoiceConf(liveMeeting, outGW, msg.body.voiceUserId)
|
VoiceApp.removeUserFromVoiceConf(liveMeeting, outGW, msg.body.voiceUserId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,8 +4,9 @@ import org.bigbluebutton.common2.msgs._
|
|||||||
import org.bigbluebutton.core.apps.PermissionCheck
|
import org.bigbluebutton.core.apps.PermissionCheck
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.MeetingUsersPoliciesDAO
|
import org.bigbluebutton.core.db.MeetingUsersPoliciesDAO
|
||||||
|
import org.bigbluebutton.core.models.{ RegisteredUsers, Roles, Users2x }
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
|
||||||
|
|
||||||
trait UpdateWebcamsOnlyForModeratorCmdMsgHdlr {
|
trait UpdateWebcamsOnlyForModeratorCmdMsgHdlr {
|
||||||
this: WebcamApp2x =>
|
this: WebcamApp2x =>
|
||||||
@ -76,6 +77,16 @@ trait UpdateWebcamsOnlyForModeratorCmdMsgHdlr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
broadcastEvent(meetingId, msg.body.setBy, value)
|
broadcastEvent(meetingId, msg.body.setBy, value)
|
||||||
|
|
||||||
|
//Refresh graphql session for all locked viewers
|
||||||
|
for {
|
||||||
|
user <- Users2x.findAll(liveMeeting.users2x)
|
||||||
|
if user.locked
|
||||||
|
if user.role == Roles.VIEWER_ROLE
|
||||||
|
regUser <- RegisteredUsers.findWithUserId(user.intId, liveMeeting.registeredUsers)
|
||||||
|
} yield {
|
||||||
|
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, "webcamOnlyForMod_changed", bus.outGW)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
|
@ -52,11 +52,11 @@ trait SendWhiteboardAnnotationsPubMsgHdlr extends RightsManagementTrait {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (isUserOneOfPermited || isUserAmongPresenters) {
|
if (isUserOneOfPermited || isUserAmongPresenters) {
|
||||||
println("============= Printing Sanitized annotations ============")
|
// println("============= Printing Sanitized annotations ============")
|
||||||
for (annotation <- msg.body.annotations) {
|
// for (annotation <- msg.body.annotations) {
|
||||||
printAnnotationInfo(annotation)
|
// printAnnotationInfo(annotation)
|
||||||
}
|
// }
|
||||||
println("============= Printed Sanitized annotations ============")
|
// println("============= Printed Sanitized annotations ============")
|
||||||
val annotations = sendWhiteboardAnnotations(msg.body.whiteboardId, msg.header.userId, msg.body.annotations, liveMeeting, isUserAmongPresenters, isUserModerator)
|
val annotations = sendWhiteboardAnnotations(msg.body.whiteboardId, msg.header.userId, msg.body.annotations, liveMeeting, isUserAmongPresenters, isUserModerator)
|
||||||
broadcastEvent(msg, msg.body.whiteboardId, annotations, msg.body.html5InstanceId)
|
broadcastEvent(msg, msg.body.whiteboardId, annotations, msg.body.html5InstanceId)
|
||||||
} else {
|
} else {
|
||||||
|
@ -20,8 +20,16 @@ case class MeetingDbModel(
|
|||||||
presentationUploadExternalUrl: String,
|
presentationUploadExternalUrl: String,
|
||||||
learningDashboardAccessToken: String,
|
learningDashboardAccessToken: String,
|
||||||
logoutUrl: String,
|
logoutUrl: String,
|
||||||
|
customLogoUrl: Option[String],
|
||||||
|
bannerText: Option[String],
|
||||||
|
bannerColor: Option[String],
|
||||||
createdTime: Long,
|
createdTime: Long,
|
||||||
durationInSeconds: Int
|
durationInSeconds: Int,
|
||||||
|
endWhenNoModerator: Boolean,
|
||||||
|
endWhenNoModeratorDelayInMinutes: Int,
|
||||||
|
endedAt: Option[java.sql.Timestamp],
|
||||||
|
endedReasonCode: Option[String],
|
||||||
|
endedBy: Option[String],
|
||||||
)
|
)
|
||||||
|
|
||||||
class MeetingDbTableDef(tag: Tag) extends Table[MeetingDbModel](tag, None, "meeting") {
|
class MeetingDbTableDef(tag: Tag) extends Table[MeetingDbModel](tag, None, "meeting") {
|
||||||
@ -38,8 +46,16 @@ class MeetingDbTableDef(tag: Tag) extends Table[MeetingDbModel](tag, None, "meet
|
|||||||
presentationUploadExternalUrl,
|
presentationUploadExternalUrl,
|
||||||
learningDashboardAccessToken,
|
learningDashboardAccessToken,
|
||||||
logoutUrl,
|
logoutUrl,
|
||||||
|
customLogoUrl,
|
||||||
|
bannerText,
|
||||||
|
bannerColor,
|
||||||
createdTime,
|
createdTime,
|
||||||
durationInSeconds
|
durationInSeconds,
|
||||||
|
endWhenNoModerator,
|
||||||
|
endWhenNoModeratorDelayInMinutes,
|
||||||
|
endedAt,
|
||||||
|
endedReasonCode,
|
||||||
|
endedBy
|
||||||
) <> (MeetingDbModel.tupled, MeetingDbModel.unapply)
|
) <> (MeetingDbModel.tupled, MeetingDbModel.unapply)
|
||||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||||
val extId = column[String]("extId")
|
val extId = column[String]("extId")
|
||||||
@ -53,8 +69,16 @@ class MeetingDbTableDef(tag: Tag) extends Table[MeetingDbModel](tag, None, "meet
|
|||||||
val presentationUploadExternalUrl = column[String]("presentationUploadExternalUrl")
|
val presentationUploadExternalUrl = column[String]("presentationUploadExternalUrl")
|
||||||
val learningDashboardAccessToken = column[String]("learningDashboardAccessToken")
|
val learningDashboardAccessToken = column[String]("learningDashboardAccessToken")
|
||||||
val logoutUrl = column[String]("logoutUrl")
|
val logoutUrl = column[String]("logoutUrl")
|
||||||
|
val customLogoUrl = column[Option[String]]("customLogoUrl")
|
||||||
|
val bannerText = column[Option[String]]("bannerText")
|
||||||
|
val bannerColor = column[Option[String]]("bannerColor")
|
||||||
val createdTime = column[Long]("createdTime")
|
val createdTime = column[Long]("createdTime")
|
||||||
val durationInSeconds = column[Int]("durationInSeconds")
|
val durationInSeconds = column[Int]("durationInSeconds")
|
||||||
|
val endWhenNoModerator = column[Boolean]("endWhenNoModerator")
|
||||||
|
val endWhenNoModeratorDelayInMinutes = column[Int]("endWhenNoModeratorDelayInMinutes")
|
||||||
|
val endedAt = column[Option[java.sql.Timestamp]]("endedAt")
|
||||||
|
val endedReasonCode = column[Option[String]]("endedReasonCode")
|
||||||
|
val endedBy = column[Option[String]]("endedBy")
|
||||||
}
|
}
|
||||||
|
|
||||||
object MeetingDAO {
|
object MeetingDAO {
|
||||||
@ -74,8 +98,25 @@ object MeetingDAO {
|
|||||||
presentationUploadExternalUrl = meetingProps.meetingProp.presentationUploadExternalUrl,
|
presentationUploadExternalUrl = meetingProps.meetingProp.presentationUploadExternalUrl,
|
||||||
learningDashboardAccessToken = meetingProps.password.learningDashboardAccessToken,
|
learningDashboardAccessToken = meetingProps.password.learningDashboardAccessToken,
|
||||||
logoutUrl = meetingProps.systemProps.logoutUrl,
|
logoutUrl = meetingProps.systemProps.logoutUrl,
|
||||||
|
customLogoUrl = meetingProps.systemProps.customLogoURL match {
|
||||||
|
case "" => None
|
||||||
|
case logoUrl => Some(logoUrl)
|
||||||
|
},
|
||||||
|
bannerText = meetingProps.systemProps.bannerText match {
|
||||||
|
case "" => None
|
||||||
|
case bannerText => Some(bannerText)
|
||||||
|
},
|
||||||
|
bannerColor = meetingProps.systemProps.bannerColor match {
|
||||||
|
case "" => None
|
||||||
|
case bannerColor => Some(bannerColor)
|
||||||
|
},
|
||||||
createdTime = meetingProps.durationProps.createdTime,
|
createdTime = meetingProps.durationProps.createdTime,
|
||||||
durationInSeconds = meetingProps.durationProps.duration * 60
|
durationInSeconds = meetingProps.durationProps.duration * 60,
|
||||||
|
endWhenNoModerator = meetingProps.durationProps.endWhenNoModerator,
|
||||||
|
endWhenNoModeratorDelayInMinutes = meetingProps.durationProps.endWhenNoModeratorDelayInMinutes,
|
||||||
|
endedAt = None,
|
||||||
|
endedReasonCode = None,
|
||||||
|
endedBy = None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).onComplete {
|
).onComplete {
|
||||||
@ -126,4 +167,50 @@ object MeetingDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def setMeetingEnded(meetingId: String, endedReasonCode: String, endedBy: String) = {
|
||||||
|
|
||||||
|
UserDAO.softDeleteAllFromMeeting(meetingId)
|
||||||
|
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[MeetingDbTableDef]
|
||||||
|
.filter(_.meetingId === meetingId)
|
||||||
|
.map(a => (a.endedAt, a.endedReasonCode, a.endedBy))
|
||||||
|
.update(
|
||||||
|
(
|
||||||
|
Some(new java.sql.Timestamp(System.currentTimeMillis())),
|
||||||
|
Some(endedReasonCode),
|
||||||
|
endedBy match {
|
||||||
|
case "" => None
|
||||||
|
case c => Some(c)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated endedAt=now() on Meeting table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating endedAt=now() Meeting: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def setAllMeetingsEnded(endedReasonCode: String, endedBy: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[MeetingDbTableDef]
|
||||||
|
.filter(_.endedAt.isEmpty)
|
||||||
|
.map(a => (a.endedAt, a.endedReasonCode, a.endedBy))
|
||||||
|
.update(
|
||||||
|
(
|
||||||
|
Some(new java.sql.Timestamp(System.currentTimeMillis())),
|
||||||
|
Some(endedReasonCode),
|
||||||
|
endedBy match {
|
||||||
|
case "" => None
|
||||||
|
case c => Some(c)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated all-meetings endedAt=now() on Meeting table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating all-meetings endedAt=now() on Meeting table: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
package org.bigbluebutton.core.db
|
package org.bigbluebutton.core.db
|
||||||
|
|
||||||
import PostgresProfile.api._
|
import PostgresProfile.api._
|
||||||
|
import org.bigbluebutton.core.db.DatabaseConnection.{db, logger}
|
||||||
import spray.json.JsValue
|
import spray.json.JsValue
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
import scala.concurrent.{Await, Future}
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
|
import scala.concurrent.duration.Duration
|
||||||
|
|
||||||
object Permission {
|
object Permission {
|
||||||
val allowedRoles = List("MODERATOR","VIEWER","PRESENTER")
|
val allowedRoles = List("MODERATOR","VIEWER","PRESENTER")
|
||||||
@ -19,6 +23,7 @@ case class PluginDataChannelMessageDbModel(
|
|||||||
toRoles: Option[List[String]],
|
toRoles: Option[List[String]],
|
||||||
toUserIds: Option[List[String]],
|
toUserIds: Option[List[String]],
|
||||||
createdAt: java.sql.Timestamp,
|
createdAt: java.sql.Timestamp,
|
||||||
|
deletedAt: Option[java.sql.Timestamp],
|
||||||
)
|
)
|
||||||
|
|
||||||
class PluginDataChannelMessageDbTableDef(tag: Tag) extends Table[PluginDataChannelMessageDbModel](tag, None, "pluginDataChannelMessage") {
|
class PluginDataChannelMessageDbTableDef(tag: Tag) extends Table[PluginDataChannelMessageDbModel](tag, None, "pluginDataChannelMessage") {
|
||||||
@ -31,7 +36,8 @@ class PluginDataChannelMessageDbTableDef(tag: Tag) extends Table[PluginDataChann
|
|||||||
val toRoles = column[Option[List[String]]]("toRoles")
|
val toRoles = column[Option[List[String]]]("toRoles")
|
||||||
val toUserIds = column[Option[List[String]]]("toUserIds")
|
val toUserIds = column[Option[List[String]]]("toUserIds")
|
||||||
val createdAt = column[java.sql.Timestamp]("createdAt")
|
val createdAt = column[java.sql.Timestamp]("createdAt")
|
||||||
override def * = (meetingId, pluginName, dataChannel, payloadJson, fromUserId, toRoles, toUserIds, createdAt) <> (PluginDataChannelMessageDbModel.tupled, PluginDataChannelMessageDbModel.unapply)
|
val deletedAt = column[Option[java.sql.Timestamp]]("deletedAt")
|
||||||
|
override def * = (meetingId, pluginName, dataChannel, payloadJson, fromUserId, toRoles, toUserIds, createdAt, deletedAt) <> (PluginDataChannelMessageDbModel.tupled, PluginDataChannelMessageDbModel.unapply)
|
||||||
}
|
}
|
||||||
|
|
||||||
object PluginDataChannelMessageDAO {
|
object PluginDataChannelMessageDAO {
|
||||||
@ -49,7 +55,8 @@ object PluginDataChannelMessageDAO {
|
|||||||
case filtered => Some(filtered)
|
case filtered => Some(filtered)
|
||||||
},
|
},
|
||||||
toUserIds = if(toUserIds.isEmpty) None else Some(toUserIds),
|
toUserIds = if(toUserIds.isEmpty) None else Some(toUserIds),
|
||||||
createdAt = new java.sql.Timestamp(System.currentTimeMillis())
|
createdAt = new java.sql.Timestamp(System.currentTimeMillis()),
|
||||||
|
deletedAt = None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).onComplete {
|
).onComplete {
|
||||||
@ -57,4 +64,51 @@ object PluginDataChannelMessageDAO {
|
|||||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting PluginDataChannelMessage: $e")
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting PluginDataChannelMessage: $e")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def reset(meetingId: String, pluginName: String, dataChannel: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[PluginDataChannelMessageDbTableDef]
|
||||||
|
.filter(_.meetingId === meetingId)
|
||||||
|
.filter(_.pluginName === pluginName)
|
||||||
|
.filter(_.dataChannel === dataChannel)
|
||||||
|
.map(u => (u.deletedAt))
|
||||||
|
.update(Some(new java.sql.Timestamp(System.currentTimeMillis())))
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated deleted=now() on pluginDataChannelMessage table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.error(s"Error updating deleted=now() pluginDataChannelMessage: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def getMessageSender(meetingId: String, pluginName: String, dataChannel: String, messageId: String): String = {
|
||||||
|
val query = sql"""SELECT "fromUserId"
|
||||||
|
FROM "pluginDataChannelMessage"
|
||||||
|
WHERE "deletedAt" is null
|
||||||
|
AND "meetingId" = ${meetingId}
|
||||||
|
AND "pluginName" = ${pluginName}
|
||||||
|
AND "dataChannel" = ${dataChannel}
|
||||||
|
AND "messageId" = ${messageId}""".as[String].headOption
|
||||||
|
|
||||||
|
Await.result(DatabaseConnection.db.run(query), Duration.Inf) match {
|
||||||
|
case Some(userId) => userId
|
||||||
|
case None => {
|
||||||
|
logger.debug("Message {} not found in database (maybe it was deleted).", messageId)
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def delete(meetingId: String, pluginName: String, dataChannel: String, messageId: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
sqlu"""UPDATE "pluginDataChannelMessage" SET
|
||||||
|
"deletedAt" = current_timestamp
|
||||||
|
WHERE "meetingId" = ${meetingId}
|
||||||
|
AND "pluginName" = ${pluginName}
|
||||||
|
AND "dataChannel" = ${dataChannel}
|
||||||
|
AND "messageId" = ${messageId}"""
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated deleted=now() on pluginDataChannelMessage table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating deleted=now() pluginDataChannelMessage: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -14,8 +14,8 @@ case class TimerDbModel(
|
|||||||
active: Boolean,
|
active: Boolean,
|
||||||
time: Long,
|
time: Long,
|
||||||
accumulated: Long,
|
accumulated: Long,
|
||||||
startedAt: Long,
|
startedOn: Long,
|
||||||
endedAt: Long,
|
endedOn: Long,
|
||||||
songTrack: String,
|
songTrack: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,10 +26,10 @@ class TimerDbTableDef(tag: Tag) extends Table[TimerDbModel](tag, None, "timer")
|
|||||||
val active = column[Boolean]("active")
|
val active = column[Boolean]("active")
|
||||||
val time = column[Long]("time")
|
val time = column[Long]("time")
|
||||||
val accumulated = column[Long]("accumulated")
|
val accumulated = column[Long]("accumulated")
|
||||||
val startedAt = column[Long]("startedAt")
|
val startedOn = column[Long]("startedOn")
|
||||||
val endedAt = column[Long]("endedAt")
|
val endedOn = column[Long]("endedOn")
|
||||||
val songTrack = column[String]("songTrack")
|
val songTrack = column[String]("songTrack")
|
||||||
override def * = (meetingId, stopwatch, running, active, time, accumulated, startedAt, endedAt, songTrack) <> (TimerDbModel.tupled, TimerDbModel.unapply)
|
override def * = (meetingId, stopwatch, running, active, time, accumulated, startedOn, endedOn, songTrack) <> (TimerDbModel.tupled, TimerDbModel.unapply)
|
||||||
}
|
}
|
||||||
|
|
||||||
object TimerDAO {
|
object TimerDAO {
|
||||||
@ -43,8 +43,8 @@ object TimerDAO {
|
|||||||
active = false,
|
active = false,
|
||||||
time = 300000,
|
time = 300000,
|
||||||
accumulated = 0,
|
accumulated = 0,
|
||||||
startedAt = 0,
|
startedOn = 0,
|
||||||
endedAt = 0,
|
endedOn = 0,
|
||||||
songTrack = "noTrack",
|
songTrack = "noTrack",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -58,7 +58,7 @@ object TimerDAO {
|
|||||||
DatabaseConnection.db.run(
|
DatabaseConnection.db.run(
|
||||||
TableQuery[TimerDbTableDef]
|
TableQuery[TimerDbTableDef]
|
||||||
.filter(_.meetingId === meetingId)
|
.filter(_.meetingId === meetingId)
|
||||||
.map(t => (t.stopwatch, t.running, t.active, t.time, t.accumulated, t.startedAt, t.endedAt, t.songTrack))
|
.map(t => (t.stopwatch, t.running, t.active, t.time, t.accumulated, t.startedOn, t.endedOn, t.songTrack))
|
||||||
.update((getStopwatch(timerModel), getRunning(timerModel), getIsActive(timerModel), getTime(timerModel), getAccumulated(timerModel), getStartedAt(timerModel), getEndedAt(timerModel), getTrack(timerModel))
|
.update((getStopwatch(timerModel), getRunning(timerModel), getIsActive(timerModel), getTime(timerModel), getAccumulated(timerModel), getStartedAt(timerModel), getEndedAt(timerModel), getTrack(timerModel))
|
||||||
)
|
)
|
||||||
).onComplete {
|
).onComplete {
|
||||||
|
@ -7,16 +7,20 @@ import scala.util.{ Failure, Success }
|
|||||||
case class UserConnectionStatusDbModel(
|
case class UserConnectionStatusDbModel(
|
||||||
userId: String,
|
userId: String,
|
||||||
meetingId: String,
|
meetingId: String,
|
||||||
connectionAliveAt: Option[java.sql.Timestamp]
|
connectionAliveAt: Option[java.sql.Timestamp],
|
||||||
|
userClientResponseAt: Option[java.sql.Timestamp],
|
||||||
|
networkRttInMs: Option[Double]
|
||||||
)
|
)
|
||||||
|
|
||||||
class UserConnectionStatusDbTableDef(tag: Tag) extends Table[UserConnectionStatusDbModel](tag, None, "user_connectionStatus") {
|
class UserConnectionStatusDbTableDef(tag: Tag) extends Table[UserConnectionStatusDbModel](tag, None, "user_connectionStatus") {
|
||||||
override def * = (
|
override def * = (
|
||||||
userId, meetingId, connectionAliveAt
|
userId, meetingId, connectionAliveAt, userClientResponseAt, networkRttInMs
|
||||||
) <> (UserConnectionStatusDbModel.tupled, UserConnectionStatusDbModel.unapply)
|
) <> (UserConnectionStatusDbModel.tupled, UserConnectionStatusDbModel.unapply)
|
||||||
val userId = column[String]("userId", O.PrimaryKey)
|
val userId = column[String]("userId", O.PrimaryKey)
|
||||||
val meetingId = column[String]("meetingId")
|
val meetingId = column[String]("meetingId")
|
||||||
val connectionAliveAt = column[Option[java.sql.Timestamp]]("connectionAliveAt")
|
val connectionAliveAt = column[Option[java.sql.Timestamp]]("connectionAliveAt")
|
||||||
|
val userClientResponseAt = column[Option[java.sql.Timestamp]]("userClientResponseAt")
|
||||||
|
val networkRttInMs = column[Option[Double]]("networkRttInMs")
|
||||||
}
|
}
|
||||||
|
|
||||||
object UserConnectionStatusDAO {
|
object UserConnectionStatusDAO {
|
||||||
@ -27,7 +31,9 @@ object UserConnectionStatusDAO {
|
|||||||
UserConnectionStatusDbModel(
|
UserConnectionStatusDbModel(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
meetingId = meetingId,
|
meetingId = meetingId,
|
||||||
connectionAliveAt = None
|
connectionAliveAt = None,
|
||||||
|
userClientResponseAt = None,
|
||||||
|
networkRttInMs = None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).onComplete {
|
).onComplete {
|
||||||
@ -36,4 +42,28 @@ object UserConnectionStatusDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def updateUserAlive(userId: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[UserConnectionStatusDbTableDef]
|
||||||
|
.filter(_.userId === userId)
|
||||||
|
.map(t => (t.connectionAliveAt))
|
||||||
|
.update(Some(new java.sql.Timestamp(System.currentTimeMillis())))
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated connectionAliveAt on UserConnectionStatus table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating connectionAliveAt on UserConnectionStatus: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateUserRtt(userId: String, networkRttInMs: Double) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[UserConnectionStatusDbTableDef]
|
||||||
|
.filter(_.userId === userId)
|
||||||
|
.map(t => (t.networkRttInMs, t.userClientResponseAt))
|
||||||
|
.update((Some(networkRttInMs), Some(new java.sql.Timestamp(System.currentTimeMillis()))))
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated networkRttInMs on UserConnectionStatus table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating networkRttInMs on UserConnectionStatus: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ case class UserDbModel(
|
|||||||
avatar: String = "",
|
avatar: String = "",
|
||||||
color: String = "",
|
color: String = "",
|
||||||
sessionToken: String = "",
|
sessionToken: String = "",
|
||||||
|
authToken: String = "",
|
||||||
authed: Boolean = false,
|
authed: Boolean = false,
|
||||||
joined: Boolean = false,
|
joined: Boolean = false,
|
||||||
joinErrorMessage: Option[String],
|
joinErrorMessage: Option[String],
|
||||||
@ -31,7 +32,7 @@ case class UserDbModel(
|
|||||||
|
|
||||||
class UserDbTableDef(tag: Tag) extends Table[UserDbModel](tag, None, "user") {
|
class UserDbTableDef(tag: Tag) extends Table[UserDbModel](tag, None, "user") {
|
||||||
override def * = (
|
override def * = (
|
||||||
userId,extId,meetingId,name,role,avatar,color, sessionToken, authed,joined,joinErrorCode, joinErrorMessage, banned,loggedOut,guest,guestStatus,registeredOn,excludeFromDashboard, enforceLayout) <> (UserDbModel.tupled, UserDbModel.unapply)
|
userId,extId,meetingId,name,role,avatar,color, sessionToken, authToken, authed,joined,joinErrorCode, joinErrorMessage, banned,loggedOut,guest,guestStatus,registeredOn,excludeFromDashboard, enforceLayout) <> (UserDbModel.tupled, UserDbModel.unapply)
|
||||||
val userId = column[String]("userId", O.PrimaryKey)
|
val userId = column[String]("userId", O.PrimaryKey)
|
||||||
val extId = column[String]("extId")
|
val extId = column[String]("extId")
|
||||||
val meetingId = column[String]("meetingId")
|
val meetingId = column[String]("meetingId")
|
||||||
@ -40,6 +41,7 @@ class UserDbTableDef(tag: Tag) extends Table[UserDbModel](tag, None, "user") {
|
|||||||
val avatar = column[String]("avatar")
|
val avatar = column[String]("avatar")
|
||||||
val color = column[String]("color")
|
val color = column[String]("color")
|
||||||
val sessionToken = column[String]("sessionToken")
|
val sessionToken = column[String]("sessionToken")
|
||||||
|
val authToken = column[String]("authToken")
|
||||||
val authed = column[Boolean]("authed")
|
val authed = column[Boolean]("authed")
|
||||||
val joined = column[Boolean]("joined")
|
val joined = column[Boolean]("joined")
|
||||||
val joinErrorCode = column[Option[String]]("joinErrorCode")
|
val joinErrorCode = column[Option[String]]("joinErrorCode")
|
||||||
@ -60,6 +62,7 @@ object UserDAO {
|
|||||||
UserDbModel(
|
UserDbModel(
|
||||||
userId = regUser.id,
|
userId = regUser.id,
|
||||||
extId = regUser.externId,
|
extId = regUser.externId,
|
||||||
|
authToken = regUser.authToken,
|
||||||
meetingId = meetingId,
|
meetingId = meetingId,
|
||||||
name = regUser.name,
|
name = regUser.name,
|
||||||
role = regUser.role,
|
role = regUser.role,
|
||||||
@ -132,7 +135,7 @@ object UserDAO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def delete(intId: String) = {
|
def softDelete(intId: String) = {
|
||||||
DatabaseConnection.db.run(
|
DatabaseConnection.db.run(
|
||||||
TableQuery[UserDbTableDef]
|
TableQuery[UserDbTableDef]
|
||||||
.filter(_.userId === intId)
|
.filter(_.userId === intId)
|
||||||
@ -144,7 +147,19 @@ object UserDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def deleteAllFromMeeting(meetingId: String) = {
|
def softDeleteAllFromMeeting(meetingId: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[UserDbTableDef]
|
||||||
|
.filter(_.meetingId === meetingId)
|
||||||
|
.map(u => (u.loggedOut))
|
||||||
|
.update((true))
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated loggedOut=true on user table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.error(s"Error updating loggedOut=true user: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def permanentlyDeleteAllFromMeeting(meetingId: String) = {
|
||||||
DatabaseConnection.db.run(
|
DatabaseConnection.db.run(
|
||||||
TableQuery[UserDbTableDef]
|
TableQuery[UserDbTableDef]
|
||||||
.filter(_.meetingId === meetingId)
|
.filter(_.meetingId === meetingId)
|
||||||
|
@ -9,32 +9,35 @@ import scala.util.{Failure, Success }
|
|||||||
case class UserGraphqlConnectionDbModel (
|
case class UserGraphqlConnectionDbModel (
|
||||||
graphqlConnectionId: Option[Int],
|
graphqlConnectionId: Option[Int],
|
||||||
sessionToken: String,
|
sessionToken: String,
|
||||||
|
middlewareUID: String,
|
||||||
middlewareConnectionId: String,
|
middlewareConnectionId: String,
|
||||||
stablishedAt: java.sql.Timestamp,
|
establishedAt: java.sql.Timestamp,
|
||||||
closedAt: Option[java.sql.Timestamp],
|
closedAt: Option[java.sql.Timestamp],
|
||||||
)
|
)
|
||||||
|
|
||||||
class UserGraphqlConnectionDbTableDef(tag: Tag) extends Table[UserGraphqlConnectionDbModel](tag, None, "user_graphqlConnection") {
|
class UserGraphqlConnectionDbTableDef(tag: Tag) extends Table[UserGraphqlConnectionDbModel](tag, None, "user_graphqlConnection") {
|
||||||
override def * = (
|
override def * = (
|
||||||
graphqlConnectionId, sessionToken, middlewareConnectionId, stablishedAt, closedAt
|
graphqlConnectionId, sessionToken, middlewareUID, middlewareConnectionId, establishedAt, closedAt
|
||||||
) <> (UserGraphqlConnectionDbModel.tupled, UserGraphqlConnectionDbModel.unapply)
|
) <> (UserGraphqlConnectionDbModel.tupled, UserGraphqlConnectionDbModel.unapply)
|
||||||
val graphqlConnectionId = column[Option[Int]]("graphqlConnectionId", O.PrimaryKey, O.AutoInc)
|
val graphqlConnectionId = column[Option[Int]]("graphqlConnectionId", O.PrimaryKey, O.AutoInc)
|
||||||
val sessionToken = column[String]("sessionToken")
|
val sessionToken = column[String]("sessionToken")
|
||||||
|
val middlewareUID = column[String]("middlewareUID")
|
||||||
val middlewareConnectionId = column[String]("middlewareConnectionId")
|
val middlewareConnectionId = column[String]("middlewareConnectionId")
|
||||||
val stablishedAt = column[java.sql.Timestamp]("stablishedAt")
|
val establishedAt = column[java.sql.Timestamp]("establishedAt")
|
||||||
val closedAt = column[Option[java.sql.Timestamp]]("closedAt")
|
val closedAt = column[Option[java.sql.Timestamp]]("closedAt")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
object UserGraphqlConnectionDAO {
|
object UserGraphqlConnectionDAO {
|
||||||
def insert(sessionToken: String, middlewareConnectionId: String) = {
|
def insert(sessionToken: String, middlewareUID:String, middlewareConnectionId: String) = {
|
||||||
DatabaseConnection.db.run(
|
DatabaseConnection.db.run(
|
||||||
TableQuery[UserGraphqlConnectionDbTableDef].insertOrUpdate(
|
TableQuery[UserGraphqlConnectionDbTableDef].insertOrUpdate(
|
||||||
UserGraphqlConnectionDbModel(
|
UserGraphqlConnectionDbModel(
|
||||||
graphqlConnectionId = None,
|
graphqlConnectionId = None,
|
||||||
sessionToken = sessionToken,
|
sessionToken = sessionToken,
|
||||||
|
middlewareUID = middlewareUID,
|
||||||
middlewareConnectionId = middlewareConnectionId,
|
middlewareConnectionId = middlewareConnectionId,
|
||||||
stablishedAt = new java.sql.Timestamp(System.currentTimeMillis()),
|
establishedAt = new java.sql.Timestamp(System.currentTimeMillis()),
|
||||||
closedAt = None
|
closedAt = None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -46,11 +49,12 @@ object UserGraphqlConnectionDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def updateClosed(sessionToken: String, middlewareConnectionId: String) = {
|
def updateClosed(sessionToken: String, middlewareUID: String, middlewareConnectionId: String) = {
|
||||||
DatabaseConnection.db.run(
|
DatabaseConnection.db.run(
|
||||||
TableQuery[UserGraphqlConnectionDbTableDef]
|
TableQuery[UserGraphqlConnectionDbTableDef]
|
||||||
.filter(_.sessionToken === sessionToken)
|
.filter(_.sessionToken === sessionToken)
|
||||||
.filter(_.middlewareConnectionId === middlewareConnectionId)
|
.filter(_.middlewareConnectionId === middlewareConnectionId)
|
||||||
|
.filter(_.middlewareUID === middlewareUID)
|
||||||
.filter(_.closedAt.isEmpty)
|
.filter(_.closedAt.isEmpty)
|
||||||
.map(u => u.closedAt)
|
.map(u => u.closedAt)
|
||||||
.update(Some(new java.sql.Timestamp(System.currentTimeMillis())))
|
.update(Some(new java.sql.Timestamp(System.currentTimeMillis())))
|
||||||
|
@ -26,11 +26,15 @@ case class UserStateDbModel(
|
|||||||
pinned: Boolean = false,
|
pinned: Boolean = false,
|
||||||
locked: Boolean = false,
|
locked: Boolean = false,
|
||||||
speechLocale: String,
|
speechLocale: String,
|
||||||
|
inactivityWarningDisplay: Boolean = false,
|
||||||
|
inactivityWarningTimeoutSecs: Option[Long],
|
||||||
)
|
)
|
||||||
|
|
||||||
class UserStateDbTableDef(tag: Tag) extends Table[UserStateDbModel](tag, None, "user") {
|
class UserStateDbTableDef(tag: Tag) extends Table[UserStateDbModel](tag, None, "user") {
|
||||||
override def * = (
|
override def * = (
|
||||||
userId,emoji,away,raiseHand,guestStatus,guestStatusSetByModerator,guestLobbyMessage,mobile,clientType,disconnected,expired,ejected,ejectReason,ejectReasonCode,ejectedByModerator,presenter,pinned,locked,speechLocale) <> (UserStateDbModel.tupled, UserStateDbModel.unapply)
|
userId,emoji,away,raiseHand,guestStatus,guestStatusSetByModerator,guestLobbyMessage,mobile,clientType,disconnected,
|
||||||
|
expired,ejected,ejectReason,ejectReasonCode,ejectedByModerator,presenter,pinned,locked,speechLocale,
|
||||||
|
inactivityWarningDisplay, inactivityWarningTimeoutSecs) <> (UserStateDbModel.tupled, UserStateDbModel.unapply)
|
||||||
val userId = column[String]("userId", O.PrimaryKey)
|
val userId = column[String]("userId", O.PrimaryKey)
|
||||||
val emoji = column[String]("emoji")
|
val emoji = column[String]("emoji")
|
||||||
val away = column[Boolean]("away")
|
val away = column[Boolean]("away")
|
||||||
@ -50,6 +54,8 @@ class UserStateDbTableDef(tag: Tag) extends Table[UserStateDbModel](tag, None, "
|
|||||||
val pinned = column[Boolean]("pinned")
|
val pinned = column[Boolean]("pinned")
|
||||||
val locked = column[Boolean]("locked")
|
val locked = column[Boolean]("locked")
|
||||||
val speechLocale = column[String]("speechLocale")
|
val speechLocale = column[String]("speechLocale")
|
||||||
|
val inactivityWarningDisplay = column[Boolean]("inactivityWarningDisplay")
|
||||||
|
val inactivityWarningTimeoutSecs = column[Option[Long]]("inactivityWarningTimeoutSecs")
|
||||||
}
|
}
|
||||||
|
|
||||||
object UserStateDAO {
|
object UserStateDAO {
|
||||||
@ -119,4 +125,21 @@ object UserStateDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def updateInactivityWarning(intId: String, inactivityWarningDisplay: Boolean, inactivityWarningTimeoutSecs: Long) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[UserStateDbTableDef]
|
||||||
|
.filter(_.userId === intId)
|
||||||
|
.map(u => (u.inactivityWarningDisplay, u.inactivityWarningTimeoutSecs))
|
||||||
|
.update((inactivityWarningDisplay,
|
||||||
|
inactivityWarningTimeoutSecs match {
|
||||||
|
case 0 => None
|
||||||
|
case timeout: Long => Some(timeout)
|
||||||
|
case _ => None
|
||||||
|
}))
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated inactivityWarningDisplay on user table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.error(s"Error updating inactivityWarningDisplay user: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package org.bigbluebutton.core.db
|
||||||
|
|
||||||
|
import org.bigbluebutton.core.models.{ VoiceUserState }
|
||||||
|
import slick.jdbc.PostgresProfile.api._
|
||||||
|
|
||||||
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
import scala.util.{ Failure, Success }
|
||||||
|
|
||||||
|
case class UserTranscriptionErrorDbModel(
|
||||||
|
userId: String,
|
||||||
|
meetingId: String,
|
||||||
|
errorCode: String,
|
||||||
|
errorMessage: String,
|
||||||
|
lastUpdatedAt: java.sql.Timestamp = new java.sql.Timestamp(System.currentTimeMillis())
|
||||||
|
)
|
||||||
|
|
||||||
|
class UserTranscriptionErrorDbTableDef(tag: Tag) extends Table[UserTranscriptionErrorDbModel](tag, None, "user_transcriptionError") {
|
||||||
|
override def * = (
|
||||||
|
userId, meetingId, errorCode, errorMessage, lastUpdatedAt
|
||||||
|
) <> (UserTranscriptionErrorDbModel.tupled, UserTranscriptionErrorDbModel.unapply)
|
||||||
|
val userId = column[String]("userId", O.PrimaryKey)
|
||||||
|
val meetingId = column[String]("meetingId")
|
||||||
|
val errorCode = column[String]("errorCode")
|
||||||
|
val errorMessage = column[String]("errorMessage")
|
||||||
|
val lastUpdatedAt = column[java.sql.Timestamp]("lastUpdatedAt")
|
||||||
|
}
|
||||||
|
|
||||||
|
object UserTranscriptionErrorDAO {
|
||||||
|
def insert(userId: String, meetingId: String, errorCode: String, errorMessage: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[UserTranscriptionErrorDbTableDef].insertOrUpdate(
|
||||||
|
UserTranscriptionErrorDbModel(
|
||||||
|
userId = userId,
|
||||||
|
meetingId = meetingId,
|
||||||
|
errorCode = errorCode,
|
||||||
|
errorMessage = errorMessage,
|
||||||
|
lastUpdatedAt = new java.sql.Timestamp(System.currentTimeMillis()),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => {
|
||||||
|
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on user_transcriptionError table!")
|
||||||
|
}
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting user_transcriptionError: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -42,4 +42,5 @@ object MeetingEndReason {
|
|||||||
val BREAKOUT_ENDED_BY_MOD = "BREAKOUT_ENDED_BY_MOD"
|
val BREAKOUT_ENDED_BY_MOD = "BREAKOUT_ENDED_BY_MOD"
|
||||||
val ENDED_DUE_TO_NO_AUTHED_USER = "ENDED_DUE_TO_NO_AUTHED_USER"
|
val ENDED_DUE_TO_NO_AUTHED_USER = "ENDED_DUE_TO_NO_AUTHED_USER"
|
||||||
val ENDED_DUE_TO_NO_MODERATOR = "ENDED_DUE_TO_NO_MODERATOR"
|
val ENDED_DUE_TO_NO_MODERATOR = "ENDED_DUE_TO_NO_MODERATOR"
|
||||||
|
val ENDED_DUE_TO_SERVICE_INTERRUPTION = "ENDED_DUE_TO_SERVICE_INTERRUPTION"
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,10 @@ import org.bigbluebutton.SystemConfiguration
|
|||||||
object AudioCaptions extends SystemConfiguration {
|
object AudioCaptions extends SystemConfiguration {
|
||||||
def setFloor(audioCaptions: AudioCaptions, userId: String) = audioCaptions.floor = userId
|
def setFloor(audioCaptions: AudioCaptions, userId: String) = audioCaptions.floor = userId
|
||||||
|
|
||||||
def isFloor(audioCaptions: AudioCaptions, userId: String) = audioCaptions.floor == userId
|
def isFloor(audioCaptions: AudioCaptions, userId: String) = true
|
||||||
|
|
||||||
def parseTranscript(transcript: String): String = {
|
def parseTranscript(transcript: String): String = {
|
||||||
val words = transcript.split("\\s+") // Split on whitespaces
|
transcript
|
||||||
val lines = words.grouped(transcriptWords).toArray // Group each X words into lines
|
|
||||||
lines.takeRight(transcriptLines).map(l => l.mkString(" ")).mkString("\n") // Join the last X lines
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -104,7 +104,7 @@ object Polls {
|
|||||||
} yield {
|
} yield {
|
||||||
val pageId = if (poll.id.contains("deskshare")) "deskshare" else page.id
|
val pageId = if (poll.id.contains("deskshare")) "deskshare" else page.id
|
||||||
val updatedShape = shape + ("whiteboardId" -> pageId)
|
val updatedShape = shape + ("whiteboardId" -> pageId)
|
||||||
val annotation = new AnnotationVO(poll.id, updatedShape, pageId, requesterId)
|
val annotation = new AnnotationVO(s"shape:poll-result-${poll.id}", updatedShape, pageId, requesterId)
|
||||||
annotation
|
annotation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,7 +243,6 @@ object Polls {
|
|||||||
|
|
||||||
private def handleRespondToTypedPoll(poll: SimplePollResultOutVO, requesterId: String, pollId: String, questionId: Int,
|
private def handleRespondToTypedPoll(poll: SimplePollResultOutVO, requesterId: String, pollId: String, questionId: Int,
|
||||||
answer: String, lm: LiveMeeting): Option[SimplePollResultOutVO] = {
|
answer: String, lm: LiveMeeting): Option[SimplePollResultOutVO] = {
|
||||||
|
|
||||||
addQuestionResponse(poll.id, questionId, answer, requesterId, lm.polls)
|
addQuestionResponse(poll.id, questionId, answer, requesterId, lm.polls)
|
||||||
for {
|
for {
|
||||||
updatedPoll <- getSimplePollResult(poll.id, lm.polls)
|
updatedPoll <- getSimplePollResult(poll.id, lm.polls)
|
||||||
@ -254,12 +253,13 @@ object Polls {
|
|||||||
|
|
||||||
private def pollResultToWhiteboardShape(result: SimplePollResultOutVO): scala.collection.immutable.Map[String, Object] = {
|
private def pollResultToWhiteboardShape(result: SimplePollResultOutVO): scala.collection.immutable.Map[String, Object] = {
|
||||||
val shape = new scala.collection.mutable.HashMap[String, Object]()
|
val shape = new scala.collection.mutable.HashMap[String, Object]()
|
||||||
shape += "numRespondents" -> new Integer(result.numRespondents)
|
shape += "numRespondents" -> Integer.valueOf(result.numRespondents)
|
||||||
shape += "numResponders" -> new Integer(result.numResponders)
|
shape += "numResponders" -> Integer.valueOf(result.numResponders)
|
||||||
shape += "questionType" -> result.questionType
|
shape += "questionType" -> result.questionType
|
||||||
shape += "questionText" -> result.questionText
|
shape += "questionText" -> result.questionText.getOrElse("")
|
||||||
shape += "id" -> result.id
|
shape += "id" -> s"shape:poll-result-${result.id}"
|
||||||
shape += "answers" -> result.answers
|
shape += "answers" -> result.answers
|
||||||
|
shape += "type" -> "geo"
|
||||||
shape.toMap
|
shape.toMap
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,10 +362,10 @@ object Polls {
|
|||||||
pvo
|
pvo
|
||||||
}
|
}
|
||||||
|
|
||||||
def checkUserResponded(pollId: String, userId: String, polls: Polls): Boolean = {
|
def hasUserAlreadyResponded(pollId: String, userId: String, polls: Polls): Boolean = {
|
||||||
polls.polls.get(pollId) match {
|
polls.polls.get(pollId) match {
|
||||||
case Some(p) => {
|
case Some(p) => {
|
||||||
if (p.getResponders().filter(p => p.userId == userId).length > 0) {
|
if (p.getResponders().exists(p => p.userId == userId)) {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -375,10 +375,10 @@ object Polls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def checkUserAddedQuestion(pollId: String, userId: String, polls: Polls): Boolean = {
|
def hasUserAlreadyAddedTypedAnswer(pollId: String, userId: String, polls: Polls): Boolean = {
|
||||||
polls.polls.get(pollId) match {
|
polls.polls.get(pollId) match {
|
||||||
case Some(p) => {
|
case Some(p) => {
|
||||||
if (p.getTypedPollResponders().filter(responderId => responderId == userId).length > 0) {
|
if (p.getTypedPollResponders().contains(userId)) {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -401,6 +401,17 @@ object Polls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def findAnswerWithText(pollId: String, questionId: Int, answerText: String, polls: Polls): Option[Int] = {
|
||||||
|
for {
|
||||||
|
poll <- Polls.getPoll(pollId, polls)
|
||||||
|
question <- poll.questions.find(q => q.id == questionId)
|
||||||
|
answers <- question.answers
|
||||||
|
equalAnswer <- answers.find(ans => ans.text.getOrElse("") == answerText)
|
||||||
|
} yield {
|
||||||
|
equalAnswer.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def showPollResult(pollId: String, polls: Polls) {
|
def showPollResult(pollId: String, polls: Polls) {
|
||||||
polls.get(pollId) foreach {
|
polls.get(pollId) foreach {
|
||||||
p =>
|
p =>
|
||||||
|
@ -176,8 +176,8 @@ case class PresentationPod(id: String, currentPresenter: String,
|
|||||||
// 100D-checkedWidth is the maximum the page can be moved over
|
// 100D-checkedWidth is the maximum the page can be moved over
|
||||||
val checkedWidth = Math.min(widthRatio, 100D) //if (widthRatio <= 100D) widthRatio else 100D
|
val checkedWidth = Math.min(widthRatio, 100D) //if (widthRatio <= 100D) widthRatio else 100D
|
||||||
val checkedHeight = Math.min(heightRatio, 100D)
|
val checkedHeight = Math.min(heightRatio, 100D)
|
||||||
val checkedXOffset = Math.min(xOffset, 0D)
|
val checkedXOffset = xOffset
|
||||||
val checkedYOffset = Math.min(yOffset, 0D)
|
val checkedYOffset = yOffset
|
||||||
|
|
||||||
for {
|
for {
|
||||||
pres <- presentations.get(presentationId)
|
pres <- presentations.get(presentationId)
|
||||||
|
@ -91,7 +91,7 @@ object RegisteredUsers {
|
|||||||
// will fail and can't join.
|
// will fail and can't join.
|
||||||
// ralam april 21, 2020
|
// ralam april 21, 2020
|
||||||
val bannedUser = user.copy(banned = true)
|
val bannedUser = user.copy(banned = true)
|
||||||
//UserDAO.insert(meetingId, bannedUser)
|
UserDAO.insert(meetingId, bannedUser)
|
||||||
users.save(bannedUser)
|
users.save(bannedUser)
|
||||||
} else {
|
} else {
|
||||||
// If user hasn't been ejected, we allow user to join
|
// If user hasn't been ejected, we allow user to join
|
||||||
@ -122,7 +122,7 @@ object RegisteredUsers {
|
|||||||
u
|
u
|
||||||
} else {
|
} else {
|
||||||
users.delete(ejectedUser.id)
|
users.delete(ejectedUser.id)
|
||||||
// UserDAO.delete(ejectedUser) it's being removed in User2x already
|
// UserDAO.softDelete(ejectedUser) it's being removed in User2x already
|
||||||
ejectedUser
|
ejectedUser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ object Users2x {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def remove(users: Users2x, intId: String): Option[UserState] = {
|
def remove(users: Users2x, intId: String): Option[UserState] = {
|
||||||
//UserDAO.delete(intId)
|
//UserDAO.softDelete(intId)
|
||||||
users.remove(intId)
|
users.remove(intId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,15 +78,6 @@ object Users2x {
|
|||||||
users.toVector.filter(u => !u.presenter)
|
users.toVector.filter(u => !u.presenter)
|
||||||
}
|
}
|
||||||
|
|
||||||
def getRandomlyPickableUsers(users: Users2x, reduceDup: Boolean): Vector[UserState] = {
|
|
||||||
|
|
||||||
if (reduceDup) {
|
|
||||||
users.toVector.filter(u => !u.presenter && u.role != Roles.MODERATOR_ROLE && !u.userLeftFlag.left && !u.pickExempted)
|
|
||||||
} else {
|
|
||||||
users.toVector.filter(u => !u.presenter && u.role != Roles.MODERATOR_ROLE && !u.userLeftFlag.left)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def findViewers(users: Users2x): Vector[UserState] = {
|
def findViewers(users: Users2x): Vector[UserState] = {
|
||||||
users.toVector.filter(u => u.role == Roles.VIEWER_ROLE)
|
users.toVector.filter(u => u.role == Roles.VIEWER_ROLE)
|
||||||
}
|
}
|
||||||
@ -98,6 +89,19 @@ object Users2x {
|
|||||||
def updateLastUserActivity(users: Users2x, u: UserState): UserState = {
|
def updateLastUserActivity(users: Users2x, u: UserState): UserState = {
|
||||||
val newUserState = modify(u)(_.lastActivityTime).setTo(System.currentTimeMillis())
|
val newUserState = modify(u)(_.lastActivityTime).setTo(System.currentTimeMillis())
|
||||||
users.save(newUserState)
|
users.save(newUserState)
|
||||||
|
|
||||||
|
//Reset inactivity warning
|
||||||
|
if (u.lastInactivityInspect != 0) {
|
||||||
|
resetLastInactivityInspect(users, newUserState)
|
||||||
|
} else {
|
||||||
|
newUserState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def resetLastInactivityInspect(users: Users2x, u: UserState): UserState = {
|
||||||
|
val newUserState = modify(u)(_.lastInactivityInspect).setTo(0)
|
||||||
|
users.save(newUserState)
|
||||||
|
UserStateDAO.updateInactivityWarning(u.intId, inactivityWarningDisplay = false, 0)
|
||||||
newUserState
|
newUserState
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +129,7 @@ object Users2x {
|
|||||||
_ <- users.remove(intId)
|
_ <- users.remove(intId)
|
||||||
ejectedUser <- users.removeFromCache(intId)
|
ejectedUser <- users.removeFromCache(intId)
|
||||||
} yield {
|
} yield {
|
||||||
// UserDAO.delete(intId) --it will keep the user on Db
|
// UserDAO.softDelete(intId) --it will keep the user on Db
|
||||||
ejectedUser
|
ejectedUser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -241,16 +245,6 @@ object Users2x {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def setUserExempted(users: Users2x, intId: String, exempted: Boolean): Option[UserState] = {
|
|
||||||
for {
|
|
||||||
u <- findWithIntId(users, intId)
|
|
||||||
} yield {
|
|
||||||
val newUser = u.modify(_.pickExempted).setTo(exempted)
|
|
||||||
users.save(newUser)
|
|
||||||
newUser
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def setUserSpeechLocale(users: Users2x, intId: String, locale: String): Option[UserState] = {
|
def setUserSpeechLocale(users: Users2x, intId: String, locale: String): Option[UserState] = {
|
||||||
for {
|
for {
|
||||||
u <- findWithIntId(users, intId)
|
u <- findWithIntId(users, intId)
|
||||||
@ -435,7 +429,6 @@ case class UserState(
|
|||||||
lastActivityTime: Long = System.currentTimeMillis(),
|
lastActivityTime: Long = System.currentTimeMillis(),
|
||||||
lastInactivityInspect: Long = 0,
|
lastInactivityInspect: Long = 0,
|
||||||
clientType: String,
|
clientType: String,
|
||||||
pickExempted: Boolean,
|
|
||||||
userLeftFlag: UserLeftFlag,
|
userLeftFlag: UserLeftFlag,
|
||||||
speechLocale: String = ""
|
speechLocale: String = ""
|
||||||
)
|
)
|
||||||
|
@ -111,10 +111,14 @@ class ReceivedJsonMsgHandlerActor(
|
|||||||
routeGenericMsg[ChangeUserPinStateReqMsg](envelope, jsonNode)
|
routeGenericMsg[ChangeUserPinStateReqMsg](envelope, jsonNode)
|
||||||
case ChangeUserMobileFlagReqMsg.NAME =>
|
case ChangeUserMobileFlagReqMsg.NAME =>
|
||||||
routeGenericMsg[ChangeUserMobileFlagReqMsg](envelope, jsonNode)
|
routeGenericMsg[ChangeUserMobileFlagReqMsg](envelope, jsonNode)
|
||||||
|
case UserConnectionAliveReqMsg.NAME =>
|
||||||
|
routeGenericMsg[UserConnectionAliveReqMsg](envelope, jsonNode)
|
||||||
|
case UserConnectionUpdateRttReqMsg.NAME =>
|
||||||
|
routeGenericMsg[UserConnectionUpdateRttReqMsg](envelope, jsonNode)
|
||||||
case SetUserSpeechLocaleReqMsg.NAME =>
|
case SetUserSpeechLocaleReqMsg.NAME =>
|
||||||
routeGenericMsg[SetUserSpeechLocaleReqMsg](envelope, jsonNode)
|
routeGenericMsg[SetUserSpeechLocaleReqMsg](envelope, jsonNode)
|
||||||
case SelectRandomViewerReqMsg.NAME =>
|
case SetUserSpeechOptionsReqMsg.NAME =>
|
||||||
routeGenericMsg[SelectRandomViewerReqMsg](envelope, jsonNode)
|
routeGenericMsg[SetUserSpeechOptionsReqMsg](envelope, jsonNode)
|
||||||
|
|
||||||
// Poll
|
// Poll
|
||||||
case StartCustomPollReqMsg.NAME =>
|
case StartCustomPollReqMsg.NAME =>
|
||||||
@ -404,6 +408,8 @@ class ReceivedJsonMsgHandlerActor(
|
|||||||
// AudioCaptions
|
// AudioCaptions
|
||||||
case UpdateTranscriptPubMsg.NAME =>
|
case UpdateTranscriptPubMsg.NAME =>
|
||||||
routeGenericMsg[UpdateTranscriptPubMsg](envelope, jsonNode)
|
routeGenericMsg[UpdateTranscriptPubMsg](envelope, jsonNode)
|
||||||
|
case TranscriptionProviderErrorMsg.NAME =>
|
||||||
|
routeGenericMsg[TranscriptionProviderErrorMsg](envelope, jsonNode)
|
||||||
|
|
||||||
// GroupChats
|
// GroupChats
|
||||||
case GetGroupChatsReqMsg.NAME =>
|
case GetGroupChatsReqMsg.NAME =>
|
||||||
@ -416,8 +422,14 @@ class ReceivedJsonMsgHandlerActor(
|
|||||||
routeGenericMsg[CreateGroupChatReqMsg](envelope, jsonNode)
|
routeGenericMsg[CreateGroupChatReqMsg](envelope, jsonNode)
|
||||||
|
|
||||||
//Plugin
|
//Plugin
|
||||||
case DispatchPluginDataChannelMessageMsg.NAME =>
|
case PluginDataChannelDispatchMessageMsg.NAME =>
|
||||||
routeGenericMsg[DispatchPluginDataChannelMessageMsg](envelope, jsonNode)
|
routeGenericMsg[PluginDataChannelDispatchMessageMsg](envelope, jsonNode)
|
||||||
|
|
||||||
|
case PluginDataChannelDeleteMessageMsg.NAME =>
|
||||||
|
routeGenericMsg[PluginDataChannelDeleteMessageMsg](envelope, jsonNode)
|
||||||
|
|
||||||
|
case PluginDataChannelResetMsg.NAME =>
|
||||||
|
routeGenericMsg[PluginDataChannelResetMsg](envelope, jsonNode)
|
||||||
|
|
||||||
// ExternalVideo
|
// ExternalVideo
|
||||||
case StartExternalVideoPubMsg.NAME =>
|
case StartExternalVideoPubMsg.NAME =>
|
||||||
@ -456,6 +468,9 @@ class ReceivedJsonMsgHandlerActor(
|
|||||||
case UserGraphqlConnectionClosedSysMsg.NAME =>
|
case UserGraphqlConnectionClosedSysMsg.NAME =>
|
||||||
route[UserGraphqlConnectionClosedSysMsg](meetingManagerChannel, envelope, jsonNode)
|
route[UserGraphqlConnectionClosedSysMsg](meetingManagerChannel, envelope, jsonNode)
|
||||||
|
|
||||||
|
case CheckGraphqlMiddlewareAlivePongSysMsg.NAME =>
|
||||||
|
route[CheckGraphqlMiddlewareAlivePongSysMsg](meetingManagerChannel, envelope, jsonNode)
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
log.error("Cannot route envelope name " + envelope.name)
|
log.error("Cannot route envelope name " + envelope.name)
|
||||||
// do nothing
|
// do nothing
|
||||||
|
@ -27,6 +27,10 @@ class StoreExportJobInRedisPresAnnEvent extends AbstractPresentationWithAnnotati
|
|||||||
|
|
||||||
setEvent("StoreExportJobInRedisPresAnnEvent")
|
setEvent("StoreExportJobInRedisPresAnnEvent")
|
||||||
|
|
||||||
|
def setserverSideFilename(serverSideFilename: String) {
|
||||||
|
eventMap.put(SERVER_SIDE_FILENAME, serverSideFilename)
|
||||||
|
}
|
||||||
|
|
||||||
def setJobId(jobId: String) {
|
def setJobId(jobId: String) {
|
||||||
eventMap.put(JOB_ID, jobId)
|
eventMap.put(JOB_ID, jobId)
|
||||||
}
|
}
|
||||||
@ -68,6 +72,7 @@ object StoreExportJobInRedisPresAnnEvent {
|
|||||||
protected final val JOB_ID = "jobId"
|
protected final val JOB_ID = "jobId"
|
||||||
protected final val JOB_TYPE = "jobType"
|
protected final val JOB_TYPE = "jobType"
|
||||||
protected final val FILENAME = "filename"
|
protected final val FILENAME = "filename"
|
||||||
|
protected final val SERVER_SIDE_FILENAME = "serverSideFilename"
|
||||||
protected final val PRES_ID = "presId"
|
protected final val PRES_ID = "presId"
|
||||||
protected final val PRES_LOCATION = "presLocation"
|
protected final val PRES_LOCATION = "presLocation"
|
||||||
protected final val ALL_PAGES = "allPages"
|
protected final val ALL_PAGES = "allPages"
|
||||||
|
@ -7,7 +7,7 @@ import org.bigbluebutton.core.apps.groupchats.GroupChatApp
|
|||||||
import org.bigbluebutton.core.apps.users.UsersApp
|
import org.bigbluebutton.core.apps.users.UsersApp
|
||||||
import org.bigbluebutton.core.apps.voice.VoiceApp
|
import org.bigbluebutton.core.apps.voice.VoiceApp
|
||||||
import org.bigbluebutton.core.bus.{BigBlueButtonEvent, InternalEventBus}
|
import org.bigbluebutton.core.bus.{BigBlueButtonEvent, InternalEventBus}
|
||||||
import org.bigbluebutton.core.db.{BreakoutRoomUserDAO, MeetingRecordingDAO, UserBreakoutRoomDAO}
|
import org.bigbluebutton.core.db.{BreakoutRoomUserDAO, MeetingDAO, MeetingRecordingDAO, UserBreakoutRoomDAO}
|
||||||
import org.bigbluebutton.core.domain.{MeetingEndReason, MeetingState2x}
|
import org.bigbluebutton.core.domain.{MeetingEndReason, MeetingState2x}
|
||||||
import org.bigbluebutton.core.models._
|
import org.bigbluebutton.core.models._
|
||||||
import org.bigbluebutton.core2.MeetingStatus2x
|
import org.bigbluebutton.core2.MeetingStatus2x
|
||||||
@ -73,7 +73,6 @@ trait HandlerHelpers extends SystemConfiguration {
|
|||||||
avatar = regUser.avatarURL,
|
avatar = regUser.avatarURL,
|
||||||
color = regUser.color,
|
color = regUser.color,
|
||||||
clientType = clientType,
|
clientType = clientType,
|
||||||
pickExempted = false,
|
|
||||||
userLeftFlag = UserLeftFlag(false, 0)
|
userLeftFlag = UserLeftFlag(false, 0)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -206,6 +205,8 @@ trait HandlerHelpers extends SystemConfiguration {
|
|||||||
|
|
||||||
val endedEvnt = buildMeetingEndedEvtMsg(liveMeeting.props.meetingProp.intId)
|
val endedEvnt = buildMeetingEndedEvtMsg(liveMeeting.props.meetingProp.intId)
|
||||||
outGW.send(endedEvnt)
|
outGW.send(endedEvnt)
|
||||||
|
|
||||||
|
MeetingDAO.setMeetingEnded(liveMeeting.props.meetingProp.intId, reason, userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
def destroyMeeting(eventBus: InternalEventBus, meetingId: String): Unit = {
|
def destroyMeeting(eventBus: InternalEventBus, meetingId: String): Unit = {
|
||||||
|
@ -76,6 +76,7 @@ class MeetingActor(
|
|||||||
|
|
||||||
with UserJoinMeetingReqMsgHdlr
|
with UserJoinMeetingReqMsgHdlr
|
||||||
with UserJoinMeetingAfterReconnectReqMsgHdlr
|
with UserJoinMeetingAfterReconnectReqMsgHdlr
|
||||||
|
with UserEstablishedGraphqlConnectionInternalMsgHdlr
|
||||||
with UserConnectedToGlobalAudioMsgHdlr
|
with UserConnectedToGlobalAudioMsgHdlr
|
||||||
with UserDisconnectedFromGlobalAudioMsgHdlr
|
with UserDisconnectedFromGlobalAudioMsgHdlr
|
||||||
with MuteAllExceptPresentersCmdMsgHdlr
|
with MuteAllExceptPresentersCmdMsgHdlr
|
||||||
@ -266,6 +267,12 @@ class MeetingActor(
|
|||||||
// internal messages
|
// internal messages
|
||||||
case msg: MonitorNumberOfUsersInternalMsg => handleMonitorNumberOfUsers(msg)
|
case msg: MonitorNumberOfUsersInternalMsg => handleMonitorNumberOfUsers(msg)
|
||||||
case msg: SetPresenterInDefaultPodInternalMsg => state = presentationPodsApp.handleSetPresenterInDefaultPodInternalMsg(msg, state, liveMeeting, msgBus)
|
case msg: SetPresenterInDefaultPodInternalMsg => state = presentationPodsApp.handleSetPresenterInDefaultPodInternalMsg(msg, state, liveMeeting, msgBus)
|
||||||
|
case msg: UserClosedAllGraphqlConnectionsInternalMsg =>
|
||||||
|
state = handleUserClosedAllGraphqlConnectionsInternalMsg(msg, state)
|
||||||
|
updateModeratorsPresence()
|
||||||
|
case msg: UserEstablishedGraphqlConnectionInternalMsg =>
|
||||||
|
state = handleUserEstablishedGraphqlConnectionInternalMsg(msg, state)
|
||||||
|
updateModeratorsPresence()
|
||||||
|
|
||||||
case msg: ExtendMeetingDuration => handleExtendMeetingDuration(msg)
|
case msg: ExtendMeetingDuration => handleExtendMeetingDuration(msg)
|
||||||
case msg: SendTimeRemainingAuditInternalMsg =>
|
case msg: SendTimeRemainingAuditInternalMsg =>
|
||||||
@ -395,10 +402,12 @@ class MeetingActor(
|
|||||||
case m: UserReactionTimeExpiredCmdMsg => handleUserReactionTimeExpiredCmdMsg(m)
|
case m: UserReactionTimeExpiredCmdMsg => handleUserReactionTimeExpiredCmdMsg(m)
|
||||||
case m: ClearAllUsersEmojiCmdMsg => handleClearAllUsersEmojiCmdMsg(m)
|
case m: ClearAllUsersEmojiCmdMsg => handleClearAllUsersEmojiCmdMsg(m)
|
||||||
case m: ClearAllUsersReactionCmdMsg => handleClearAllUsersReactionCmdMsg(m)
|
case m: ClearAllUsersReactionCmdMsg => handleClearAllUsersReactionCmdMsg(m)
|
||||||
case m: SelectRandomViewerReqMsg => usersApp.handleSelectRandomViewerReqMsg(m)
|
|
||||||
case m: ChangeUserPinStateReqMsg => usersApp.handleChangeUserPinStateReqMsg(m)
|
case m: ChangeUserPinStateReqMsg => usersApp.handleChangeUserPinStateReqMsg(m)
|
||||||
case m: ChangeUserMobileFlagReqMsg => usersApp.handleChangeUserMobileFlagReqMsg(m)
|
case m: ChangeUserMobileFlagReqMsg => usersApp.handleChangeUserMobileFlagReqMsg(m)
|
||||||
|
case m: UserConnectionAliveReqMsg => usersApp.handleUserConnectionAliveReqMsg(m)
|
||||||
|
case m: UserConnectionUpdateRttReqMsg => usersApp.handleUserConnectionUpdateRttReqMsg(m)
|
||||||
case m: SetUserSpeechLocaleReqMsg => usersApp.handleSetUserSpeechLocaleReqMsg(m)
|
case m: SetUserSpeechLocaleReqMsg => usersApp.handleSetUserSpeechLocaleReqMsg(m)
|
||||||
|
case m: SetUserSpeechOptionsReqMsg => usersApp.handleSetUserSpeechOptionsReqMsg(m)
|
||||||
|
|
||||||
// Client requested to eject user
|
// Client requested to eject user
|
||||||
case m: EjectUserFromMeetingCmdMsg =>
|
case m: EjectUserFromMeetingCmdMsg =>
|
||||||
@ -582,6 +591,7 @@ class MeetingActor(
|
|||||||
|
|
||||||
// AudioCaptions
|
// AudioCaptions
|
||||||
case m: UpdateTranscriptPubMsg => audioCaptionsApp2x.handle(m, liveMeeting, msgBus)
|
case m: UpdateTranscriptPubMsg => audioCaptionsApp2x.handle(m, liveMeeting, msgBus)
|
||||||
|
case m: TranscriptionProviderErrorMsg => audioCaptionsApp2x.handleTranscriptionProviderErrorMsg(m, liveMeeting, msgBus)
|
||||||
|
|
||||||
// GroupChat
|
// GroupChat
|
||||||
case m: CreateGroupChatReqMsg =>
|
case m: CreateGroupChatReqMsg =>
|
||||||
@ -594,7 +604,9 @@ class MeetingActor(
|
|||||||
updateUserLastActivity(m.body.msg.sender.id)
|
updateUserLastActivity(m.body.msg.sender.id)
|
||||||
|
|
||||||
// Plugin
|
// Plugin
|
||||||
case m: DispatchPluginDataChannelMessageMsg => pluginHdlrs.handle(m, state, liveMeeting)
|
case m: PluginDataChannelDispatchMessageMsg => pluginHdlrs.handle(m, state, liveMeeting)
|
||||||
|
case m: PluginDataChannelDeleteMessageMsg => pluginHdlrs.handle(m, state, liveMeeting)
|
||||||
|
case m: PluginDataChannelResetMsg => pluginHdlrs.handle(m, state, liveMeeting)
|
||||||
|
|
||||||
// Webcams
|
// Webcams
|
||||||
case m: UserBroadcastCamStartMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
|
case m: UserBroadcastCamStartMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
|
||||||
@ -973,6 +985,7 @@ class MeetingActor(
|
|||||||
|
|
||||||
val secsToDisconnect = TimeUnit.MILLISECONDS.toSeconds(expiryTracker.userActivitySignResponseDelayInMs);
|
val secsToDisconnect = TimeUnit.MILLISECONDS.toSeconds(expiryTracker.userActivitySignResponseDelayInMs);
|
||||||
Sender.sendUserInactivityInspectMsg(liveMeeting.props.meetingProp.intId, u.intId, secsToDisconnect, outGW)
|
Sender.sendUserInactivityInspectMsg(liveMeeting.props.meetingProp.intId, u.intId, secsToDisconnect, outGW)
|
||||||
|
UserStateDAO.updateInactivityWarning(u.intId, inactivityWarningDisplay = true, secsToDisconnect)
|
||||||
updateUserLastInactivityInspect(u.intId)
|
updateUserLastInactivityInspect(u.intId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,8 @@ class AnalyticsActor(val includeChat: Boolean) extends Actor with ActorLogging {
|
|||||||
case m: AssignPresenterReqMsg => logMessage(msg)
|
case m: AssignPresenterReqMsg => logMessage(msg)
|
||||||
case m: ChangeUserPinStateReqMsg => logMessage(msg)
|
case m: ChangeUserPinStateReqMsg => logMessage(msg)
|
||||||
case m: ChangeUserMobileFlagReqMsg => logMessage(msg)
|
case m: ChangeUserMobileFlagReqMsg => logMessage(msg)
|
||||||
|
case m: UserConnectionAliveReqMsg => logMessage(msg)
|
||||||
|
case m: UserConnectionUpdateRttReqMsg => logMessage(msg)
|
||||||
case m: ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg => logMessage(msg)
|
case m: ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg => logMessage(msg)
|
||||||
case m: ScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg => logMessage(msg)
|
case m: ScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg => logMessage(msg)
|
||||||
case m: ScreenshareRtmpBroadcastStartedEvtMsg => logMessage(msg)
|
case m: ScreenshareRtmpBroadcastStartedEvtMsg => logMessage(msg)
|
||||||
|
@ -256,6 +256,16 @@ object MsgBuilder {
|
|||||||
BbbCommonEnvCoreMsg(envelope, event)
|
BbbCommonEnvCoreMsg(envelope, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def buildCheckGraphqlMiddlewareAlivePingSysMsg(middlewareUid: String): BbbCommonEnvCoreMsg = {
|
||||||
|
val routing = Routing.addMsgToClientRouting(MessageTypes.SYSTEM, "", "")
|
||||||
|
val envelope = BbbCoreEnvelope(CheckGraphqlMiddlewareAlivePingSysMsg.NAME, routing)
|
||||||
|
val header = BbbCoreHeaderWithMeetingId(CheckGraphqlMiddlewareAlivePingSysMsg.NAME, "")
|
||||||
|
val body = CheckGraphqlMiddlewareAlivePingSysMsgBody(middlewareUid)
|
||||||
|
val event = CheckGraphqlMiddlewareAlivePingSysMsg(header, body)
|
||||||
|
|
||||||
|
BbbCommonEnvCoreMsg(envelope, event)
|
||||||
|
}
|
||||||
|
|
||||||
def buildEjectAllFromVoiceConfMsg(meetingId: String, voiceConf: String): BbbCommonEnvCoreMsg = {
|
def buildEjectAllFromVoiceConfMsg(meetingId: String, voiceConf: String): BbbCommonEnvCoreMsg = {
|
||||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||||
val envelope = BbbCoreEnvelope(EjectAllFromVoiceConfMsg.NAME, routing)
|
val envelope = BbbCoreEnvelope(EjectAllFromVoiceConfMsg.NAME, routing)
|
||||||
|
@ -69,8 +69,7 @@ trait FakeTestData {
|
|||||||
UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role, pin = false,
|
UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role, pin = false,
|
||||||
mobile = false, guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
|
mobile = false, guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
|
||||||
emoji = "none", reactionEmoji = "none", raiseHand = false, away = false, locked = false, presenter = false,
|
emoji = "none", reactionEmoji = "none", raiseHand = false, away = false, locked = false, presenter = false,
|
||||||
avatar = regUser.avatarURL, color = "#ff6242", clientType = "unknown",
|
avatar = regUser.avatarURL, color = "#ff6242", clientType = "unknown", userLeftFlag = UserLeftFlag(false, 0))
|
||||||
pickExempted = false, userLeftFlag = UserLeftFlag(false, 0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,7 @@ class ExportAnnotationsActor(
|
|||||||
private def handleStoreExportJobInRedisSysMsg(msg: StoreExportJobInRedisSysMsg) {
|
private def handleStoreExportJobInRedisSysMsg(msg: StoreExportJobInRedisSysMsg) {
|
||||||
val ev = new StoreExportJobInRedisPresAnnEvent()
|
val ev = new StoreExportJobInRedisPresAnnEvent()
|
||||||
|
|
||||||
|
ev.setserverSideFilename(msg.body.exportJob.serverSideFilename)
|
||||||
ev.setJobId(msg.body.exportJob.jobId)
|
ev.setJobId(msg.body.exportJob.jobId)
|
||||||
ev.setJobType(msg.body.exportJob.jobType)
|
ev.setJobType(msg.body.exportJob.jobType)
|
||||||
ev.setFilename(msg.body.exportJob.filename)
|
ev.setFilename(msg.body.exportJob.filename)
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
package org.bigbluebutton.endpoint.redis
|
|
||||||
|
|
||||||
import org.apache.pekko.actor.{Actor, ActorLogging, ActorSystem, Props}
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
|
||||||
import org.bigbluebutton.core.db.UserGraphqlConnectionDAO
|
|
||||||
|
|
||||||
object GraphqlActionsActor {
|
|
||||||
def props(system: ActorSystem): Props =
|
|
||||||
Props(
|
|
||||||
classOf[GraphqlActionsActor],
|
|
||||||
system,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
class GraphqlActionsActor(
|
|
||||||
system: ActorSystem,
|
|
||||||
) extends Actor with ActorLogging {
|
|
||||||
|
|
||||||
def receive = {
|
|
||||||
//=============================
|
|
||||||
// 2x messages
|
|
||||||
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
|
|
||||||
case _ => // do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
|
|
||||||
msg.core match {
|
|
||||||
// Messages from bbb-graphql-middleware
|
|
||||||
case m: UserGraphqlConnectionEstablishedSysMsg => handleUserGraphqlConnectionEstablishedSysMsg(m)
|
|
||||||
case m: UserGraphqlConnectionClosedSysMsg => handleUserGraphqlConnectionClosedSysMsg(m)
|
|
||||||
case _ => // message not to be handled.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def handleUserGraphqlConnectionEstablishedSysMsg(msg: UserGraphqlConnectionEstablishedSysMsg) {
|
|
||||||
UserGraphqlConnectionDAO.insert(msg.body.sessionToken, msg.body.browserConnectionId)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def handleUserGraphqlConnectionClosedSysMsg(msg: UserGraphqlConnectionClosedSysMsg) {
|
|
||||||
UserGraphqlConnectionDAO.updateClosed(msg.body.sessionToken, msg.body.browserConnectionId)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,171 @@
|
|||||||
|
package org.bigbluebutton.endpoint.redis
|
||||||
|
|
||||||
|
import org.apache.pekko.actor.{Actor, ActorLogging, ActorSystem, Props}
|
||||||
|
import org.bigbluebutton.common2.msgs._
|
||||||
|
import org.bigbluebutton.core.OutMessageGateway
|
||||||
|
import org.bigbluebutton.core.api.{UserClosedAllGraphqlConnectionsInternalMsg, UserEstablishedGraphqlConnectionInternalMsg}
|
||||||
|
import org.bigbluebutton.core.bus.{BigBlueButtonEvent, InternalEventBus}
|
||||||
|
import org.bigbluebutton.core.db.UserGraphqlConnectionDAO
|
||||||
|
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||||
|
|
||||||
|
import scala.concurrent.ExecutionContext
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
import ExecutionContext.Implicits.global
|
||||||
|
|
||||||
|
case object MiddlewareHealthCheckScheduler10Sec
|
||||||
|
|
||||||
|
object GraphqlConnectionsActor {
|
||||||
|
def props(system: ActorSystem,
|
||||||
|
eventBus: InternalEventBus,
|
||||||
|
outGW: OutMessageGateway,
|
||||||
|
): Props =
|
||||||
|
Props(
|
||||||
|
classOf[GraphqlConnectionsActor],
|
||||||
|
system,
|
||||||
|
eventBus,
|
||||||
|
outGW,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
case class GraphqlUser(
|
||||||
|
intId: String,
|
||||||
|
meetingId: String,
|
||||||
|
sessionToken: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
case class GraphqlUserConnection(
|
||||||
|
middlewareUID: String,
|
||||||
|
browserConnectionId: String,
|
||||||
|
sessionToken: String,
|
||||||
|
user: GraphqlUser,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GraphqlConnectionsActor(
|
||||||
|
system: ActorSystem,
|
||||||
|
val eventBus: InternalEventBus,
|
||||||
|
val outGW: OutMessageGateway,
|
||||||
|
) extends Actor with ActorLogging {
|
||||||
|
|
||||||
|
private var users: Map[String, GraphqlUser] = Map()
|
||||||
|
private var graphqlConnections: Map[String, GraphqlUserConnection] = Map()
|
||||||
|
private var pendingResponseMiddlewareUIDs: Map[String, BigInt] = Map()
|
||||||
|
|
||||||
|
system.scheduler.schedule(10.seconds, 10.seconds, self, MiddlewareHealthCheckScheduler10Sec)
|
||||||
|
private val maxMiddlewareInactivityInMillis = 11000
|
||||||
|
|
||||||
|
def receive = {
|
||||||
|
//=============================
|
||||||
|
// 2x messages
|
||||||
|
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
|
||||||
|
case MiddlewareHealthCheckScheduler10Sec => runMiddlewareHealthCheck()
|
||||||
|
case _ => // do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
|
||||||
|
msg.core match {
|
||||||
|
case m: RegisterUserReqMsg => handleUserRegisteredRespMsg(m)
|
||||||
|
case m: DestroyMeetingSysCmdMsg => handleDestroyMeetingSysCmdMsg(m)
|
||||||
|
// Messages from bbb-graphql-middleware
|
||||||
|
case m: UserGraphqlConnectionEstablishedSysMsg => handleUserGraphqlConnectionEstablishedSysMsg(m)
|
||||||
|
case m: UserGraphqlConnectionClosedSysMsg => handleUserGraphqlConnectionClosedSysMsg(m)
|
||||||
|
case m: CheckGraphqlMiddlewareAlivePongSysMsg => handleCheckGraphqlMiddlewareAlivePongSysMsg(m)
|
||||||
|
case _ => // message not to be handled.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleUserRegisteredRespMsg(msg: RegisterUserReqMsg): Unit = {
|
||||||
|
users += (msg.body.sessionToken -> GraphqlUser(
|
||||||
|
msg.body.intUserId,
|
||||||
|
msg.body.meetingId,
|
||||||
|
msg.body.sessionToken
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleDestroyMeetingSysCmdMsg(msg: DestroyMeetingSysCmdMsg): Unit = {
|
||||||
|
users = users.filter(u => u._2.meetingId != msg.body.meetingId)
|
||||||
|
graphqlConnections = graphqlConnections.filter(c => c._2.user.meetingId != msg.body.meetingId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleUserGraphqlConnectionEstablishedSysMsg(msg: UserGraphqlConnectionEstablishedSysMsg): Unit = {
|
||||||
|
UserGraphqlConnectionDAO.insert(msg.body.sessionToken, msg.body.middlewareUID, msg.body.browserConnectionId)
|
||||||
|
|
||||||
|
for {
|
||||||
|
user <- users.get(msg.body.sessionToken)
|
||||||
|
} yield {
|
||||||
|
|
||||||
|
//Send internal message informing user has connected
|
||||||
|
if (!graphqlConnections.values.exists(c => c.sessionToken == msg.body.sessionToken)) {
|
||||||
|
eventBus.publish(BigBlueButtonEvent(user.meetingId, UserEstablishedGraphqlConnectionInternalMsg(user.intId)))
|
||||||
|
}
|
||||||
|
|
||||||
|
graphqlConnections += (msg.body.browserConnectionId -> GraphqlUserConnection(
|
||||||
|
msg.body.middlewareUID,
|
||||||
|
msg.body.browserConnectionId,
|
||||||
|
msg.body.sessionToken,
|
||||||
|
user
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleUserGraphqlConnectionClosedSysMsg(msg: UserGraphqlConnectionClosedSysMsg): Unit = {
|
||||||
|
handleUserGraphqlConnectionClosed(msg.body.sessionToken, msg.body.middlewareUID, msg.body.browserConnectionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleUserGraphqlConnectionClosed(sessionToken: String, middlewareUID: String, browserConnectionId: String): Unit = {
|
||||||
|
UserGraphqlConnectionDAO.updateClosed(sessionToken, middlewareUID, browserConnectionId)
|
||||||
|
|
||||||
|
for {
|
||||||
|
user <- users.get(sessionToken)
|
||||||
|
} yield {
|
||||||
|
graphqlConnections = graphqlConnections.-(browserConnectionId)
|
||||||
|
|
||||||
|
//Send internal message informing user disconnected
|
||||||
|
if (!graphqlConnections.values.exists(c => c.sessionToken == sessionToken)) {
|
||||||
|
eventBus.publish(BigBlueButtonEvent(user.meetingId, UserClosedAllGraphqlConnectionsInternalMsg(user.intId)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def runMiddlewareHealthCheck(): Unit = {
|
||||||
|
removeInactiveConnections()
|
||||||
|
sendPingMessageToAllMiddlewareServices()
|
||||||
|
}
|
||||||
|
|
||||||
|
private def sendPingMessageToAllMiddlewareServices(): Unit = {
|
||||||
|
graphqlConnections.map(c => {
|
||||||
|
c._2.middlewareUID
|
||||||
|
}).toVector.distinct.map(middlewareUID => {
|
||||||
|
val event = MsgBuilder.buildCheckGraphqlMiddlewareAlivePingSysMsg(middlewareUID)
|
||||||
|
outGW.send(event)
|
||||||
|
log.debug(s"Sent ping message from graphql middleware ${middlewareUID}.")
|
||||||
|
pendingResponseMiddlewareUIDs.get(middlewareUID) match {
|
||||||
|
case None => pendingResponseMiddlewareUIDs += (middlewareUID -> System.currentTimeMillis)
|
||||||
|
case _ => //Ignore
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private def removeInactiveConnections(): Unit = {
|
||||||
|
for {
|
||||||
|
(middlewareUid, pingSentAt) <- pendingResponseMiddlewareUIDs
|
||||||
|
if (System.currentTimeMillis - pingSentAt) > maxMiddlewareInactivityInMillis
|
||||||
|
} yield {
|
||||||
|
log.info("Removing connections from the middleware {} due to inactivity of the service.",middlewareUid)
|
||||||
|
for {
|
||||||
|
(_, graphqlConn) <- graphqlConnections
|
||||||
|
if graphqlConn.middlewareUID == middlewareUid
|
||||||
|
} yield {
|
||||||
|
handleUserGraphqlConnectionClosed(graphqlConn.sessionToken, graphqlConn.middlewareUID, graphqlConn.browserConnectionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingResponseMiddlewareUIDs -= middlewareUid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleCheckGraphqlMiddlewareAlivePongSysMsg(msg: CheckGraphqlMiddlewareAlivePongSysMsg): Unit = {
|
||||||
|
log.debug(s"Received pong message from graphql middleware ${msg.body.middlewareUID}.")
|
||||||
|
pendingResponseMiddlewareUIDs -= msg.body.middlewareUID
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -20,6 +20,7 @@ case class Meeting(
|
|||||||
intId: String,
|
intId: String,
|
||||||
extId: String,
|
extId: String,
|
||||||
name: String,
|
name: String,
|
||||||
|
downloadSessionDataEnabled: Boolean,
|
||||||
users: Map[String, User] = Map(),
|
users: Map[String, User] = Map(),
|
||||||
polls: Map[String, Poll] = Map(),
|
polls: Map[String, Poll] = Map(),
|
||||||
screenshares: Vector[Screenshare] = Vector(),
|
screenshares: Vector[Screenshare] = Vector(),
|
||||||
@ -585,6 +586,7 @@ class LearningDashboardActor(
|
|||||||
msg.body.props.meetingProp.intId,
|
msg.body.props.meetingProp.intId,
|
||||||
msg.body.props.meetingProp.extId,
|
msg.body.props.meetingProp.extId,
|
||||||
msg.body.props.meetingProp.name,
|
msg.body.props.meetingProp.name,
|
||||||
|
downloadSessionDataEnabled = !msg.body.props.meetingProp.disabledFeatures.contains("learningDashboardDownloadSessionData"),
|
||||||
)
|
)
|
||||||
|
|
||||||
meetings += (newMeeting.intId -> newMeeting)
|
meetings += (newMeeting.intId -> newMeeting)
|
||||||
|
@ -85,6 +85,9 @@ class RedisRecorderActor(
|
|||||||
case m: UserLeftMeetingEvtMsg => handleUserLeftMeetingEvtMsg(m)
|
case m: UserLeftMeetingEvtMsg => handleUserLeftMeetingEvtMsg(m)
|
||||||
case m: PresenterAssignedEvtMsg => handlePresenterAssignedEvtMsg(m)
|
case m: PresenterAssignedEvtMsg => handlePresenterAssignedEvtMsg(m)
|
||||||
case m: UserEmojiChangedEvtMsg => handleUserEmojiChangedEvtMsg(m)
|
case m: UserEmojiChangedEvtMsg => handleUserEmojiChangedEvtMsg(m)
|
||||||
|
case m: UserAwayChangedEvtMsg => handleUserAwayChangedEvtMsg(m)
|
||||||
|
case m: UserRaiseHandChangedEvtMsg => handleUserRaiseHandChangedEvtMsg(m)
|
||||||
|
case m: UserReactionEmojiChangedEvtMsg => handleUserReactionEmojiChangedEvtMsg(m)
|
||||||
case m: UserRoleChangedEvtMsg => handleUserRoleChangedEvtMsg(m)
|
case m: UserRoleChangedEvtMsg => handleUserRoleChangedEvtMsg(m)
|
||||||
case m: UserBroadcastCamStartedEvtMsg => handleUserBroadcastCamStartedEvtMsg(m)
|
case m: UserBroadcastCamStartedEvtMsg => handleUserBroadcastCamStartedEvtMsg(m)
|
||||||
case m: UserBroadcastCamStoppedEvtMsg => handleUserBroadcastCamStoppedEvtMsg(m)
|
case m: UserBroadcastCamStoppedEvtMsg => handleUserBroadcastCamStoppedEvtMsg(m)
|
||||||
@ -112,7 +115,7 @@ class RedisRecorderActor(
|
|||||||
//case m: DeskShareNotifyViewersRTMP => handleDeskShareNotifyViewersRTMP(m)
|
//case m: DeskShareNotifyViewersRTMP => handleDeskShareNotifyViewersRTMP(m)
|
||||||
|
|
||||||
// AudioCaptions
|
// AudioCaptions
|
||||||
case m: TranscriptUpdatedEvtMsg => handleTranscriptUpdatedEvtMsg(m)
|
//case m: TranscriptUpdatedEvtMsg => handleTranscriptUpdatedEvtMsg(m) // temporarily disabling due to issue https://github.com/bigbluebutton/bigbluebutton/issues/19701
|
||||||
|
|
||||||
// Meeting
|
// Meeting
|
||||||
case m: RecordingStatusChangedEvtMsg => handleRecordingStatusChangedEvtMsg(m)
|
case m: RecordingStatusChangedEvtMsg => handleRecordingStatusChangedEvtMsg(m)
|
||||||
@ -379,6 +382,18 @@ class RedisRecorderActor(
|
|||||||
handleUserStatusChange(msg.header.meetingId, msg.body.userId, "emojiStatus", msg.body.emoji)
|
handleUserStatusChange(msg.header.meetingId, msg.body.userId, "emojiStatus", msg.body.emoji)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def handleUserAwayChangedEvtMsg(msg: UserAwayChangedEvtMsg) {
|
||||||
|
handleUserStatusChange(msg.header.meetingId, msg.body.userId, "away", if (msg.body.away) "true" else "false")
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleUserRaiseHandChangedEvtMsg(msg: UserRaiseHandChangedEvtMsg) {
|
||||||
|
handleUserStatusChange(msg.header.meetingId, msg.body.userId, "raiseHand", if (msg.body.raiseHand) "true" else "false")
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleUserReactionEmojiChangedEvtMsg(msg: UserReactionEmojiChangedEvtMsg) {
|
||||||
|
handleUserStatusChange(msg.header.meetingId, msg.body.userId, "reactionEmoji", msg.body.reactionEmoji)
|
||||||
|
}
|
||||||
|
|
||||||
private def handleUserRoleChangedEvtMsg(msg: UserRoleChangedEvtMsg) {
|
private def handleUserRoleChangedEvtMsg(msg: UserRoleChangedEvtMsg) {
|
||||||
handleUserStatusChange(msg.header.meetingId, msg.body.userId, "role", msg.body.role)
|
handleUserStatusChange(msg.header.meetingId, msg.body.userId, "role", msg.body.role)
|
||||||
}
|
}
|
||||||
@ -521,6 +536,7 @@ class RedisRecorderActor(
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* temporarily disabling due to issue https://github.com/bigbluebutton/bigbluebutton/issues/19701
|
||||||
private def handleTranscriptUpdatedEvtMsg(msg: TranscriptUpdatedEvtMsg) {
|
private def handleTranscriptUpdatedEvtMsg(msg: TranscriptUpdatedEvtMsg) {
|
||||||
val ev = new TranscriptUpdatedRecordEvent()
|
val ev = new TranscriptUpdatedRecordEvent()
|
||||||
ev.setMeetingId(msg.header.meetingId)
|
ev.setMeetingId(msg.header.meetingId)
|
||||||
@ -529,6 +545,7 @@ class RedisRecorderActor(
|
|||||||
|
|
||||||
record(msg.header.meetingId, ev.toMap.asJava)
|
record(msg.header.meetingId, ev.toMap.asJava)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
private def handleStartExternalVideoEvtMsg(msg: StartExternalVideoEvtMsg) {
|
private def handleStartExternalVideoEvtMsg(msg: StartExternalVideoEvtMsg) {
|
||||||
val ev = new StartExternalVideoRecordEvent()
|
val ev = new StartExternalVideoRecordEvent()
|
||||||
|
@ -51,7 +51,7 @@ object TestDataGen {
|
|||||||
guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
|
guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
|
||||||
emoji = "none", reactionEmoji = "none", raiseHand = false, away = false, pin = false, mobile = false,
|
emoji = "none", reactionEmoji = "none", raiseHand = false, away = false, pin = false, mobile = false,
|
||||||
locked = false, presenter = false, avatar = regUser.avatarURL, color = "#ff6242",
|
locked = false, presenter = false, avatar = regUser.avatarURL, color = "#ff6242",
|
||||||
clientType = "unknown", pickExempted = false, userLeftFlag = UserLeftFlag(false, 0))
|
clientType = "unknown", userLeftFlag = UserLeftFlag(false, 0))
|
||||||
Users2x.add(liveMeeting.users2x, u)
|
Users2x.add(liveMeeting.users2x, u)
|
||||||
u
|
u
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ postgres {
|
|||||||
}
|
}
|
||||||
numThreads = 1
|
numThreads = 1
|
||||||
maxConnections = 1
|
maxConnections = 1
|
||||||
|
queueSize = 20000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -64,6 +65,7 @@ expire {
|
|||||||
services {
|
services {
|
||||||
bbbWebAPI = "https://192.168.23.33/bigbluebutton/api"
|
bbbWebAPI = "https://192.168.23.33/bigbluebutton/api"
|
||||||
sharedSecret = "changeme"
|
sharedSecret = "changeme"
|
||||||
|
checkSumAlgorithmForBreakouts = "sha256"
|
||||||
}
|
}
|
||||||
|
|
||||||
eventBus {
|
eventBus {
|
||||||
|
@ -14,7 +14,7 @@ object Dependencies {
|
|||||||
// Libraries
|
// Libraries
|
||||||
val pekkoVersion = "1.0.1"
|
val pekkoVersion = "1.0.1"
|
||||||
val pekkoHttpVersion = "1.0.0"
|
val pekkoHttpVersion = "1.0.0"
|
||||||
val logback = "1.2.10"
|
val logback = "1.2.13"
|
||||||
|
|
||||||
// Apache Commons
|
// Apache Commons
|
||||||
val lang = "3.12.0"
|
val lang = "3.12.0"
|
||||||
|
@ -70,6 +70,9 @@ case class LockSettingsProps(
|
|||||||
case class SystemProps(
|
case class SystemProps(
|
||||||
html5InstanceId: Int,
|
html5InstanceId: Int,
|
||||||
logoutUrl: String,
|
logoutUrl: String,
|
||||||
|
customLogoURL: String,
|
||||||
|
bannerText: String,
|
||||||
|
bannerColor: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
case class GroupProps(
|
case class GroupProps(
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
package org.bigbluebutton.common2.msgs
|
package org.bigbluebutton.common2.msgs
|
||||||
|
|
||||||
|
object TranscriptionProviderErrorMsg { val NAME = "TranscriptionProviderErrorMsg" }
|
||||||
|
case class TranscriptionProviderErrorMsg(header: BbbClientMsgHeader, body: TranscriptionProviderErrorMsgBody) extends StandardMsg
|
||||||
|
case class TranscriptionProviderErrorMsgBody(
|
||||||
|
errorCode: String,
|
||||||
|
errorMessage: String,
|
||||||
|
)
|
||||||
|
|
||||||
// In messages
|
// In messages
|
||||||
object UpdateTranscriptPubMsg { val NAME = "UpdateTranscriptPubMsg" }
|
object UpdateTranscriptPubMsg { val NAME = "UpdateTranscriptPubMsg" }
|
||||||
case class UpdateTranscriptPubMsg(header: BbbClientMsgHeader, body: UpdateTranscriptPubMsgBody) extends StandardMsg
|
case class UpdateTranscriptPubMsg(header: BbbClientMsgHeader, body: UpdateTranscriptPubMsgBody) extends StandardMsg
|
||||||
@ -14,6 +21,10 @@ case class UpdateTranscriptPubMsgBody(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Out messages
|
// Out messages
|
||||||
|
object TranscriptionProviderErrorEvtMsg { val NAME = "TranscriptionProviderErrorEvtMsg" }
|
||||||
|
case class TranscriptionProviderErrorEvtMsg(header: BbbClientMsgHeader, body: TranscriptionProviderErrorEvtMsgBody) extends BbbCoreMsg
|
||||||
|
case class TranscriptionProviderErrorEvtMsgBody(errorCode: String, errorMessage: String)
|
||||||
|
|
||||||
object TranscriptUpdatedEvtMsg { val NAME = "TranscriptUpdatedEvtMsg" }
|
object TranscriptUpdatedEvtMsg { val NAME = "TranscriptUpdatedEvtMsg" }
|
||||||
case class TranscriptUpdatedEvtMsg(header: BbbClientMsgHeader, body: TranscriptUpdatedEvtMsgBody) extends BbbCoreMsg
|
case class TranscriptUpdatedEvtMsg(header: BbbClientMsgHeader, body: TranscriptUpdatedEvtMsgBody) extends BbbCoreMsg
|
||||||
case class TranscriptUpdatedEvtMsgBody(transcriptId: String, transcript: String, locale: String, result: Boolean)
|
case class TranscriptUpdatedEvtMsgBody(transcriptId: String, transcript: String, locale: String, result: Boolean)
|
||||||
|
@ -15,7 +15,7 @@ object GroupChatMessageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case class GroupChatUser(id: String, name: String = "", role: String = "VIEWER")
|
case class GroupChatUser(id: String, name: String = "", role: String = "VIEWER")
|
||||||
case class GroupChatMsgFromUser(correlationId: String, sender: GroupChatUser, chatEmphasizedText: Boolean = false, message: String)
|
case class GroupChatMsgFromUser(correlationId: String, sender: GroupChatUser, message: String)
|
||||||
case class GroupChatMsgToUser(id: String, timestamp: Long, correlationId: String, sender: GroupChatUser, chatEmphasizedText: Boolean = false, message: String)
|
case class GroupChatMsgToUser(id: String, timestamp: Long, correlationId: String, sender: GroupChatUser, chatEmphasizedText: Boolean = false, message: String)
|
||||||
case class GroupChatInfo(id: String, access: String, createdBy: GroupChatUser, users: Vector[GroupChatUser])
|
case class GroupChatInfo(id: String, access: String, createdBy: GroupChatUser, users: Vector[GroupChatUser])
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ case class PadTailEvtMsgBody(externalId: String, tail: String)
|
|||||||
// client -> apps
|
// client -> apps
|
||||||
object PadUpdatePubMsg { val NAME = "PadUpdatePubMsg" }
|
object PadUpdatePubMsg { val NAME = "PadUpdatePubMsg" }
|
||||||
case class PadUpdatePubMsg(header: BbbClientMsgHeader, body: PadUpdatePubMsgBody) extends StandardMsg
|
case class PadUpdatePubMsg(header: BbbClientMsgHeader, body: PadUpdatePubMsgBody) extends StandardMsg
|
||||||
case class PadUpdatePubMsgBody(externalId: String, text: String)
|
case class PadUpdatePubMsgBody(externalId: String, text: String, transcript: Boolean)
|
||||||
|
|
||||||
// apps -> pads
|
// apps -> pads
|
||||||
object PadUpdateCmdMsg { val NAME = "PadUpdateCmdMsg" }
|
object PadUpdateCmdMsg { val NAME = "PadUpdateCmdMsg" }
|
||||||
|
@ -5,12 +5,28 @@ package org.bigbluebutton.common2.msgs
|
|||||||
/**
|
/**
|
||||||
* Sent from graphql-actions to bbb-akka
|
* Sent from graphql-actions to bbb-akka
|
||||||
*/
|
*/
|
||||||
object DispatchPluginDataChannelMessageMsg { val NAME = "DispatchPluginDataChannelMessageMsg" }
|
object PluginDataChannelDispatchMessageMsg { val NAME = "PluginDataChannelDispatchMessageMsg" }
|
||||||
case class DispatchPluginDataChannelMessageMsg(header: BbbClientMsgHeader, body: DispatchPluginDataChannelMessageMsgBody) extends StandardMsg
|
case class PluginDataChannelDispatchMessageMsg(header: BbbClientMsgHeader, body: PluginDataChannelDispatchMessageMsgBody) extends StandardMsg
|
||||||
case class DispatchPluginDataChannelMessageMsgBody(
|
case class PluginDataChannelDispatchMessageMsgBody(
|
||||||
pluginName: String,
|
pluginName: String,
|
||||||
dataChannel: String,
|
dataChannel: String,
|
||||||
payloadJson: String,
|
payloadJson: String,
|
||||||
toRoles: List[String],
|
toRoles: List[String],
|
||||||
toUserIds: List[String],
|
toUserIds: List[String],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
object PluginDataChannelDeleteMessageMsg { val NAME = "PluginDataChannelDeleteMessageMsg" }
|
||||||
|
case class PluginDataChannelDeleteMessageMsg(header: BbbClientMsgHeader, body: PluginDataChannelDeleteMessageMsgBody) extends StandardMsg
|
||||||
|
case class PluginDataChannelDeleteMessageMsgBody(
|
||||||
|
pluginName: String,
|
||||||
|
dataChannel: String,
|
||||||
|
messageId: String
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
object PluginDataChannelResetMsg { val NAME = "PluginDataChannelResetMsg" }
|
||||||
|
case class PluginDataChannelResetMsg(header: BbbClientMsgHeader, body: PluginDataChannelResetMsgBody) extends StandardMsg
|
||||||
|
case class PluginDataChannelResetMsgBody(
|
||||||
|
pluginName: String,
|
||||||
|
dataChannel: String
|
||||||
|
)
|
||||||
|
@ -17,7 +17,7 @@ case class MakePresentationDownloadReqMsgBody(presId: String, allPages: Boolean,
|
|||||||
object NewPresFileAvailableMsg { val NAME = "NewPresFileAvailableMsg" }
|
object NewPresFileAvailableMsg { val NAME = "NewPresFileAvailableMsg" }
|
||||||
case class NewPresFileAvailableMsg(header: BbbClientMsgHeader, body: NewPresFileAvailableMsgBody) extends StandardMsg
|
case class NewPresFileAvailableMsg(header: BbbClientMsgHeader, body: NewPresFileAvailableMsgBody) extends StandardMsg
|
||||||
case class NewPresFileAvailableMsgBody(annotatedFileURI: String, originalFileURI: String, convertedFileURI: String,
|
case class NewPresFileAvailableMsgBody(annotatedFileURI: String, originalFileURI: String, convertedFileURI: String,
|
||||||
presId: String, fileStateType: String)
|
presId: String, fileStateType: String, fileName: String)
|
||||||
|
|
||||||
object PresAnnStatusMsg { val NAME = "PresAnnStatusMsg" }
|
object PresAnnStatusMsg { val NAME = "PresAnnStatusMsg" }
|
||||||
case class PresAnnStatusMsg(header: BbbClientMsgHeader, body: PresAnnStatusMsgBody) extends StandardMsg
|
case class PresAnnStatusMsg(header: BbbClientMsgHeader, body: PresAnnStatusMsgBody) extends StandardMsg
|
||||||
|
@ -232,6 +232,26 @@ object DeletedRecordingSysMsg { val NAME = "DeletedRecordingSysMsg" }
|
|||||||
case class DeletedRecordingSysMsg(header: BbbCoreBaseHeader, body: DeletedRecordingSysMsgBody) extends BbbCoreMsg
|
case class DeletedRecordingSysMsg(header: BbbCoreBaseHeader, body: DeletedRecordingSysMsgBody) extends BbbCoreMsg
|
||||||
case class DeletedRecordingSysMsgBody(recordId: String)
|
case class DeletedRecordingSysMsgBody(recordId: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent from akka-apps to graphql-middleware
|
||||||
|
*/
|
||||||
|
object CheckGraphqlMiddlewareAlivePingSysMsg { val NAME = "CheckGraphqlMiddlewareAlivePingSysMsg" }
|
||||||
|
case class CheckGraphqlMiddlewareAlivePingSysMsg(
|
||||||
|
header: BbbCoreHeaderWithMeetingId,
|
||||||
|
body: CheckGraphqlMiddlewareAlivePingSysMsgBody
|
||||||
|
) extends BbbCoreMsg
|
||||||
|
case class CheckGraphqlMiddlewareAlivePingSysMsgBody(middlewareUID: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent from graphql-middleware to akka-apps
|
||||||
|
*/
|
||||||
|
object CheckGraphqlMiddlewareAlivePongSysMsg { val NAME = "CheckGraphqlMiddlewareAlivePongSysMsg" }
|
||||||
|
case class CheckGraphqlMiddlewareAlivePongSysMsg(
|
||||||
|
header: BbbCoreBaseHeader,
|
||||||
|
body: CheckGraphqlMiddlewareAlivePongSysMsgBody
|
||||||
|
) extends BbbCoreMsg
|
||||||
|
case class CheckGraphqlMiddlewareAlivePongSysMsgBody(middlewareUID: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent from akka-apps to graphql-middleware
|
* Sent from akka-apps to graphql-middleware
|
||||||
*/
|
*/
|
||||||
@ -251,21 +271,21 @@ case class UserGraphqlReconnectionForcedEvtMsg(
|
|||||||
header: BbbCoreBaseHeader,
|
header: BbbCoreBaseHeader,
|
||||||
body: UserGraphqlReconnectionForcedEvtMsgBody
|
body: UserGraphqlReconnectionForcedEvtMsgBody
|
||||||
) extends BbbCoreMsg
|
) extends BbbCoreMsg
|
||||||
case class UserGraphqlReconnectionForcedEvtMsgBody(sessionToken: String, browserConnectionId: String)
|
case class UserGraphqlReconnectionForcedEvtMsgBody(middlewareUID: String, sessionToken: String, browserConnectionId: String)
|
||||||
|
|
||||||
object UserGraphqlConnectionEstablishedSysMsg { val NAME = "UserGraphqlConnectionEstablishedSysMsg" }
|
object UserGraphqlConnectionEstablishedSysMsg { val NAME = "UserGraphqlConnectionEstablishedSysMsg" }
|
||||||
case class UserGraphqlConnectionEstablishedSysMsg(
|
case class UserGraphqlConnectionEstablishedSysMsg(
|
||||||
header: BbbCoreBaseHeader,
|
header: BbbCoreBaseHeader,
|
||||||
body: UserGraphqlConnectionEstablishedSysMsgBody
|
body: UserGraphqlConnectionEstablishedSysMsgBody
|
||||||
) extends BbbCoreMsg
|
) extends BbbCoreMsg
|
||||||
case class UserGraphqlConnectionEstablishedSysMsgBody(sessionToken: String, browserConnectionId: String)
|
case class UserGraphqlConnectionEstablishedSysMsgBody(middlewareUID: String, sessionToken: String, browserConnectionId: String)
|
||||||
|
|
||||||
object UserGraphqlConnectionClosedSysMsg { val NAME = "UserGraphqlConnectionClosedSysMsg" }
|
object UserGraphqlConnectionClosedSysMsg { val NAME = "UserGraphqlConnectionClosedSysMsg" }
|
||||||
case class UserGraphqlConnectionClosedSysMsg(
|
case class UserGraphqlConnectionClosedSysMsg(
|
||||||
header: BbbCoreBaseHeader,
|
header: BbbCoreBaseHeader,
|
||||||
body: UserGraphqlConnectionClosedSysMsgBody
|
body: UserGraphqlConnectionClosedSysMsgBody
|
||||||
) extends BbbCoreMsg
|
) extends BbbCoreMsg
|
||||||
case class UserGraphqlConnectionClosedSysMsgBody(sessionToken: String, browserConnectionId: String)
|
case class UserGraphqlConnectionClosedSysMsgBody(middlewareUID: String, sessionToken: String, browserConnectionId: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent from akka-apps to bbb-web to inform a summary of the meeting activities
|
* Sent from akka-apps to bbb-web to inform a summary of the meeting activities
|
||||||
|
@ -19,7 +19,7 @@ case class StartTimerReqMsgBody()
|
|||||||
|
|
||||||
object StopTimerReqMsg { val NAME = "StopTimerReqMsg" }
|
object StopTimerReqMsg { val NAME = "StopTimerReqMsg" }
|
||||||
case class StopTimerReqMsg(header: BbbClientMsgHeader, body: StopTimerReqMsgBody) extends StandardMsg
|
case class StopTimerReqMsg(header: BbbClientMsgHeader, body: StopTimerReqMsgBody) extends StandardMsg
|
||||||
case class StopTimerReqMsgBody(accumulated: Int)
|
case class StopTimerReqMsgBody()
|
||||||
|
|
||||||
object SwitchTimerReqMsg { val NAME = "SwitchTimerReqMsg" }
|
object SwitchTimerReqMsg { val NAME = "SwitchTimerReqMsg" }
|
||||||
case class SwitchTimerReqMsg(header: BbbClientMsgHeader, body: SwitchTimerReqMsgBody) extends StandardMsg
|
case class SwitchTimerReqMsg(header: BbbClientMsgHeader, body: SwitchTimerReqMsgBody) extends StandardMsg
|
||||||
|
@ -293,6 +293,20 @@ object ChangeUserMobileFlagReqMsg { val NAME = "ChangeUserMobileFlagReqMsg" }
|
|||||||
case class ChangeUserMobileFlagReqMsg(header: BbbClientMsgHeader, body: ChangeUserMobileFlagReqMsgBody) extends StandardMsg
|
case class ChangeUserMobileFlagReqMsg(header: BbbClientMsgHeader, body: ChangeUserMobileFlagReqMsgBody) extends StandardMsg
|
||||||
case class ChangeUserMobileFlagReqMsgBody(userId: String, mobile: Boolean)
|
case class ChangeUserMobileFlagReqMsgBody(userId: String, mobile: Boolean)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent from client to inform the connection is alive.
|
||||||
|
*/
|
||||||
|
object UserConnectionAliveReqMsg { val NAME = "UserConnectionAliveReqMsg" }
|
||||||
|
case class UserConnectionAliveReqMsg(header: BbbClientMsgHeader, body: UserConnectionAliveReqMsgBody) extends StandardMsg
|
||||||
|
case class UserConnectionAliveReqMsgBody(userId: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent from client to inform the RTT (time it took to send the Alive and receive confirmation).
|
||||||
|
*/
|
||||||
|
object UserConnectionUpdateRttReqMsg { val NAME = "UserConnectionUpdateRttReqMsg" }
|
||||||
|
case class UserConnectionUpdateRttReqMsg(header: BbbClientMsgHeader, body: UserConnectionUpdateRttReqMsgBody) extends StandardMsg
|
||||||
|
case class UserConnectionUpdateRttReqMsgBody(userId: String, networkRttInMs: Double)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent to all clients about a user mobile flag.
|
* Sent to all clients about a user mobile flag.
|
||||||
*/
|
*/
|
||||||
@ -511,20 +525,6 @@ object UserActivitySignCmdMsg { val NAME = "UserActivitySignCmdMsg" }
|
|||||||
case class UserActivitySignCmdMsg(header: BbbClientMsgHeader, body: UserActivitySignCmdMsgBody) extends StandardMsg
|
case class UserActivitySignCmdMsg(header: BbbClientMsgHeader, body: UserActivitySignCmdMsgBody) extends StandardMsg
|
||||||
case class UserActivitySignCmdMsgBody(userId: String)
|
case class UserActivitySignCmdMsgBody(userId: String)
|
||||||
|
|
||||||
/**
|
|
||||||
* Sent from client to randomly select a viewer
|
|
||||||
*/
|
|
||||||
object SelectRandomViewerReqMsg { val NAME = "SelectRandomViewerReqMsg" }
|
|
||||||
case class SelectRandomViewerReqMsg(header: BbbClientMsgHeader, body: SelectRandomViewerReqMsgBody) extends StandardMsg
|
|
||||||
case class SelectRandomViewerReqMsgBody(requestedBy: String)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response to request for a random viewer
|
|
||||||
*/
|
|
||||||
object SelectRandomViewerRespMsg { val NAME = "SelectRandomViewerRespMsg" }
|
|
||||||
case class SelectRandomViewerRespMsg(header: BbbClientMsgHeader, body: SelectRandomViewerRespMsgBody) extends StandardMsg
|
|
||||||
case class SelectRandomViewerRespMsgBody(requestedBy: String, userIds: Vector[String], choice: String)
|
|
||||||
|
|
||||||
object SetUserSpeechLocaleReqMsg { val NAME = "SetUserSpeechLocaleReqMsg" }
|
object SetUserSpeechLocaleReqMsg { val NAME = "SetUserSpeechLocaleReqMsg" }
|
||||||
case class SetUserSpeechLocaleReqMsg(header: BbbClientMsgHeader, body: SetUserSpeechLocaleReqMsgBody) extends StandardMsg
|
case class SetUserSpeechLocaleReqMsg(header: BbbClientMsgHeader, body: SetUserSpeechLocaleReqMsgBody) extends StandardMsg
|
||||||
case class SetUserSpeechLocaleReqMsgBody(locale: String, provider: String)
|
case class SetUserSpeechLocaleReqMsgBody(locale: String, provider: String)
|
||||||
@ -532,3 +532,11 @@ case class SetUserSpeechLocaleReqMsgBody(locale: String, provider: String)
|
|||||||
object UserSpeechLocaleChangedEvtMsg { val NAME = "UserSpeechLocaleChangedEvtMsg" }
|
object UserSpeechLocaleChangedEvtMsg { val NAME = "UserSpeechLocaleChangedEvtMsg" }
|
||||||
case class UserSpeechLocaleChangedEvtMsg(header: BbbClientMsgHeader, body: UserSpeechLocaleChangedEvtMsgBody) extends BbbCoreMsg
|
case class UserSpeechLocaleChangedEvtMsg(header: BbbClientMsgHeader, body: UserSpeechLocaleChangedEvtMsgBody) extends BbbCoreMsg
|
||||||
case class UserSpeechLocaleChangedEvtMsgBody(locale: String, provider: String)
|
case class UserSpeechLocaleChangedEvtMsgBody(locale: String, provider: String)
|
||||||
|
|
||||||
|
object SetUserSpeechOptionsReqMsg { val NAME = "SetUserSpeechOptionsReqMsg" }
|
||||||
|
case class SetUserSpeechOptionsReqMsg(header: BbbClientMsgHeader, body: SetUserSpeechOptionsReqMsgBody) extends StandardMsg
|
||||||
|
case class SetUserSpeechOptionsReqMsgBody(partialUtterances: Boolean, minUtteranceLength: Int)
|
||||||
|
|
||||||
|
object UserSpeechOptionsChangedEvtMsg { val NAME = "UserSpeechOptionsChangedEvtMsg" }
|
||||||
|
case class UserSpeechOptionsChangedEvtMsg(header: BbbClientMsgHeader, body: UserSpeechOptionsChangedEvtMsgBody) extends BbbCoreMsg
|
||||||
|
case class UserSpeechOptionsChangedEvtMsgBody(partialUtterances: Boolean, minUtteranceLength: Int)
|
||||||
|
@ -22,6 +22,7 @@ case class ExportJob(
|
|||||||
jobId: String,
|
jobId: String,
|
||||||
jobType: String,
|
jobType: String,
|
||||||
filename: String,
|
filename: String,
|
||||||
|
serverSideFilename: String,
|
||||||
presId: String,
|
presId: String,
|
||||||
presLocation: String,
|
presLocation: String,
|
||||||
allPages: Boolean,
|
allPages: Boolean,
|
||||||
|
@ -103,10 +103,10 @@ homepage := Some(url("http://www.bigbluebutton.org"))
|
|||||||
|
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
"javax.validation" % "validation-api" % "2.0.1.Final",
|
"javax.validation" % "validation-api" % "2.0.1.Final",
|
||||||
"org.springframework.boot" % "spring-boot-starter-validation" % "2.7.12",
|
"org.springframework.boot" % "spring-boot-starter-validation" % "2.7.17",
|
||||||
"org.springframework.data" % "spring-data-commons" % "2.7.6",
|
"org.springframework.data" % "spring-data-commons" % "2.7.6",
|
||||||
"org.apache.httpcomponents" % "httpclient" % "4.5.13",
|
"org.apache.httpcomponents" % "httpclient" % "4.5.13",
|
||||||
"org.postgresql" % "postgresql" % "42.4.3",
|
"org.postgresql" % "postgresql" % "42.7.2",
|
||||||
"org.hibernate" % "hibernate-core" % "5.6.1.Final",
|
"org.hibernate" % "hibernate-core" % "5.6.1.Final",
|
||||||
"org.flywaydb" % "flyway-core" % "7.8.2",
|
"org.flywaydb" % "flyway-core" % "7.8.2",
|
||||||
"com.zaxxer" % "HikariCP" % "4.0.3",
|
"com.zaxxer" % "HikariCP" % "4.0.3",
|
||||||
|
@ -75,6 +75,7 @@ public class MeetingService implements MessageListener {
|
|||||||
*/
|
*/
|
||||||
private final ConcurrentMap<String, Meeting> meetings;
|
private final ConcurrentMap<String, Meeting> meetings;
|
||||||
private final ConcurrentMap<String, UserSession> sessions;
|
private final ConcurrentMap<String, UserSession> sessions;
|
||||||
|
private final ConcurrentMap<String, UserSessionBasicData> removedSessions;
|
||||||
|
|
||||||
private RecordingService recordingService;
|
private RecordingService recordingService;
|
||||||
private LearningDashboardService learningDashboardService;
|
private LearningDashboardService learningDashboardService;
|
||||||
@ -88,6 +89,7 @@ public class MeetingService implements MessageListener {
|
|||||||
|
|
||||||
private long usersTimeout;
|
private long usersTimeout;
|
||||||
private long waitingGuestUsersTimeout;
|
private long waitingGuestUsersTimeout;
|
||||||
|
private int sessionsCleanupDelayInMinutes;
|
||||||
private long enteredUsersTimeout;
|
private long enteredUsersTimeout;
|
||||||
|
|
||||||
private ParamsProcessorUtil paramsProcessorUtil;
|
private ParamsProcessorUtil paramsProcessorUtil;
|
||||||
@ -100,6 +102,7 @@ public class MeetingService implements MessageListener {
|
|||||||
public MeetingService() {
|
public MeetingService() {
|
||||||
meetings = new ConcurrentHashMap<String, Meeting>(8, 0.9f, 1);
|
meetings = new ConcurrentHashMap<String, Meeting>(8, 0.9f, 1);
|
||||||
sessions = new ConcurrentHashMap<String, UserSession>(8, 0.9f, 1);
|
sessions = new ConcurrentHashMap<String, UserSession>(8, 0.9f, 1);
|
||||||
|
removedSessions = new ConcurrentHashMap<String, UserSessionBasicData>(8, 0.9f, 1);
|
||||||
uploadAuthzTokens = new HashMap<String, PresentationUploadToken>();
|
uploadAuthzTokens = new HashMap<String, PresentationUploadToken>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,12 +152,16 @@ public class MeetingService implements MessageListener {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserSession getUserSessionWithAuthToken(String token) {
|
public UserSession getUserSessionWithSessionToken(String token) {
|
||||||
return sessions.get(token);
|
return sessions.get(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UserSessionBasicData getRemovedUserSessionWithSessionToken(String sessionToken) {
|
||||||
|
return removedSessions.get(sessionToken);
|
||||||
|
}
|
||||||
|
|
||||||
public Boolean getAllowRequestsWithoutSession(String token) {
|
public Boolean getAllowRequestsWithoutSession(String token) {
|
||||||
UserSession us = getUserSessionWithAuthToken(token);
|
UserSession us = getUserSessionWithSessionToken(token);
|
||||||
if (us == null) {
|
if (us == null) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@ -164,12 +171,22 @@ public class MeetingService implements MessageListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserSession removeUserSessionWithAuthToken(String token) {
|
public void removeUserSessionWithSessionToken(String token) {
|
||||||
UserSession user = sessions.remove(token);
|
log.debug("Removing token={}", token);
|
||||||
if (user != null) {
|
UserSession us = getUserSessionWithSessionToken(token);
|
||||||
log.debug("Found user {} token={} to meeting {}", user.fullname, token, user.meetingID);
|
if (us != null) {
|
||||||
|
log.debug("Found user {} token={} to meeting {}", us.fullname, token, us.meetingID);
|
||||||
|
|
||||||
|
UserSessionBasicData removedUser = new UserSessionBasicData();
|
||||||
|
removedUser.meetingId = us.meetingID;
|
||||||
|
removedUser.userId = us.internalUserId;
|
||||||
|
removedUser.sessionToken = us.authToken;
|
||||||
|
removedUser.role = us.role;
|
||||||
|
removedSessions.put(token, removedUser);
|
||||||
|
sessions.remove(token);
|
||||||
|
} else {
|
||||||
|
log.debug("Not found token={}", token);
|
||||||
}
|
}
|
||||||
return user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -295,17 +312,41 @@ public class MeetingService implements MessageListener {
|
|||||||
notifier.sendUploadFileTooLargeMessage(presUploadToken, uploadedFileSize, maxUploadFileSize);
|
notifier.sendUploadFileTooLargeMessage(presUploadToken, uploadedFileSize, maxUploadFileSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeUserSessions(String meetingId) {
|
private void removeUserSessionsFromMeeting(String meetingId) {
|
||||||
Iterator<Map.Entry<String, UserSession>> iterator = sessions.entrySet().iterator();
|
for (String token : sessions.keySet()) {
|
||||||
while (iterator.hasNext()) {
|
UserSession userSession = sessions.get(token);
|
||||||
Map.Entry<String, UserSession> entry = iterator.next();
|
|
||||||
UserSession userSession = entry.getValue();
|
|
||||||
|
|
||||||
if (userSession.meetingID.equals(meetingId)) {
|
if (userSession.meetingID.equals(meetingId)) {
|
||||||
|
System.out.println(token + " = " + userSession.authToken);
|
||||||
|
removeUserSessionWithSessionToken(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleRemovedSessionsCleanUp(meetingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleRemovedSessionsCleanUp(String meetingId) {
|
||||||
|
Calendar cleanUpDelayCalendar = Calendar.getInstance();
|
||||||
|
cleanUpDelayCalendar.add(Calendar.MINUTE, sessionsCleanupDelayInMinutes);
|
||||||
|
|
||||||
|
log.debug("Sessions for meeting={} will be removed within {} minutes.", meetingId, sessionsCleanupDelayInMinutes);
|
||||||
|
new java.util.Timer().schedule(
|
||||||
|
new java.util.TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Iterator<Map.Entry<String, UserSessionBasicData>> iterator = removedSessions.entrySet().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Map.Entry<String, UserSessionBasicData> entry = iterator.next();
|
||||||
|
UserSessionBasicData removedUserSession = entry.getValue();
|
||||||
|
|
||||||
|
if (removedUserSession.meetingId.equals(meetingId)) {
|
||||||
|
log.debug("Removed user {} session for meeting {}.",removedUserSession.userId, removedUserSession.meetingId);
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, cleanUpDelayCalendar.getTime()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private void destroyMeeting(String meetingId) {
|
private void destroyMeeting(String meetingId) {
|
||||||
gw.destroyMeeting(new DestroyMeetingMessage(meetingId));
|
gw.destroyMeeting(new DestroyMeetingMessage(meetingId));
|
||||||
@ -411,8 +452,8 @@ public class MeetingService implements MessageListener {
|
|||||||
m.getUserInactivityInspectTimerInMinutes(), m.getUserInactivityThresholdInMinutes(),
|
m.getUserInactivityInspectTimerInMinutes(), m.getUserInactivityThresholdInMinutes(),
|
||||||
m.getUserActivitySignResponseDelayInMinutes(), m.getEndWhenNoModerator(), m.getEndWhenNoModeratorDelayInMinutes(),
|
m.getUserActivitySignResponseDelayInMinutes(), m.getEndWhenNoModerator(), m.getEndWhenNoModeratorDelayInMinutes(),
|
||||||
m.getMuteOnStart(), m.getAllowModsToUnmuteUsers(), m.getAllowModsToEjectCameras(), m.getMeetingKeepEvents(),
|
m.getMuteOnStart(), m.getAllowModsToUnmuteUsers(), m.getAllowModsToEjectCameras(), m.getMeetingKeepEvents(),
|
||||||
m.breakoutRoomsParams, m.lockSettingsParams, m.getHtml5InstanceId(), m.getLogoutUrl(),
|
m.breakoutRoomsParams, m.lockSettingsParams, m.getHtml5InstanceId(), m.getLogoutUrl(), m.getCustomLogoURL(),
|
||||||
m.getGroups(), m.getDisabledFeatures(), m.getNotifyRecordingIsOn(),
|
m.getBannerText(), m.getBannerColor(), m.getGroups(), m.getDisabledFeatures(), m.getNotifyRecordingIsOn(),
|
||||||
m.getPresentationUploadExternalDescription(), m.getPresentationUploadExternalUrl(),
|
m.getPresentationUploadExternalDescription(), m.getPresentationUploadExternalUrl(),
|
||||||
m.getOverrideClientSettings());
|
m.getOverrideClientSettings());
|
||||||
}
|
}
|
||||||
@ -703,7 +744,7 @@ public class MeetingService implements MessageListener {
|
|||||||
}
|
}
|
||||||
destroyMeeting(m.getInternalId());
|
destroyMeeting(m.getInternalId());
|
||||||
meetings.remove(m.getInternalId());
|
meetings.remove(m.getInternalId());
|
||||||
removeUserSessions(m.getInternalId());
|
removeUserSessionsFromMeeting(m.getInternalId());
|
||||||
|
|
||||||
Map<String, Object> logData = new HashMap<>();
|
Map<String, Object> logData = new HashMap<>();
|
||||||
logData.put("meetingId", m.getInternalId());
|
logData.put("meetingId", m.getInternalId());
|
||||||
@ -1111,7 +1152,7 @@ public class MeetingService implements MessageListener {
|
|||||||
user.setRole(message.role);
|
user.setRole(message.role);
|
||||||
String sessionToken = getTokenByUserId(user.getInternalUserId());
|
String sessionToken = getTokenByUserId(user.getInternalUserId());
|
||||||
if (sessionToken != null) {
|
if (sessionToken != null) {
|
||||||
UserSession userSession = getUserSessionWithAuthToken(sessionToken);
|
UserSession userSession = getUserSessionWithSessionToken(sessionToken);
|
||||||
userSession.role = message.role;
|
userSession.role = message.role;
|
||||||
sessions.replace(sessionToken, userSession);
|
sessions.replace(sessionToken, userSession);
|
||||||
}
|
}
|
||||||
@ -1184,6 +1225,10 @@ public class MeetingService implements MessageListener {
|
|||||||
processGuestStatusChangedEventMsg((GuestStatusChangedEventMsg) message);
|
processGuestStatusChangedEventMsg((GuestStatusChangedEventMsg) message);
|
||||||
} else if (message instanceof GuestPolicyChanged) {
|
} else if (message instanceof GuestPolicyChanged) {
|
||||||
processGuestPolicyChanged((GuestPolicyChanged) message);
|
processGuestPolicyChanged((GuestPolicyChanged) message);
|
||||||
|
} else if (message instanceof LockSettingsChanged) {
|
||||||
|
processLockSettingsChanged((LockSettingsChanged) message);
|
||||||
|
} else if (message instanceof WebcamsOnlyForModeratorChanged) {
|
||||||
|
processWebcamsOnlyForModeratorChanged((WebcamsOnlyForModeratorChanged) message);
|
||||||
} else if (message instanceof GuestLobbyMessageChanged) {
|
} else if (message instanceof GuestLobbyMessageChanged) {
|
||||||
processGuestLobbyMessageChanged((GuestLobbyMessageChanged) message);
|
processGuestLobbyMessageChanged((GuestLobbyMessageChanged) message);
|
||||||
} else if (message instanceof PrivateGuestLobbyMessageChanged) {
|
} else if (message instanceof PrivateGuestLobbyMessageChanged) {
|
||||||
@ -1210,6 +1255,32 @@ public class MeetingService implements MessageListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void processLockSettingsChanged(LockSettingsChanged msg) {
|
||||||
|
Meeting m = getMeeting(msg.meetingId);
|
||||||
|
if (m != null) {
|
||||||
|
m.setLockSettings(
|
||||||
|
new LockSettingsParams(
|
||||||
|
msg.disableCam,
|
||||||
|
msg.disableMic,
|
||||||
|
msg.disablePrivateChat,
|
||||||
|
msg.disablePublicChat,
|
||||||
|
msg.disableNotes,
|
||||||
|
msg.hideUserList,
|
||||||
|
msg.lockOnJoin,
|
||||||
|
msg.lockOnJoinConfigurable,
|
||||||
|
msg.hideViewersCursor,
|
||||||
|
msg.hideViewersAnnotation)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processWebcamsOnlyForModeratorChanged(WebcamsOnlyForModeratorChanged msg) {
|
||||||
|
Meeting m = getMeeting(msg.meetingId);
|
||||||
|
if (m != null) {
|
||||||
|
m.setWebcamsOnlyForModerator(msg.webcamsOnlyForModerator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void processPositionInWaitingQueueUpdated(PositionInWaitingQueueUpdated msg) {
|
public void processPositionInWaitingQueueUpdated(PositionInWaitingQueueUpdated msg) {
|
||||||
Meeting m = getMeeting(msg.meetingId);
|
Meeting m = getMeeting(msg.meetingId);
|
||||||
HashMap<String,String> guestUsers = msg.guests;
|
HashMap<String,String> guestUsers = msg.guests;
|
||||||
@ -1333,6 +1404,10 @@ public class MeetingService implements MessageListener {
|
|||||||
waitingGuestUsersTimeout = value;
|
waitingGuestUsersTimeout = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSessionsCleanupDelayInMinutes(int value) {
|
||||||
|
sessionsCleanupDelayInMinutes = value;
|
||||||
|
}
|
||||||
|
|
||||||
public void setEnteredUsersTimeout(long value) {
|
public void setEnteredUsersTimeout(long value) {
|
||||||
enteredUsersTimeout = value;
|
enteredUsersTimeout = value;
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,8 @@ public class ParamsProcessorUtil {
|
|||||||
private String defaultServerUrl;
|
private String defaultServerUrl;
|
||||||
private int defaultNumDigitsForTelVoice;
|
private int defaultNumDigitsForTelVoice;
|
||||||
private String defaultHTML5ClientUrl;
|
private String defaultHTML5ClientUrl;
|
||||||
|
|
||||||
|
private String graphqlWebsocketUrl;
|
||||||
private String defaultGuestWaitURL;
|
private String defaultGuestWaitURL;
|
||||||
private Boolean allowRequestsWithoutSession = false;
|
private Boolean allowRequestsWithoutSession = false;
|
||||||
private Integer defaultHttpSessionTimeout = 14400;
|
private Integer defaultHttpSessionTimeout = 14400;
|
||||||
@ -864,6 +866,10 @@ public class ParamsProcessorUtil {
|
|||||||
return defaultHTML5ClientUrl;
|
return defaultHTML5ClientUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getGraphqlWebsocketUrl() {
|
||||||
|
return graphqlWebsocketUrl;
|
||||||
|
}
|
||||||
|
|
||||||
public String getDefaultGuestWaitURL() {
|
public String getDefaultGuestWaitURL() {
|
||||||
return defaultGuestWaitURL;
|
return defaultGuestWaitURL;
|
||||||
}
|
}
|
||||||
@ -1217,6 +1223,10 @@ public class ParamsProcessorUtil {
|
|||||||
this.defaultHTML5ClientUrl = defaultHTML5ClientUrl;
|
this.defaultHTML5ClientUrl = defaultHTML5ClientUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setGraphqlWebsocketUrl(String graphqlWebsocketUrl) {
|
||||||
|
this.graphqlWebsocketUrl = graphqlWebsocketUrl.replace("https://","wss://");
|
||||||
|
}
|
||||||
|
|
||||||
public void setDefaultGuestWaitURL(String url) {
|
public void setDefaultGuestWaitURL(String url) {
|
||||||
this.defaultGuestWaitURL = url;
|
this.defaultGuestWaitURL = url;
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ public class Meeting {
|
|||||||
private Integer endWhenNoModeratorDelayInMinutes = 1;
|
private Integer endWhenNoModeratorDelayInMinutes = 1;
|
||||||
|
|
||||||
public final BreakoutRoomsParams breakoutRoomsParams;
|
public final BreakoutRoomsParams breakoutRoomsParams;
|
||||||
public final LockSettingsParams lockSettingsParams;
|
public LockSettingsParams lockSettingsParams;
|
||||||
|
|
||||||
public final Integer maxUserConcurrentAccesses;
|
public final Integer maxUserConcurrentAccesses;
|
||||||
|
|
||||||
@ -475,6 +475,14 @@ public class Meeting {
|
|||||||
return guestPolicy;
|
return guestPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLockSettings(LockSettingsParams lockSettingsParams) {
|
||||||
|
this.lockSettingsParams = lockSettingsParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWebcamsOnlyForModerator(Boolean webcamsOnlyForModerator) {
|
||||||
|
this.webcamsOnlyForModerator = webcamsOnlyForModerator;
|
||||||
|
}
|
||||||
|
|
||||||
public void setGuestLobbyMessage(String message) {
|
public void setGuestLobbyMessage(String message) {
|
||||||
guestLobbyMessage = message;
|
guestLobbyMessage = message;
|
||||||
}
|
}
|
||||||
|
@ -138,6 +138,10 @@ public class User {
|
|||||||
return "MODERATOR".equalsIgnoreCase(this.role);
|
return "MODERATOR".equalsIgnoreCase(this.role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isLocked() {
|
||||||
|
return this.locked;
|
||||||
|
}
|
||||||
|
|
||||||
public void setStatus(String key, String value){
|
public void setStatus(String key, String value){
|
||||||
this.status.put(key, value);
|
this.status.put(key, value);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
* version.
|
||||||
|
*
|
||||||
|
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.bigbluebutton.api.domain;
|
||||||
|
|
||||||
|
public class UserSessionBasicData {
|
||||||
|
public String sessionToken = null;
|
||||||
|
public String userId = null;
|
||||||
|
public String meetingId = null;
|
||||||
|
public String role = null;
|
||||||
|
|
||||||
|
public Boolean isModerator() {
|
||||||
|
return "MODERATOR".equalsIgnoreCase(this.role);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return meetingId + " " + userId + " " + sessionToken;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package org.bigbluebutton.api.messaging.messages;
|
||||||
|
|
||||||
|
public class LockSettingsChanged implements IMessage {
|
||||||
|
|
||||||
|
public final String meetingId;
|
||||||
|
public final Boolean disableCam;
|
||||||
|
public final Boolean disableMic;
|
||||||
|
public final Boolean disablePrivateChat;
|
||||||
|
public final Boolean disablePublicChat;
|
||||||
|
public final Boolean disableNotes;
|
||||||
|
public final Boolean hideUserList;
|
||||||
|
public final Boolean lockOnJoin;
|
||||||
|
public final Boolean lockOnJoinConfigurable;
|
||||||
|
public final Boolean hideViewersCursor;
|
||||||
|
public final Boolean hideViewersAnnotation;
|
||||||
|
|
||||||
|
public LockSettingsChanged(String meetingId,
|
||||||
|
Boolean disableCam,
|
||||||
|
Boolean disableMic,
|
||||||
|
Boolean disablePrivateChat,
|
||||||
|
Boolean disablePublicChat,
|
||||||
|
Boolean disableNotes,
|
||||||
|
Boolean hideUserList,
|
||||||
|
Boolean lockOnJoin,
|
||||||
|
Boolean lockOnJoinConfigurable,
|
||||||
|
Boolean hideViewersCursor,
|
||||||
|
Boolean hideViewersAnnotation) {
|
||||||
|
this.meetingId = meetingId;
|
||||||
|
this.disableCam = disableCam;
|
||||||
|
this.disableMic = disableMic;
|
||||||
|
this.disablePrivateChat = disablePrivateChat;
|
||||||
|
this.disablePublicChat = disablePublicChat;
|
||||||
|
this.disableNotes = disableNotes;
|
||||||
|
this.hideUserList = hideUserList;
|
||||||
|
this.lockOnJoin = lockOnJoin;
|
||||||
|
this.lockOnJoinConfigurable = lockOnJoinConfigurable;
|
||||||
|
this.hideViewersCursor = hideViewersCursor;
|
||||||
|
this.hideViewersAnnotation = hideViewersAnnotation;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package org.bigbluebutton.api.messaging.messages;
|
||||||
|
|
||||||
|
public class WebcamsOnlyForModeratorChanged implements IMessage {
|
||||||
|
public final String meetingId;
|
||||||
|
public final Boolean webcamsOnlyForModerator;
|
||||||
|
|
||||||
|
public WebcamsOnlyForModeratorChanged(String meetingId, Boolean webcamsOnlyForModerator) {
|
||||||
|
this.meetingId = meetingId;
|
||||||
|
this.webcamsOnlyForModerator = webcamsOnlyForModerator;
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,7 @@ public class GuestPolicyValidator implements ConstraintValidator<GuestPolicyCons
|
|||||||
}
|
}
|
||||||
|
|
||||||
MeetingService meetingService = ServiceUtils.getMeetingService();
|
MeetingService meetingService = ServiceUtils.getMeetingService();
|
||||||
UserSession userSession = meetingService.getUserSessionWithAuthToken(sessionToken);
|
UserSession userSession = meetingService.getUserSessionWithSessionToken(sessionToken);
|
||||||
|
|
||||||
if(userSession == null || !userSession.guestStatus.equals(GuestPolicy.ALLOW)) {
|
if(userSession == null || !userSession.guestStatus.equals(GuestPolicy.ALLOW)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -19,7 +19,7 @@ public class UserSessionValidator implements ConstraintValidator<UserSessionCons
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserSession userSession = ServiceUtils.getMeetingService().getUserSessionWithAuthToken(sessionToken);
|
UserSession userSession = ServiceUtils.getMeetingService().getUserSessionWithSessionToken(sessionToken);
|
||||||
|
|
||||||
if(userSession == null) {
|
if(userSession == null) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -22,7 +22,7 @@ public class SessionService {
|
|||||||
|
|
||||||
private void getUserSessionWithToken() {
|
private void getUserSessionWithToken() {
|
||||||
if(sessionToken != null) {
|
if(sessionToken != null) {
|
||||||
userSession = meetingService.getUserSessionWithAuthToken(sessionToken);
|
userSession = meetingService.getUserSessionWithSessionToken(sessionToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,9 @@ import javax.validation.Validation;
|
|||||||
import javax.validation.Validator;
|
import javax.validation.Validator;
|
||||||
import javax.validation.ValidatorFactory;
|
import javax.validation.ValidatorFactory;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -76,6 +79,11 @@ public class ValidationService {
|
|||||||
|
|
||||||
if(request == null) {
|
if(request == null) {
|
||||||
violations.put("validationError", "Request not recognized");
|
violations.put("validationError", "Request not recognized");
|
||||||
|
} else if(params.containsKey("presentationUploadExternalUrl")) {
|
||||||
|
String urlToValidate = params.get("presentationUploadExternalUrl")[0];
|
||||||
|
if(!this.isValidURL(urlToValidate)) {
|
||||||
|
violations.put("validationError", "Param 'presentationUploadExternalUrl' is not a valid URL");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
request.populateFromParamsMap(params);
|
request.populateFromParamsMap(params);
|
||||||
violations = performValidation(request);
|
violations = performValidation(request);
|
||||||
@ -84,6 +92,15 @@ public class ValidationService {
|
|||||||
return violations;
|
return violations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isValidURL(String url) {
|
||||||
|
try {
|
||||||
|
new URL(url).toURI();
|
||||||
|
return true;
|
||||||
|
} catch (MalformedURLException | URISyntaxException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Request initializeRequest(ApiCall apiCall, Map<String, String[]> params, String queryString) {
|
private Request initializeRequest(ApiCall apiCall, Map<String, String[]> params, String queryString) {
|
||||||
Request request = null;
|
Request request = null;
|
||||||
Checksum checksum;
|
Checksum checksum;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user