Merge remote-tracking branch 'bbb/v3.0.x-release' into merge-dec-7
This commit is contained in:
commit
2126bba957
22
.github/workflows/automated-tests.yml
vendored
22
.github/workflows/automated-tests.yml
vendored
@ -13,6 +13,7 @@ on:
|
|||||||
paths-ignore:
|
paths-ignore:
|
||||||
- "docs/**"
|
- "docs/**"
|
||||||
- "**/*.md"
|
- "**/*.md"
|
||||||
|
- "bigbluebutton-html5/public/locales/*.json"
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
concurrency:
|
concurrency:
|
||||||
@ -53,7 +54,7 @@ jobs:
|
|||||||
build-list: bbb-playback bbb-playback-notes bbb-playback-podcast bbb-playback-presentation bbb-playback-screenshare bbb-playback-video bbb-record-core
|
build-list: bbb-playback bbb-playback-notes bbb-playback-podcast bbb-playback-presentation bbb-playback-screenshare bbb-playback-video bbb-record-core
|
||||||
- package: bbb-graphql-server
|
- package: bbb-graphql-server
|
||||||
build-name: bbb-graphql-server
|
build-name: bbb-graphql-server
|
||||||
build-list: bbb-graphql-server bbb-graphql-middleware
|
build-list: bbb-graphql-server bbb-graphql-middleware bbb-graphql-actions-adapter-server
|
||||||
- package: bbb-etherpad
|
- package: bbb-etherpad
|
||||||
cache-files-list: bbb-etherpad.placeholder.sh
|
cache-files-list: bbb-etherpad.placeholder.sh
|
||||||
cache-urls-list: https://api.github.com/repos/mconf/ep_pad_ttl/commits https://api.github.com/repos/alangecker/bbb-etherpad-plugin/commits https://api.github.com/repos/mconf/ep_redis_publisher/commits https://api.github.com/repos/alangecker/bbb-etherpad-skin/commits
|
cache-urls-list: https://api.github.com/repos/mconf/ep_pad_ttl/commits https://api.github.com/repos/alangecker/bbb-etherpad-plugin/commits https://api.github.com/repos/mconf/ep_redis_publisher/commits https://api.github.com/repos/alangecker/bbb-etherpad-skin/commits
|
||||||
@ -72,7 +73,7 @@ jobs:
|
|||||||
build-list: bbb-webrtc-sfu bbb-webrtc-recorder
|
build-list: bbb-webrtc-sfu bbb-webrtc-recorder
|
||||||
cache-files-list: bbb-webrtc-sfu.placeholder.sh bbb-webrtc-recorder.placeholder.sh
|
cache-files-list: bbb-webrtc-sfu.placeholder.sh bbb-webrtc-recorder.placeholder.sh
|
||||||
- package: others
|
- package: others
|
||||||
build-list: bbb-mkclean bbb-pads bbb-libreoffice-docker bbb-transcription-controller bigbluebutton
|
build-list: bbb-mkclean bbb-pads bbb-libreoffice-docker bbb-transcription-controller bigbluebutton bbb-livekit
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Merge branches
|
- name: Merge branches
|
||||||
@ -252,7 +253,7 @@ jobs:
|
|||||||
apt --purge -y remove apache2-bin
|
apt --purge -y remove apache2-bin
|
||||||
'
|
'
|
||||||
- name: Install BBB
|
- name: Install BBB
|
||||||
timeout-minutes: 15
|
timeout-minutes: 25
|
||||||
run: |
|
run: |
|
||||||
sudo -i <<EOF
|
sudo -i <<EOF
|
||||||
set -e
|
set -e
|
||||||
@ -263,6 +264,12 @@ jobs:
|
|||||||
sed -i "s/\"minify\": true,/\"minify\": false,/" /usr/share/etherpad-lite/settings.json
|
sed -i "s/\"minify\": true,/\"minify\": false,/" /usr/share/etherpad-lite/settings.json
|
||||||
bbb-conf --restart
|
bbb-conf --restart
|
||||||
EOF
|
EOF
|
||||||
|
- name: List systemctl services
|
||||||
|
timeout-minutes: 1
|
||||||
|
run: |
|
||||||
|
sudo -i <<EOF
|
||||||
|
systemctl --type=service --state=running,exited,failed --all --no-pager --no-legend
|
||||||
|
EOF
|
||||||
- name: Install test dependencies
|
- name: Install test dependencies
|
||||||
working-directory: ./bigbluebutton-tests/playwright
|
working-directory: ./bigbluebutton-tests/playwright
|
||||||
run: |
|
run: |
|
||||||
@ -272,13 +279,18 @@ jobs:
|
|||||||
npx playwright install
|
npx playwright install
|
||||||
'
|
'
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
working-directory: ./bigbluebutton-tests/playwright
|
uses: nick-fields/retry@v2
|
||||||
|
with:
|
||||||
|
timeout_minutes: 25
|
||||||
|
max_attempts: 3
|
||||||
|
command: |
|
||||||
|
cd ./bigbluebutton-tests/playwright
|
||||||
|
npm run test-chromium-ci -- --shard ${{ matrix.shard }}
|
||||||
env:
|
env:
|
||||||
NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt
|
NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt
|
||||||
ACTIONS_RUNNER_DEBUG: true
|
ACTIONS_RUNNER_DEBUG: true
|
||||||
BBB_URL: https://bbb-ci.test/bigbluebutton/api
|
BBB_URL: https://bbb-ci.test/bigbluebutton/api
|
||||||
BBB_SECRET: bbbci
|
BBB_SECRET: bbbci
|
||||||
run: npm run test-chromium-ci -- --shard ${{ matrix.shard }}
|
|
||||||
- name: Run Firefox tests
|
- name: Run Firefox tests
|
||||||
working-directory: ./bigbluebutton-tests/playwright
|
working-directory: ./bigbluebutton-tests/playwright
|
||||||
if: |
|
if: |
|
||||||
|
@ -12,7 +12,7 @@ stages:
|
|||||||
|
|
||||||
# define which docker image to use for builds
|
# define which docker image to use for builds
|
||||||
default:
|
default:
|
||||||
image: bigbluebutton/bbb-build:bbb28-2023-07-18
|
image: bigbluebutton/bbb-build:v3.0.x-release--2023-09-26-152524
|
||||||
|
|
||||||
# This stage uses git to find out since when each package has been unmodified.
|
# This stage uses git to find out since when each package has been unmodified.
|
||||||
# it then checks an API endpoint on the package server to find out for which of
|
# it then checks an API endpoint on the package server to find out for which of
|
||||||
@ -182,6 +182,11 @@ bbb-webrtc-recorder-build:
|
|||||||
script:
|
script:
|
||||||
- build/setup-inside-docker.sh bbb-webrtc-recorder
|
- build/setup-inside-docker.sh bbb-webrtc-recorder
|
||||||
|
|
||||||
|
bbb-livekit:
|
||||||
|
extends: .build_job
|
||||||
|
script:
|
||||||
|
- build/setup-inside-docker.sh bbb-livekit
|
||||||
|
|
||||||
bbb-transcription-controller-build:
|
bbb-transcription-controller-build:
|
||||||
extends: .build_job
|
extends: .build_job
|
||||||
script:
|
script:
|
||||||
|
@ -9,7 +9,7 @@ We actively support BigBlueButton through the community forums and through secur
|
|||||||
| 2.5.x (or earlier) | :x: |
|
| 2.5.x (or earlier) | :x: |
|
||||||
| 2.6.x | :white_check_mark: |
|
| 2.6.x | :white_check_mark: |
|
||||||
| 2.7.x | :white_check_mark: |
|
| 2.7.x | :white_check_mark: |
|
||||||
| 2.8.x | :x: |
|
| 3.0.x | :x: |
|
||||||
|
|
||||||
We have released 2.7 to the community and are going to support both 2.6 and 2.7 together for the coming months (while we're actively developing the next release). Also, BigBlueButton 2.5 is now end of life.
|
We have released 2.7 to the community and are going to support both 2.6 and 2.7 together for the coming months (while we're actively developing the next release). Also, BigBlueButton 2.5 is now end of life.
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package org.bigbluebutton.build
|
package org.bigbluebutton.build
|
||||||
|
|
||||||
import sbt._
|
import sbt._
|
||||||
import Keys._
|
|
||||||
|
|
||||||
object Dependencies {
|
object Dependencies {
|
||||||
|
|
||||||
@ -37,6 +36,7 @@ object Dependencies {
|
|||||||
val scalaTest = "3.2.11"
|
val scalaTest = "3.2.11"
|
||||||
val mockito = "2.23.0"
|
val mockito = "2.23.0"
|
||||||
val akkaTestKit = "2.6.0"
|
val akkaTestKit = "2.6.0"
|
||||||
|
val jacksonDataFormat = "2.13.5"
|
||||||
}
|
}
|
||||||
|
|
||||||
object Compile {
|
object Compile {
|
||||||
@ -64,7 +64,11 @@ object Dependencies {
|
|||||||
val slick = "com.typesafe.slick" %% "slick" % Versions.slick
|
val slick = "com.typesafe.slick" %% "slick" % Versions.slick
|
||||||
val slickHikaricp = "com.typesafe.slick" %% "slick-hikaricp" % Versions.slick
|
val slickHikaricp = "com.typesafe.slick" %% "slick-hikaricp" % Versions.slick
|
||||||
val slickPg = "com.github.tminglei" %% "slick-pg" % Versions.slickPg
|
val slickPg = "com.github.tminglei" %% "slick-pg" % Versions.slickPg
|
||||||
|
val slickPgSprayJson = "com.github.tminglei" %% "slick-pg_spray-json" % Versions.slickPg
|
||||||
|
|
||||||
val postgresql = "org.postgresql" % "postgresql" % Versions.postgresql
|
val postgresql = "org.postgresql" % "postgresql" % Versions.postgresql
|
||||||
|
val jacksonDataFormat = "com.fasterxml.jackson.dataformat" % "jackson-dataformat-yaml" % Versions.jacksonDataFormat
|
||||||
|
val snakeYaml = "org.yaml" % "snakeyaml"
|
||||||
}
|
}
|
||||||
|
|
||||||
object Test {
|
object Test {
|
||||||
@ -101,5 +105,7 @@ object Dependencies {
|
|||||||
Compile.slick,
|
Compile.slick,
|
||||||
Compile.slickHikaricp,
|
Compile.slickHikaricp,
|
||||||
Compile.slickPg,
|
Compile.slickPg,
|
||||||
Compile.postgresql) ++ testing
|
Compile.slickPgSprayJson,
|
||||||
|
Compile.postgresql,
|
||||||
|
Compile.jacksonDataFormat) ++ testing
|
||||||
}
|
}
|
||||||
|
@ -10,9 +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
|
import org.bigbluebutton.endpoint.redis.{AppsRedisSubscriberActor, ExportAnnotationsActor, GraphqlActionsActor, LearningDashboardActor, RedisRecorderActor}
|
||||||
import org.bigbluebutton.endpoint.redis.{ RedisRecorderActor, ExportAnnotationsActor }
|
|
||||||
import org.bigbluebutton.endpoint.redis.LearningDashboardActor
|
|
||||||
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}
|
||||||
|
|
||||||
@ -69,6 +67,12 @@ object Boot extends App with SystemConfiguration {
|
|||||||
"LearningDashboardActor"
|
"LearningDashboardActor"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val graphqlActionsActor = system.actorOf(
|
||||||
|
GraphqlActionsActor.props(system),
|
||||||
|
"GraphqlActionsActor"
|
||||||
|
)
|
||||||
|
|
||||||
|
ClientSettings.loadClientSettingsFromFile()
|
||||||
recordingEventBus.subscribe(redisRecorderActor, outMessageChannel)
|
recordingEventBus.subscribe(redisRecorderActor, outMessageChannel)
|
||||||
val incomingJsonMessageBus = new IncomingJsonMessageBus
|
val incomingJsonMessageBus = new IncomingJsonMessageBus
|
||||||
|
|
||||||
@ -85,6 +89,9 @@ 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)
|
||||||
|
bbbMsgBus.subscribe(graphqlActionsActor, 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)
|
||||||
|
|
||||||
|
@ -0,0 +1,118 @@
|
|||||||
|
package org.bigbluebutton
|
||||||
|
|
||||||
|
import org.bigbluebutton.common2.util.YamlUtil
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
import java.io.{ ByteArrayInputStream, File }
|
||||||
|
import scala.io.BufferedSource
|
||||||
|
import scala.util.{ Failure, Success, Try }
|
||||||
|
|
||||||
|
object ClientSettings extends SystemConfiguration {
|
||||||
|
var clientSettingsFromFile: Map[String, Object] = Map("" -> "")
|
||||||
|
val logger = LoggerFactory.getLogger(this.getClass)
|
||||||
|
|
||||||
|
def loadClientSettingsFromFile() = {
|
||||||
|
val clientSettingsFile = scala.io.Source.fromFile(clientSettingsPath, "UTF-8")
|
||||||
|
|
||||||
|
val clientSettingsFileOverrideToCheck = new File(clientSettingsPathOverride)
|
||||||
|
|
||||||
|
val clientSettingsFileOverride = if (clientSettingsFileOverrideToCheck.exists())
|
||||||
|
scala.io.Source.fromFile(
|
||||||
|
clientSettingsPathOverride,
|
||||||
|
"UTF-8"
|
||||||
|
)
|
||||||
|
else new BufferedSource(new ByteArrayInputStream(Array[Byte]()))
|
||||||
|
|
||||||
|
clientSettingsFromFile =
|
||||||
|
common2.util.YamlUtil.mergeImmutableMaps(
|
||||||
|
common2.util.YamlUtil.toMap[Object](clientSettingsFile.mkString) match {
|
||||||
|
case Success(value) => value
|
||||||
|
case Failure(exception) =>
|
||||||
|
println("Error while fetching client Settings: ", exception)
|
||||||
|
Map[String, Object]()
|
||||||
|
},
|
||||||
|
common2.util.YamlUtil.toMap[Object](clientSettingsFileOverride.mkString) match {
|
||||||
|
case Success(value) => value
|
||||||
|
case Failure(exception) =>
|
||||||
|
println("Error while fetching client override Settings: ", exception)
|
||||||
|
Map[String, Object]()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def getClientSettingsWithOverride(clientSettingsOverrideJson: String): Map[String, Object] = {
|
||||||
|
if (clientSettingsOverrideJson.nonEmpty) {
|
||||||
|
val scalaMapClientOverride = common2.util.JsonUtil.toMap[Object](clientSettingsOverrideJson)
|
||||||
|
scalaMapClientOverride match {
|
||||||
|
case Success(clientSettingsOverrideAsMap) => YamlUtil.mergeImmutableMaps(clientSettingsFromFile, clientSettingsOverrideAsMap)
|
||||||
|
case Failure(msg) =>
|
||||||
|
logger.debug("No valid JSON override of client configuration in create call: {}", msg)
|
||||||
|
clientSettingsFromFile
|
||||||
|
}
|
||||||
|
} else clientSettingsFromFile
|
||||||
|
}
|
||||||
|
|
||||||
|
def getConfigPropertyValueByPath(map: Map[String, Any], path: String): Option[Any] = {
|
||||||
|
val keys = path.split("\\.")
|
||||||
|
|
||||||
|
def getRecursive(map: Map[String, Any], keys: Seq[String]): Option[Any] = {
|
||||||
|
keys match {
|
||||||
|
case Seq(head, tail @ _*) =>
|
||||||
|
map.get(head) match {
|
||||||
|
case Some(innerMap: Map[String, Any]) => getRecursive(innerMap, tail)
|
||||||
|
case otherValue if tail.isEmpty => otherValue
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getRecursive(map, keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
def getPluginsFromConfig(config: Map[String, Any]): Map[String, Plugin] = {
|
||||||
|
var pluginsFromConfig: Map[String, Plugin] = Map()
|
||||||
|
|
||||||
|
val pluginsConfig = getConfigPropertyValueByPath(config, "public.plugins")
|
||||||
|
pluginsConfig match {
|
||||||
|
case Some(plugins: List[Map[String, Any]]) =>
|
||||||
|
for {
|
||||||
|
plugin <- plugins
|
||||||
|
} yield {
|
||||||
|
if (plugin.contains("name") && plugin.contains("url")) {
|
||||||
|
|
||||||
|
val pluginName = plugin("name").toString
|
||||||
|
val pluginUrl = plugin("url").toString
|
||||||
|
var pluginDataChannels: Map[String, DataChannel] = Map()
|
||||||
|
if (plugin.contains("dataChannels")) {
|
||||||
|
plugin("dataChannels") match {
|
||||||
|
case dataChannels: List[Map[String, Any]] =>
|
||||||
|
for {
|
||||||
|
dataChannel <- dataChannels
|
||||||
|
} yield {
|
||||||
|
if (dataChannel.contains("name") && dataChannel.contains("writePermission")) {
|
||||||
|
val channelName = dataChannel("name").toString
|
||||||
|
val writePermission = dataChannel("writePermission")
|
||||||
|
writePermission match {
|
||||||
|
case wPerm: List[String] => pluginDataChannels += (channelName -> DataChannel(channelName, wPerm))
|
||||||
|
case _ => logger.warn(s"Invalid writePermission for channel $channelName in plugin $pluginName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _ => logger.warn(s"Plugin $pluginName has an invalid dataChannels format")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginsFromConfig += (pluginName -> Plugin(pluginName, pluginUrl, pluginDataChannels))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _ => logger.warn(s"Invalid plugins config found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginsFromConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
case class DataChannel(name: String, writePermission: List[String])
|
||||||
|
case class Plugin(name: String, url: String, dataChannels: Map[String, DataChannel])
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
package org.bigbluebutton
|
package org.bigbluebutton
|
||||||
|
|
||||||
import scala.util.Try
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
||||||
|
|
||||||
|
import scala.util.{ Failure, Success, Try }
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
|
|
||||||
trait SystemConfiguration {
|
trait SystemConfiguration {
|
||||||
@ -77,6 +80,13 @@ trait SystemConfiguration {
|
|||||||
|
|
||||||
lazy val analyticsIncludeChat = Try(config.getBoolean("analytics.includeChat")).getOrElse(true)
|
lazy val analyticsIncludeChat = Try(config.getBoolean("analytics.includeChat")).getOrElse(true)
|
||||||
|
|
||||||
|
lazy val clientSettingsPath = Try(config.getString("client.clientSettingsFilePath")).getOrElse(
|
||||||
|
"/usr/share/meteor/bundle/programs/server/assets/app/config/settings.yml"
|
||||||
|
)
|
||||||
|
lazy val clientSettingsPathOverride = Try(config.getString("client.clientSettingsOverrideFilePath")).getOrElse(
|
||||||
|
"/etc/bigbluebutton/bbb-html5.yml"
|
||||||
|
)
|
||||||
|
|
||||||
// Grab the "interface" parameter from the http config
|
// Grab the "interface" parameter from the http config
|
||||||
val httpHost = config.getString("http.interface")
|
val httpHost = config.getString("http.interface")
|
||||||
// Grab the "port" parameter from the http config
|
// Grab the "port" parameter from the http config
|
||||||
|
@ -81,6 +81,8 @@ class BigBlueButtonActor(
|
|||||||
case m: GetRunningMeetingsReqMsg => handleGetRunningMeetingsReqMsg(m)
|
case m: GetRunningMeetingsReqMsg => handleGetRunningMeetingsReqMsg(m)
|
||||||
case m: CheckAlivePingSysMsg => handleCheckAlivePingSysMsg(m)
|
case m: CheckAlivePingSysMsg => handleCheckAlivePingSysMsg(m)
|
||||||
case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m)
|
case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m)
|
||||||
|
case _: UserGraphqlConnectionStablishedSysMsg => //Ignore
|
||||||
|
case _: UserGraphqlConnectionClosedSysMsg => //Ignore
|
||||||
case _ => log.warning("Cannot handle " + msg.envelope.name)
|
case _ => log.warning("Cannot handle " + msg.envelope.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,12 @@ case class EjectUserFromBreakoutInternalMsg(parentId: String, breakoutId: String
|
|||||||
*/
|
*/
|
||||||
case class CapturePresentationReqInternalMsg(userId: String, parentMeetingId: String, filename: String, allPages: Boolean = true) extends InMessage
|
case class CapturePresentationReqInternalMsg(userId: String, parentMeetingId: String, filename: String, allPages: Boolean = true) extends InMessage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent to the same meeting to force a new presenter to the Pod
|
||||||
|
* @param presenterId
|
||||||
|
*/
|
||||||
|
case class SetPresenterInDefaultPodInternalMsg(presenterId: String) extends InMessage
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent by breakout room to parent meeting to obtain padId
|
* Sent by breakout room to parent meeting to obtain padId
|
||||||
* @param breakoutId
|
* @param breakoutId
|
||||||
|
@ -16,7 +16,7 @@ object TimerModel {
|
|||||||
|
|
||||||
def reset(model: TimerModel) : Unit = {
|
def reset(model: TimerModel) : Unit = {
|
||||||
model.accumulated = 0
|
model.accumulated = 0
|
||||||
model.startedAt = 0
|
model.startedAt = if (model.running) System.currentTimeMillis() else 0
|
||||||
model.endedAt = 0
|
model.endedAt = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ object TimerModel {
|
|||||||
model.isActive = active
|
model.isActive = active
|
||||||
}
|
}
|
||||||
|
|
||||||
def getIsACtive(model: TimerModel): Boolean = {
|
def getIsActive(model: TimerModel): Boolean = {
|
||||||
model.isActive
|
model.isActive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ package org.bigbluebutton.core.apps.audiocaptions
|
|||||||
|
|
||||||
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.AudioCaptionDAO
|
import org.bigbluebutton.core.db.CaptionDAO
|
||||||
import org.bigbluebutton.core.models.AudioCaptions
|
import org.bigbluebutton.core.models.{AudioCaptions, Users2x}
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
import java.sql.Timestamp
|
import java.sql.Timestamp
|
||||||
@ -66,7 +66,12 @@ trait UpdateTranscriptPubMsgHdlr {
|
|||||||
|
|
||||||
val transcript = AudioCaptions.parseTranscript(msg.body.transcript)
|
val transcript = AudioCaptions.parseTranscript(msg.body.transcript)
|
||||||
|
|
||||||
AudioCaptionDAO.insertOrUpdateAudioCaption(msg.body.transcriptId, meetingId, msg.header.userId, transcript, new Timestamp(System.currentTimeMillis()))
|
|
||||||
|
for {
|
||||||
|
u <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||||
|
} yield {
|
||||||
|
CaptionDAO.insertOrUpdateAudioCaption(msg.body.transcriptId, meetingId, msg.header.userId, transcript, u.speechLocale)
|
||||||
|
}
|
||||||
|
|
||||||
broadcastEvent(
|
broadcastEvent(
|
||||||
msg.header.userId,
|
msg.header.userId,
|
||||||
|
@ -70,7 +70,7 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
|||||||
breakout.freeJoin,
|
breakout.freeJoin,
|
||||||
liveMeeting.props.voiceProp.dialNumber,
|
liveMeeting.props.voiceProp.dialNumber,
|
||||||
breakout.voiceConf,
|
breakout.voiceConf,
|
||||||
msg.body.durationInMinutes * 60,
|
msg.body.durationInMinutes,
|
||||||
liveMeeting.props.password.moderatorPass,
|
liveMeeting.props.password.moderatorPass,
|
||||||
liveMeeting.props.password.viewerPass,
|
liveMeeting.props.password.viewerPass,
|
||||||
presId, presSlide, msg.body.record,
|
presId, presSlide, msg.body.record,
|
||||||
|
@ -4,7 +4,7 @@ import org.bigbluebutton.common2.msgs._
|
|||||||
import org.bigbluebutton.core.api.{ SendTimeRemainingAuditInternalMsg, UpdateBreakoutRoomTimeInternalMsg }
|
import org.bigbluebutton.core.api.{ SendTimeRemainingAuditInternalMsg, UpdateBreakoutRoomTimeInternalMsg }
|
||||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
||||||
import org.bigbluebutton.core.db.BreakoutRoomDAO
|
import org.bigbluebutton.core.db.{ BreakoutRoomDAO, MeetingDAO }
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||||
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
|
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
|
||||||
@ -78,6 +78,7 @@ trait UpdateBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
|
|||||||
|
|
||||||
log.debug("Updating {} minutes for breakout rooms time in meeting {}", msg.body.timeInMinutes, props.meetingProp.intId)
|
log.debug("Updating {} minutes for breakout rooms time in meeting {}", msg.body.timeInMinutes, props.meetingProp.intId)
|
||||||
BreakoutRoomDAO.updateRoomsDuration(props.meetingProp.intId, newDurationInSeconds)
|
BreakoutRoomDAO.updateRoomsDuration(props.meetingProp.intId, newDurationInSeconds)
|
||||||
|
MeetingDAO.updateMeetingDurationByParentMeeting(props.meetingProp.intId, newDurationInSeconds)
|
||||||
breakoutModel.setTime(newDurationInSeconds)
|
breakoutModel.setTime(newDurationInSeconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package org.bigbluebutton.core.apps.pads
|
|||||||
|
|
||||||
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.models.Pads
|
import org.bigbluebutton.core.models.Pads
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ trait PadPatchSysMsgHdlr {
|
|||||||
broadcastEditCaptionHistoryEvent(msg.body.userId, msg.body.start, msg.body.end, group.name, locale, msg.body.text)
|
broadcastEditCaptionHistoryEvent(msg.body.userId, msg.body.start, msg.body.end, group.name, locale, msg.body.text)
|
||||||
val tail = liveMeeting.captionModel.getTextTail(group.name)
|
val tail = liveMeeting.captionModel.getTextTail(group.name)
|
||||||
broadcastPadTailEvent(group.externalId, tail)
|
broadcastPadTailEvent(group.externalId, tail)
|
||||||
|
CaptionDAO.insertOrUpdatePadCaption(liveMeeting.props.meetingProp.intId, locale, msg.body.userId, tail)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package org.bigbluebutton.core.apps.plugin
|
||||||
|
|
||||||
|
import org.bigbluebutton.ClientSettings
|
||||||
|
import org.bigbluebutton.common2.msgs._
|
||||||
|
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 DispatchPluginDataChannelMessageMsgHdlr extends HandlerHelpers {
|
||||||
|
|
||||||
|
def handle(msg: DispatchPluginDataChannelMessageMsg, 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 {
|
||||||
|
writePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.dataChannel).writePermission
|
||||||
|
} yield {
|
||||||
|
writePermission.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 write in plugin: '${msg.body.pluginName}', data channel: '${msg.body.dataChannel}'.")
|
||||||
|
} else {
|
||||||
|
PluginDataChannelMessageDAO.insert(
|
||||||
|
meetingId,
|
||||||
|
msg.body.pluginName,
|
||||||
|
msg.body.dataChannel,
|
||||||
|
msg.header.userId,
|
||||||
|
msg.body.payloadJson,
|
||||||
|
msg.body.toRoles,
|
||||||
|
msg.body.toUserIds
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package org.bigbluebutton.core.apps.plugin
|
||||||
|
|
||||||
|
import org.apache.pekko.actor.ActorContext
|
||||||
|
import org.apache.pekko.event.Logging
|
||||||
|
|
||||||
|
class PluginHdlrs(implicit val context: ActorContext)
|
||||||
|
extends DispatchPluginDataChannelMessageMsgHdlr {
|
||||||
|
|
||||||
|
val log = Logging(context.system, getClass)
|
||||||
|
}
|
@ -6,7 +6,7 @@ import org.bigbluebutton.core.apps.groupchats.GroupChatApp
|
|||||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.apps.presentationpod.PresentationSender
|
import org.bigbluebutton.core.apps.presentationpod.PresentationSender
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
import org.bigbluebutton.core.db.ChatMessageDAO
|
import org.bigbluebutton.core.db.{ ChatMessageDAO, PresPresentationDAO }
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
import org.bigbluebutton.core.util.RandomStringGenerator
|
import org.bigbluebutton.core.util.RandomStringGenerator
|
||||||
@ -53,12 +53,13 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
|
|
||||||
def buildBroadcastNewPresFileAvailable(newPresFileAvailableMsg: NewPresFileAvailableMsg, liveMeeting: LiveMeeting): BbbCommonEnvCoreMsg = {
|
def buildBroadcastNewPresFileAvailable(newPresFileAvailableMsg: NewPresFileAvailableMsg, liveMeeting: LiveMeeting): BbbCommonEnvCoreMsg = {
|
||||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, "not-used")
|
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, "not-used")
|
||||||
val envelope = BbbCoreEnvelope(PresentationPageConvertedEventMsg.NAME, routing)
|
val envelope = BbbCoreEnvelope(NewPresFileAvailableEvtMsg.NAME, routing)
|
||||||
val header = BbbClientMsgHeader(NewPresFileAvailableEvtMsg.NAME, liveMeeting.props.meetingProp.intId, "not-used")
|
val header = BbbClientMsgHeader(NewPresFileAvailableEvtMsg.NAME, liveMeeting.props.meetingProp.intId, "not-used")
|
||||||
val body = NewPresFileAvailableEvtMsgBody(
|
val body = NewPresFileAvailableEvtMsgBody(
|
||||||
annotatedFileURI = newPresFileAvailableMsg.body.annotatedFileURI,
|
annotatedFileURI = newPresFileAvailableMsg.body.annotatedFileURI,
|
||||||
originalFileURI = newPresFileAvailableMsg.body.originalFileURI,
|
originalFileURI = newPresFileAvailableMsg.body.originalFileURI,
|
||||||
convertedFileURI = newPresFileAvailableMsg.body.convertedFileURI, presId = newPresFileAvailableMsg.body.presId,
|
convertedFileURI = newPresFileAvailableMsg.body.convertedFileURI,
|
||||||
|
presId = newPresFileAvailableMsg.body.presId,
|
||||||
fileStateType = newPresFileAvailableMsg.body.fileStateType
|
fileStateType = newPresFileAvailableMsg.body.fileStateType
|
||||||
)
|
)
|
||||||
val event = NewPresFileAvailableEvtMsg(header, body)
|
val event = NewPresFileAvailableEvtMsg(header, body)
|
||||||
@ -85,11 +86,11 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
BbbCommonEnvCoreMsg(envelope, event)
|
BbbCommonEnvCoreMsg(envelope, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
def buildPresentationUploadTokenSysPubMsg(parentId: String, userId: String, presentationUploadToken: String, filename: String): BbbCommonEnvCoreMsg = {
|
def buildPresentationUploadTokenSysPubMsg(parentMeetingId: String, userId: String, presentationUploadToken: String, filename: String, presId: String): BbbCommonEnvCoreMsg = {
|
||||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||||
val envelope = BbbCoreEnvelope(PresentationUploadTokenSysPubMsg.NAME, routing)
|
val envelope = BbbCoreEnvelope(PresentationUploadTokenSysPubMsg.NAME, routing)
|
||||||
val header = BbbClientMsgHeader(PresentationUploadTokenSysPubMsg.NAME, parentId, userId)
|
val header = BbbClientMsgHeader(PresentationUploadTokenSysPubMsg.NAME, parentMeetingId, userId)
|
||||||
val body = PresentationUploadTokenSysPubMsgBody("DEFAULT_PRESENTATION_POD", presentationUploadToken, filename, parentId)
|
val body = PresentationUploadTokenSysPubMsgBody("DEFAULT_PRESENTATION_POD", presentationUploadToken, filename, parentMeetingId, presId)
|
||||||
val event = PresentationUploadTokenSysPubMsg(header, body)
|
val event = PresentationUploadTokenSysPubMsg(header, body)
|
||||||
BbbCommonEnvCoreMsg(envelope, event)
|
BbbCommonEnvCoreMsg(envelope, event)
|
||||||
}
|
}
|
||||||
@ -133,9 +134,18 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
|
|
||||||
val currentPres: Option[PresentationInPod] = presentationPods.flatMap(_.getPresentation(presId)).headOption
|
val currentPres: Option[PresentationInPod] = presentationPods.flatMap(_.getPresentation(presId)).headOption
|
||||||
|
|
||||||
if (liveMeeting.props.meetingProp.disabledFeatures.contains("downloadPresentationWithAnnotations")) {
|
if (liveMeeting.props.meetingProp.disabledFeatures.contains("downloadPresentationWithAnnotations")
|
||||||
|
&& m.body.fileStateType == "Annotated") {
|
||||||
val reason = "Annotated presentation download disabled for this meeting."
|
val reason = "Annotated presentation download disabled for this meeting."
|
||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, userId, reason, bus.outGW, liveMeeting)
|
PermissionCheck.ejectUserForFailedPermission(meetingId, userId, reason, bus.outGW, liveMeeting)
|
||||||
|
} else if (liveMeeting.props.meetingProp.disabledFeatures.contains("downloadPresentationOriginalFile")
|
||||||
|
&& m.body.fileStateType == "Original") {
|
||||||
|
val reason = "Original presentation download disabled for this meeting."
|
||||||
|
PermissionCheck.ejectUserForFailedPermission(meetingId, userId, reason, bus.outGW, liveMeeting)
|
||||||
|
} else if (liveMeeting.props.meetingProp.disabledFeatures.contains("downloadPresentationConvertedToPdf")
|
||||||
|
&& m.body.fileStateType == "Converted") {
|
||||||
|
val reason = "Converted presentation download disabled for this meeting. (PDF format)"
|
||||||
|
PermissionCheck.ejectUserForFailedPermission(meetingId, userId, reason, bus.outGW, liveMeeting)
|
||||||
} else if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, userId)) {
|
} else if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, userId)) {
|
||||||
val reason = "No permission to download presentation."
|
val reason = "No permission to download presentation."
|
||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, userId, reason, bus.outGW, liveMeeting)
|
PermissionCheck.ejectUserForFailedPermission(meetingId, userId, reason, bus.outGW, liveMeeting)
|
||||||
@ -196,9 +206,10 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
|
|
||||||
val filename = m.filename
|
val filename = m.filename
|
||||||
val presentationUploadToken: String = PresentationPodsApp.generateToken("DEFAULT_PRESENTATION_POD", userId)
|
val presentationUploadToken: String = PresentationPodsApp.generateToken("DEFAULT_PRESENTATION_POD", userId)
|
||||||
|
val presentationId = PresentationPodsApp.generatePresentationId(m.filename)
|
||||||
|
|
||||||
// Informs bbb-web about the token so that when we use it to upload the presentation, it is able to look it up in the list of tokens
|
// Informs bbb-web about the token so that when we use it to upload the presentation, it is able to look it up in the list of tokens
|
||||||
bus.outGW.send(buildPresentationUploadTokenSysPubMsg(parentMeetingId, userId, presentationUploadToken, filename))
|
bus.outGW.send(buildPresentationUploadTokenSysPubMsg(parentMeetingId, userId, presentationUploadToken, filename, presentationId))
|
||||||
|
|
||||||
if (liveMeeting.props.meetingProp.disabledFeatures.contains("importPresentationWithAnnotationsFromBreakoutRooms")) {
|
if (liveMeeting.props.meetingProp.disabledFeatures.contains("importPresentationWithAnnotationsFromBreakoutRooms")) {
|
||||||
log.error(s"Capturing breakout rooms slides disabled in meeting ${meetingId}.")
|
log.error(s"Capturing breakout rooms slides disabled in meeting ${meetingId}.")
|
||||||
@ -215,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 = new ExportJob(jobId, JobTypes.CAPTURE_PRESENTATION, filename, presId, presLocation, allPages, pagesRange, parentMeetingId, presentationUploadToken)
|
val exportJob: ExportJob = ExportJob(jobId, JobTypes.CAPTURE_PRESENTATION, 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
|
||||||
@ -248,8 +259,13 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
"filename" -> "annotated_slides.pdf"
|
"filename" -> "annotated_slides.pdf"
|
||||||
)
|
)
|
||||||
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") {
|
||||||
|
PresPresentationDAO.updateDownloadUri(m.body.presId, m.body.convertedFileURI)
|
||||||
|
} else if (m.body.fileStateType == "Original") {
|
||||||
|
PresPresentationDAO.updateDownloadUri(m.body.presId, m.body.originalFileURI)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PresPresentationDAO.updateExportToChatStatus(m.body.presId, "EXPORTED")
|
||||||
bus.outGW.send(buildBroadcastNewPresFileAvailable(m, liveMeeting))
|
bus.outGW.send(buildBroadcastNewPresFileAvailable(m, liveMeeting))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,6 +281,7 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def handle(m: PresAnnStatusMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
def handle(m: PresAnnStatusMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||||
|
PresPresentationDAO.updateExportToChat(m.body.presId, m.body.status, m.body.pageNumber, m.body.error)
|
||||||
bus.outGW.send(buildBroadcastPresAnnStatusMsg(m, liveMeeting))
|
bus.outGW.send(buildBroadcastPresAnnStatusMsg(m, liveMeeting))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,8 +291,9 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
|
|||||||
val jobId: String = s"${m.body.breakoutId}-notes" // Used as the temporaryPresentationId upon upload
|
val jobId: String = s"${m.body.breakoutId}-notes" // Used as the temporaryPresentationId upon upload
|
||||||
val filename = m.body.filename
|
val filename = m.body.filename
|
||||||
val presentationUploadToken: String = PresentationPodsApp.generateToken("DEFAULT_PRESENTATION_POD", userId)
|
val presentationUploadToken: String = PresentationPodsApp.generateToken("DEFAULT_PRESENTATION_POD", userId)
|
||||||
|
val presentationId = PresentationPodsApp.generatePresentationId(m.body.filename)
|
||||||
|
|
||||||
bus.outGW.send(buildPresentationUploadTokenSysPubMsg(m.body.parentMeetingId, userId, presentationUploadToken, filename))
|
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, m.body.padId, "", true, List(), m.body.parentMeetingId, presentationUploadToken)
|
||||||
val job = buildStoreExportJobInRedisSysMsg(exportJob, liveMeeting)
|
val job = buildStoreExportJobInRedisSysMsg(exportJob, liveMeeting)
|
||||||
|
@ -41,21 +41,21 @@ trait PdfConversionInvalidErrorSysPubMsgHdlr {
|
|||||||
pod <- PresentationPodsApp.getPresentationPod(state, msg.body.podId)
|
pod <- PresentationPodsApp.getPresentationPod(state, msg.body.podId)
|
||||||
pres <- pod.getPresentation(msg.body.presentationId)
|
pres <- pod.getPresentation(msg.body.presentationId)
|
||||||
} yield {
|
} yield {
|
||||||
val presWithError = PresentationInPod(pres.id, pres.name, pres.current, pres.pages, pres.downloadable, pres.removable,
|
val presWithError = PresentationInPod(pres.id, pres.name, pres.default, pres.current, pres.pages, pres.downloadable,
|
||||||
pres.filenameConverted, pres.uploadCompleted, pres.numPages, msg.body.messageKey, errorDetails)
|
pres.downloadFileExtension, pres.removable, pres.filenameConverted, pres.uploadCompleted, pres.numPages,
|
||||||
|
msg.body.messageKey, errorDetails)
|
||||||
var pods = state.presentationPodManager.addPod(pod)
|
var pods = state.presentationPodManager.addPod(pod)
|
||||||
pods = pods.addPresentationToPod(pod.id, presWithError)
|
pods = pods.addPresentationToPod(pod.id, presWithError)
|
||||||
PresPresentationDAO.insertOrUpdate(msg.header.meetingId, presWithError)
|
|
||||||
state.update(pods)
|
state.update(pods)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PresPresentationDAO.updateErrors(msg.body.presentationId, msg.body.messageKey, errorDetails)
|
||||||
broadcastEvent(msg)
|
broadcastEvent(msg)
|
||||||
|
|
||||||
newState match {
|
newState match {
|
||||||
case Some(ns) => ns
|
case Some(ns) => ns
|
||||||
case None =>
|
case None => state
|
||||||
PresPresentationDAO.updateErrors(msg.body.presentationId, msg.body.messageKey, errorDetails)
|
|
||||||
state
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,13 @@ import org.bigbluebutton.core.models.PresentationInPod
|
|||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
trait PresentationConversionCompletedSysPubMsgHdlr {
|
trait PresentationConversionCompletedSysPubMsgHdlr {
|
||||||
|
|
||||||
this: PresentationPodHdlrs =>
|
this: PresentationPodHdlrs =>
|
||||||
|
|
||||||
def handle(
|
def handle(
|
||||||
msg: PresentationConversionCompletedSysPubMsg, state: MeetingState2x,
|
msg: PresentationConversionCompletedSysPubMsg,
|
||||||
liveMeeting: LiveMeeting, bus: MessageBus
|
state: MeetingState2x,
|
||||||
|
liveMeeting: LiveMeeting,
|
||||||
|
bus: MessageBus
|
||||||
): MeetingState2x = {
|
): MeetingState2x = {
|
||||||
|
|
||||||
val meetingId = liveMeeting.props.meetingProp.intId
|
val meetingId = liveMeeting.props.meetingProp.intId
|
||||||
@ -24,7 +25,7 @@ trait PresentationConversionCompletedSysPubMsgHdlr {
|
|||||||
pres <- pod.getPresentation(msg.body.presentation.id)
|
pres <- pod.getPresentation(msg.body.presentation.id)
|
||||||
} yield {
|
} yield {
|
||||||
val presVO = PresentationPodsApp.translatePresentationToPresentationVO(pres, temporaryPresentationId,
|
val presVO = PresentationPodsApp.translatePresentationToPresentationVO(pres, temporaryPresentationId,
|
||||||
msg.body.presentation.isInitialPresentation, msg.body.presentation.filenameConverted)
|
msg.body.presentation.defaultPresentation, msg.body.presentation.filenameConverted)
|
||||||
PresentationSender.broadcastPresentationConversionCompletedEvtMsg(
|
PresentationSender.broadcastPresentationConversionCompletedEvtMsg(
|
||||||
bus,
|
bus,
|
||||||
meetingId,
|
meetingId,
|
||||||
@ -47,13 +48,13 @@ trait PresentationConversionCompletedSysPubMsgHdlr {
|
|||||||
originalDownloadableExtension
|
originalDownloadableExtension
|
||||||
)
|
)
|
||||||
|
|
||||||
val presWithConvertedName = PresentationInPod(pres.id, pres.name, pres.current, pres.pages,
|
val presWithConvertedName = PresentationInPod(pres.id, pres.name, default = msg.body.presentation.defaultPresentation,
|
||||||
pres.downloadable, pres.removable, msg.body.presentation.filenameConverted, uploadCompleted = true, numPages = pres.numPages, errorDetails = Map.empty)
|
pres.current, pres.pages, pres.downloadable, pres.downloadFileExtension, pres.removable, msg.body.presentation.filenameConverted,
|
||||||
|
uploadCompleted = true, numPages = pres.numPages, errorDetails = Map.empty)
|
||||||
var pods = state.presentationPodManager.addPod(pod)
|
var pods = state.presentationPodManager.addPod(pod)
|
||||||
pods = pods.addPresentationToPod(pod.id, presWithConvertedName)
|
pods = pods.addPresentationToPod(pod.id, presWithConvertedName)
|
||||||
|
|
||||||
PresPresentationDAO.insertOrUpdate(meetingId, presWithConvertedName)
|
PresPresentationDAO.updatePages(presWithConvertedName)
|
||||||
|
|
||||||
state.update(pods)
|
state.update(pods)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,10 +37,6 @@ trait PresentationConversionUpdatePubMsgHdlr {
|
|||||||
bus.outGW.send(msgEvent)
|
bus.outGW.send(msgEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
val pres = new PresentationInPod(msg.body.presentationId, msg.body.presName, false, Map.empty, false,
|
|
||||||
false, uploadCompleted = false, numPages = -1, errorDetails = Map.empty)
|
|
||||||
PresPresentationDAO.insertOrUpdate(msg.header.meetingId, pres)
|
|
||||||
|
|
||||||
broadcastEvent(msg)
|
broadcastEvent(msg)
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
@ -39,11 +39,9 @@ trait PresentationHasInvalidMimeTypeErrorPubMsgHdlr {
|
|||||||
"fileExtension" -> msg.body.fileExtension
|
"fileExtension" -> msg.body.fileExtension
|
||||||
)
|
)
|
||||||
|
|
||||||
val pres = new PresentationInPod(msg.body.presentationId, msg.body.presentationName, false, Map.empty, false,
|
PresPresentationDAO.updateErrors(msg.body.presentationId, msg.body.messageKey, errorDetails)
|
||||||
false, uploadCompleted = false, numPages = -1, errorMsgKey = msg.body.messageKey, errorDetails = errorDetails)
|
|
||||||
PresPresentationDAO.insertOrUpdate(msg.header.meetingId, pres)
|
|
||||||
|
|
||||||
broadcastEvent(msg)
|
broadcastEvent(msg)
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,9 @@ trait PresentationPageConversionStartedSysMsgHdlr {
|
|||||||
podId = msg.body.podId,
|
podId = msg.body.podId,
|
||||||
presentationId = msg.body.presentationId,
|
presentationId = msg.body.presentationId,
|
||||||
current = msg.body.current,
|
current = msg.body.current,
|
||||||
|
default = msg.body.default,
|
||||||
presName = msg.body.presName,
|
presName = msg.body.presName,
|
||||||
|
presFilenameConverted = msg.body.presFilenameConverted,
|
||||||
downloadable = msg.body.downloadable,
|
downloadable = msg.body.downloadable,
|
||||||
removable = msg.body.removable,
|
removable = msg.body.removable,
|
||||||
authzToken = msg.body.authzToken,
|
authzToken = msg.body.authzToken,
|
||||||
@ -44,8 +46,8 @@ trait PresentationPageConversionStartedSysMsgHdlr {
|
|||||||
val presentationId = msg.body.presentationId
|
val presentationId = msg.body.presentationId
|
||||||
val podId = msg.body.podId
|
val podId = msg.body.podId
|
||||||
|
|
||||||
val pres = new PresentationInPod(presentationId, msg.body.presName, msg.body.current, Map.empty, downloadable,
|
val pres = new PresentationInPod(presentationId, msg.body.presName, msg.body.default, msg.body.current, Map.empty, downloadable,
|
||||||
removable, uploadCompleted = false, numPages = msg.body.numPages, errorDetails = Map.empty)
|
"", removable, filenameConverted = msg.body.presFilenameConverted, uploadCompleted = false, numPages = msg.body.numPages, errorDetails = Map.empty)
|
||||||
|
|
||||||
val newState = for {
|
val newState = for {
|
||||||
pod <- PresentationPodsApp.getPresentationPod(state, podId)
|
pod <- PresentationPodsApp.getPresentationPod(state, podId)
|
||||||
@ -59,6 +61,7 @@ trait PresentationPageConversionStartedSysMsgHdlr {
|
|||||||
state.update(pods)
|
state.update(pods)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PresPresentationDAO.updateConversionStarted(liveMeeting.props.meetingProp.intId, pres)
|
||||||
broadcastEvent(msg)
|
broadcastEvent(msg)
|
||||||
|
|
||||||
newState match {
|
newState match {
|
||||||
|
@ -3,7 +3,7 @@ package org.bigbluebutton.core.apps.presentationpod
|
|||||||
import org.bigbluebutton.common2.domain.PresentationPageVO
|
import org.bigbluebutton.common2.domain.PresentationPageVO
|
||||||
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.PresPresentationDAO
|
import org.bigbluebutton.core.db.{ PresPageDAO, PresPresentationDAO }
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.models.{ PresentationInPod, PresentationPage }
|
import org.bigbluebutton.core.models.{ PresentationInPod, PresentationPage }
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
@ -55,6 +55,7 @@ trait PresentationPageConvertedSysMsgHdlr {
|
|||||||
msg.body.page.id,
|
msg.body.page.id,
|
||||||
msg.body.page.num,
|
msg.body.page.num,
|
||||||
msg.body.page.urls,
|
msg.body.page.urls,
|
||||||
|
msg.body.page.content,
|
||||||
msg.body.page.current,
|
msg.body.page.current,
|
||||||
width = msg.body.page.width,
|
width = msg.body.page.width,
|
||||||
height = msg.body.page.height,
|
height = msg.body.page.height,
|
||||||
@ -69,7 +70,7 @@ trait PresentationPageConvertedSysMsgHdlr {
|
|||||||
var pods = state.presentationPodManager.addPod(pod)
|
var pods = state.presentationPodManager.addPod(pod)
|
||||||
pods = pods.addPresentationToPod(pod.id, newPres)
|
pods = pods.addPresentationToPod(pod.id, newPres)
|
||||||
|
|
||||||
PresPresentationDAO.insertOrUpdate(msg.header.meetingId, newPres)
|
PresPageDAO.insert(pres.id, page)
|
||||||
state.update(pods)
|
state.update(pods)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,21 +41,20 @@ trait PresentationPageCountErrorPubMsgHdlr {
|
|||||||
pod <- PresentationPodsApp.getPresentationPod(state, msg.body.podId)
|
pod <- PresentationPodsApp.getPresentationPod(state, msg.body.podId)
|
||||||
pres <- pod.getPresentation(msg.body.presentationId)
|
pres <- pod.getPresentation(msg.body.presentationId)
|
||||||
} yield {
|
} yield {
|
||||||
val presWithError = PresentationInPod(pres.id, pres.name, pres.current, pres.pages, pres.downloadable, pres.removable,
|
val presWithError = PresentationInPod(pres.id, pres.name, pres.default, pres.current, pres.pages, pres.downloadable,
|
||||||
pres.filenameConverted, pres.uploadCompleted, msg.body.numberOfPages, msg.body.messageKey, errorDetails)
|
"", pres.removable, pres.filenameConverted, pres.uploadCompleted, msg.body.numberOfPages, msg.body.messageKey, errorDetails)
|
||||||
var pods = state.presentationPodManager.addPod(pod)
|
var pods = state.presentationPodManager.addPod(pod)
|
||||||
pods = pods.addPresentationToPod(pod.id, presWithError)
|
pods = pods.addPresentationToPod(pod.id, presWithError)
|
||||||
PresPresentationDAO.insertOrUpdate(msg.header.meetingId, presWithError)
|
|
||||||
state.update(pods)
|
state.update(pods)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PresPresentationDAO.updateErrors(msg.body.presentationId, msg.body.messageKey, errorDetails)
|
||||||
broadcastEvent(msg)
|
broadcastEvent(msg)
|
||||||
|
|
||||||
newState match {
|
newState match {
|
||||||
case Some(ns) => ns
|
case Some(ns) => ns
|
||||||
case None =>
|
case None => state
|
||||||
PresPresentationDAO.updateErrors(msg.body.presentationId, msg.body.messageKey, errorDetails)
|
|
||||||
state
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ class PresentationPodHdlrs(implicit val context: ActorContext)
|
|||||||
with PresentationConversionCompletedSysPubMsgHdlr
|
with PresentationConversionCompletedSysPubMsgHdlr
|
||||||
with PdfConversionInvalidErrorSysPubMsgHdlr
|
with PdfConversionInvalidErrorSysPubMsgHdlr
|
||||||
with SetCurrentPagePubMsgHdlr
|
with SetCurrentPagePubMsgHdlr
|
||||||
with SetPresenterInPodReqMsgHdlr
|
with SetPresenterInDefaultPodInternalMsgHdlr
|
||||||
with RemovePresentationPubMsgHdlr
|
with RemovePresentationPubMsgHdlr
|
||||||
with SetPresentationDownloadablePubMsgHdlr
|
with SetPresentationDownloadablePubMsgHdlr
|
||||||
with PresentationConversionUpdatePubMsgHdlr
|
with PresentationConversionUpdatePubMsgHdlr
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.bigbluebutton.core.apps.presentationpod
|
package org.bigbluebutton.core.apps.presentationpod
|
||||||
|
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils
|
||||||
import org.bigbluebutton.common2.domain._
|
import org.bigbluebutton.common2.domain._
|
||||||
import org.bigbluebutton.core.domain._
|
import org.bigbluebutton.core.domain._
|
||||||
import org.bigbluebutton.core.models._
|
import org.bigbluebutton.core.models._
|
||||||
@ -74,7 +75,7 @@ object PresentationPodsApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def translatePresentationToPresentationVO(pres: PresentationInPod, temporaryPresentationId: String,
|
def translatePresentationToPresentationVO(pres: PresentationInPod, temporaryPresentationId: String,
|
||||||
isInitialPresentation: Boolean, filenameConverted: String): PresentationVO = {
|
defaultPresentation: Boolean, filenameConverted: String): PresentationVO = {
|
||||||
val pages = pres.pages.values.map { page =>
|
val pages = pres.pages.values.map { page =>
|
||||||
PageVO(
|
PageVO(
|
||||||
id = page.id,
|
id = page.id,
|
||||||
@ -92,7 +93,7 @@ object PresentationPodsApp {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
PresentationVO(pres.id, temporaryPresentationId, pres.name, pres.current, pages.toVector, pres.downloadable,
|
PresentationVO(pres.id, temporaryPresentationId, pres.name, pres.current, pages.toVector, pres.downloadable,
|
||||||
pres.removable, isInitialPresentation, filenameConverted)
|
pres.removable, defaultPresentation, filenameConverted)
|
||||||
}
|
}
|
||||||
|
|
||||||
def setCurrentPresentationInPod(state: MeetingState2x, podId: String, nextCurrentPresId: String): Option[PresentationPod] = {
|
def setCurrentPresentationInPod(state: MeetingState2x, podId: String, nextCurrentPresId: String): Option[PresentationPod] = {
|
||||||
@ -107,5 +108,11 @@ object PresentationPodsApp {
|
|||||||
def generateToken(podId: String, userId: String): String = {
|
def generateToken(podId: String, userId: String): String = {
|
||||||
"PresUploadToken-" + RandomStringGenerator.randomAlphanumericString(8) + podId + "-" + userId
|
"PresUploadToken-" + RandomStringGenerator.randomAlphanumericString(8) + podId + "-" + userId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def generatePresentationId(presFilename: String) = {
|
||||||
|
val timestamp = System.currentTimeMillis
|
||||||
|
DigestUtils.sha1Hex(presFilename + RandomStringGenerator.randomAlphanumericString(8)) + "-" + timestamp
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
package org.bigbluebutton.core.apps.presentationpod
|
package org.bigbluebutton.core.apps.presentationpod
|
||||||
|
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils
|
||||||
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.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.models.Users2x
|
import org.bigbluebutton.core.models.Users2x
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
|
import org.bigbluebutton.core.db.PresPresentationDAO
|
||||||
|
import org.bigbluebutton.core.util.RandomStringGenerator
|
||||||
|
|
||||||
trait PresentationUploadTokenReqMsgHdlr extends RightsManagementTrait {
|
trait PresentationUploadTokenReqMsgHdlr extends RightsManagementTrait {
|
||||||
this: PresentationPodHdlrs =>
|
this: PresentationPodHdlrs =>
|
||||||
@ -13,13 +16,13 @@ trait PresentationUploadTokenReqMsgHdlr extends RightsManagementTrait {
|
|||||||
def handle(msg: PresentationUploadTokenReqMsg, state: MeetingState2x,
|
def handle(msg: PresentationUploadTokenReqMsg, state: MeetingState2x,
|
||||||
liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
|
liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
|
||||||
|
|
||||||
def broadcastPresentationUploadTokenPassResp(msg: PresentationUploadTokenReqMsg, token: String): Unit = {
|
def broadcastPresentationUploadTokenPassResp(msg: PresentationUploadTokenReqMsg, token: String, presId: String): Unit = {
|
||||||
// send back to client
|
// send back to client
|
||||||
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||||
val envelope = BbbCoreEnvelope(PresentationUploadTokenPassRespMsg.NAME, routing)
|
val envelope = BbbCoreEnvelope(PresentationUploadTokenPassRespMsg.NAME, routing)
|
||||||
val header = BbbClientMsgHeader(PresentationUploadTokenPassRespMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
val header = BbbClientMsgHeader(PresentationUploadTokenPassRespMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||||
|
|
||||||
val body = PresentationUploadTokenPassRespMsgBody(msg.body.podId, token, msg.body.filename, msg.body.temporaryPresentationId)
|
val body = PresentationUploadTokenPassRespMsgBody(msg.body.podId, token, msg.body.filename, msg.body.uploadTemporaryId, presId)
|
||||||
val event = PresentationUploadTokenPassRespMsg(header, body)
|
val event = PresentationUploadTokenPassRespMsg(header, body)
|
||||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||||
bus.outGW.send(msgEvent)
|
bus.outGW.send(msgEvent)
|
||||||
@ -37,13 +40,13 @@ trait PresentationUploadTokenReqMsgHdlr extends RightsManagementTrait {
|
|||||||
bus.outGW.send(msgEvent)
|
bus.outGW.send(msgEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
def broadcastPresentationUploadTokenSysPubMsg(msg: PresentationUploadTokenReqMsg, token: String): Unit = {
|
def broadcastPresentationUploadTokenSysPubMsg(msg: PresentationUploadTokenReqMsg, token: String, presId: String): Unit = {
|
||||||
// send to bbb-web
|
// send to bbb-web
|
||||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||||
val envelope = BbbCoreEnvelope(PresentationUploadTokenSysPubMsg.NAME, routing)
|
val envelope = BbbCoreEnvelope(PresentationUploadTokenSysPubMsg.NAME, routing)
|
||||||
val header = BbbClientMsgHeader(PresentationUploadTokenSysPubMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
val header = BbbClientMsgHeader(PresentationUploadTokenSysPubMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||||
|
|
||||||
val body = PresentationUploadTokenSysPubMsgBody(msg.body.podId, token, msg.body.filename, liveMeeting.props.meetingProp.intId)
|
val body = PresentationUploadTokenSysPubMsgBody(msg.body.podId, token, msg.body.filename, liveMeeting.props.meetingProp.intId, presId)
|
||||||
val event = PresentationUploadTokenSysPubMsg(header, body)
|
val event = PresentationUploadTokenSysPubMsg(header, body)
|
||||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||||
|
|
||||||
@ -68,9 +71,10 @@ trait PresentationUploadTokenReqMsgHdlr extends RightsManagementTrait {
|
|||||||
log.info("handlePresentationUploadTokenReqMsg" + liveMeeting.props.meetingProp.intId +
|
log.info("handlePresentationUploadTokenReqMsg" + liveMeeting.props.meetingProp.intId +
|
||||||
" userId=" + msg.header.userId + " filename=" + msg.body.filename)
|
" userId=" + msg.header.userId + " filename=" + msg.body.filename)
|
||||||
|
|
||||||
|
val meetingId = liveMeeting.props.meetingProp.intId
|
||||||
|
|
||||||
if (liveMeeting.props.meetingProp.disabledFeatures.contains("presentation")) {
|
if (liveMeeting.props.meetingProp.disabledFeatures.contains("presentation")) {
|
||||||
broadcastPresentationUploadTokenFailResp(msg)
|
broadcastPresentationUploadTokenFailResp(msg)
|
||||||
val meetingId = liveMeeting.props.meetingProp.intId
|
|
||||||
val reason = "Presentation is disabled for this meeting"
|
val reason = "Presentation is disabled for this meeting"
|
||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||||
} else if (filterPresentationMessage(liveMeeting.users2x, msg.header.userId) &&
|
} else if (filterPresentationMessage(liveMeeting.users2x, msg.header.userId) &&
|
||||||
@ -81,8 +85,19 @@ trait PresentationUploadTokenReqMsgHdlr extends RightsManagementTrait {
|
|||||||
} else {
|
} else {
|
||||||
if (userIsAllowedToUploadInPod(msg.body.podId, msg.header.userId)) {
|
if (userIsAllowedToUploadInPod(msg.body.podId, msg.header.userId)) {
|
||||||
val token = PresentationPodsApp.generateToken(msg.body.podId, msg.header.userId)
|
val token = PresentationPodsApp.generateToken(msg.body.podId, msg.header.userId)
|
||||||
broadcastPresentationUploadTokenPassResp(msg, token)
|
val presentationId = PresentationPodsApp.generatePresentationId(msg.body.filename)
|
||||||
broadcastPresentationUploadTokenSysPubMsg(msg, token)
|
broadcastPresentationUploadTokenPassResp(msg, token, presentationId)
|
||||||
|
broadcastPresentationUploadTokenSysPubMsg(msg, token, presentationId)
|
||||||
|
|
||||||
|
PresPresentationDAO.insertToken(
|
||||||
|
meetingId,
|
||||||
|
msg.header.userId,
|
||||||
|
msg.body.uploadTemporaryId,
|
||||||
|
presentationId,
|
||||||
|
token,
|
||||||
|
msg.body.filename
|
||||||
|
)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
broadcastPresentationUploadTokenFailResp(msg)
|
broadcastPresentationUploadTokenFailResp(msg)
|
||||||
}
|
}
|
||||||
|
@ -42,21 +42,20 @@ trait PresentationUploadedFileTimeoutErrorPubMsgHdlr {
|
|||||||
pod <- PresentationPodsApp.getPresentationPod(state, msg.body.podId)
|
pod <- PresentationPodsApp.getPresentationPod(state, msg.body.podId)
|
||||||
pres <- pod.getPresentation(msg.body.presentationId)
|
pres <- pod.getPresentation(msg.body.presentationId)
|
||||||
} yield {
|
} yield {
|
||||||
val presWithError = PresentationInPod(pres.id, pres.name, pres.current, pres.pages, pres.downloadable, pres.removable,
|
val presWithError = PresentationInPod(pres.id, pres.name, pres.default, pres.current, pres.pages, pres.downloadable,
|
||||||
pres.filenameConverted, pres.uploadCompleted, pres.numPages, msg.body.messageKey, errorDetails)
|
"", pres.removable, pres.filenameConverted, pres.uploadCompleted, pres.numPages, msg.body.messageKey, errorDetails)
|
||||||
var pods = state.presentationPodManager.addPod(pod)
|
var pods = state.presentationPodManager.addPod(pod)
|
||||||
pods = pods.addPresentationToPod(pod.id, presWithError)
|
pods = pods.addPresentationToPod(pod.id, presWithError)
|
||||||
PresPresentationDAO.insertOrUpdate(msg.header.meetingId, presWithError)
|
|
||||||
state.update(pods)
|
state.update(pods)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PresPresentationDAO.updateErrors(msg.body.presentationId, msg.body.messageKey, errorDetails)
|
||||||
broadcastEvent(msg)
|
broadcastEvent(msg)
|
||||||
|
|
||||||
newState match {
|
newState match {
|
||||||
case Some(ns) => ns
|
case Some(ns) => ns
|
||||||
case None =>
|
case None => state
|
||||||
PresPresentationDAO.updateErrors(msg.body.presentationId, msg.body.messageKey, errorDetails)
|
|
||||||
state
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package org.bigbluebutton.core.apps.presentationpod
|
|||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
|
import org.bigbluebutton.core.db.PresPresentationDAO
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
@ -38,6 +39,8 @@ trait RemovePresentationPubMsgHdlr extends RightsManagementTrait {
|
|||||||
val podId = msg.body.podId
|
val podId = msg.body.podId
|
||||||
val presentationId = msg.body.presentationId
|
val presentationId = msg.body.presentationId
|
||||||
|
|
||||||
|
PresPresentationDAO.delete(presentationId)
|
||||||
|
|
||||||
val newState = for {
|
val newState = for {
|
||||||
pod <- PresentationPodsApp.getPresentationPod(state, podId)
|
pod <- PresentationPodsApp.getPresentationPod(state, podId)
|
||||||
} yield {
|
} yield {
|
||||||
|
@ -3,6 +3,7 @@ package org.bigbluebutton.core.apps.presentationpod
|
|||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
|
import org.bigbluebutton.core.db.PresPresentationDAO
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.running.LiveMeeting
|
import org.bigbluebutton.core.running.LiveMeeting
|
||||||
|
|
||||||
@ -17,8 +18,21 @@ trait SetPresentationDownloadablePubMsgHdlr extends RightsManagementTrait {
|
|||||||
val meetingId = liveMeeting.props.meetingProp.intId
|
val meetingId = liveMeeting.props.meetingProp.intId
|
||||||
|
|
||||||
if (filterPresentationMessage(liveMeeting.users2x, msg.header.userId) &&
|
if (filterPresentationMessage(liveMeeting.users2x, msg.header.userId) &&
|
||||||
permissionFailed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
|
permissionFailed(
|
||||||
val reason = "No permission to remove presentation from meeting."
|
PermissionCheck.GUEST_LEVEL,
|
||||||
|
PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId
|
||||||
|
)) {
|
||||||
|
val reason = "No permission to make presentation downloadable for meeting."
|
||||||
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||||
|
state
|
||||||
|
} else if (liveMeeting.props.meetingProp.disabledFeatures.contains("downloadPresentationOriginalFile")
|
||||||
|
&& msg.body.fileStateType == "Original") {
|
||||||
|
val reason = "Download original presentation is disabled for meeting."
|
||||||
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||||
|
state
|
||||||
|
} else if (liveMeeting.props.meetingProp.disabledFeatures.contains("downloadPresentationConvertedToPdf")
|
||||||
|
&& msg.body.fileStateType == "Converted") {
|
||||||
|
val reason = "Download converted presentation is disabled for meeting."
|
||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||||
state
|
state
|
||||||
} else {
|
} else {
|
||||||
@ -37,7 +51,9 @@ trait SetPresentationDownloadablePubMsgHdlr extends RightsManagementTrait {
|
|||||||
PresentationSender.broadcastSetPresentationDownloadableEvtMsg(bus, meetingId, pod.id,
|
PresentationSender.broadcastSetPresentationDownloadableEvtMsg(bus, meetingId, pod.id,
|
||||||
msg.header.userId, presentationId, downloadable, pres.name, downloadableExtension)
|
msg.header.userId, presentationId, downloadable, pres.name, downloadableExtension)
|
||||||
|
|
||||||
val pods = state.presentationPodManager.setPresentationDownloadableInPod(pod.id, presentationId, downloadable)
|
val pods = state.presentationPodManager.setPresentationDownloadableInPod(pod.id, presentationId, downloadable, downloadableExtension)
|
||||||
|
|
||||||
|
PresPresentationDAO.updateDownloadable(presentationId, downloadable, downloadableExtension)
|
||||||
state.update(pods)
|
state.update(pods)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
package org.bigbluebutton.core.apps.presentationpod
|
||||||
|
|
||||||
|
import org.bigbluebutton.common2.msgs._
|
||||||
|
import org.bigbluebutton.core.api.SetPresenterInDefaultPodInternalMsg
|
||||||
|
import org.bigbluebutton.core.apps.{ RightsManagementTrait }
|
||||||
|
import org.bigbluebutton.core.bus.MessageBus
|
||||||
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
|
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||||
|
import org.bigbluebutton.core.models.{ PresentationPod, Users2x }
|
||||||
|
|
||||||
|
trait SetPresenterInDefaultPodInternalMsgHdlr {
|
||||||
|
this: PresentationPodHdlrs =>
|
||||||
|
|
||||||
|
def handleSetPresenterInDefaultPodInternalMsg(
|
||||||
|
msg: SetPresenterInDefaultPodInternalMsg, state: MeetingState2x,
|
||||||
|
liveMeeting: LiveMeeting, bus: MessageBus
|
||||||
|
): MeetingState2x = {
|
||||||
|
// Swith presenter as default presenter pod has changed.
|
||||||
|
log.info("Presenter pod change will trigger a presenter change")
|
||||||
|
SetPresenterInPodActionHandler.handleAction(state, liveMeeting, bus.outGW, "", PresentationPod.DEFAULT_PRESENTATION_POD, msg.presenterId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object SetPresenterInPodActionHandler extends RightsManagementTrait {
|
||||||
|
def handleAction(
|
||||||
|
state: MeetingState2x,
|
||||||
|
liveMeeting: LiveMeeting,
|
||||||
|
outGW: OutMsgRouter,
|
||||||
|
assignedBy: String,
|
||||||
|
podId: String,
|
||||||
|
newPresenterId: String
|
||||||
|
): MeetingState2x = {
|
||||||
|
|
||||||
|
def broadcastSetPresenterInPodRespMsg(podId: String, nextPresenterId: String, requesterId: String): Unit = {
|
||||||
|
val routing = Routing.addMsgToClientRouting(
|
||||||
|
MessageTypes.BROADCAST_TO_MEETING,
|
||||||
|
liveMeeting.props.meetingProp.intId, requesterId
|
||||||
|
)
|
||||||
|
val envelope = BbbCoreEnvelope(SetPresenterInPodRespMsg.NAME, routing)
|
||||||
|
val header = BbbClientMsgHeader(SetPresenterInPodRespMsg.NAME, liveMeeting.props.meetingProp.intId, requesterId)
|
||||||
|
|
||||||
|
val body = SetPresenterInPodRespMsgBody(podId, nextPresenterId)
|
||||||
|
val event = SetPresenterInPodRespMsg(header, body)
|
||||||
|
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||||
|
outGW.send(msgEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
val newState = for {
|
||||||
|
user <- Users2x.findWithIntId(liveMeeting.users2x, newPresenterId)
|
||||||
|
pod <- PresentationPodsApp.getPresentationPod(state, podId)
|
||||||
|
} yield {
|
||||||
|
if (pod.currentPresenter != "") {
|
||||||
|
Users2x.removeUserFromPresenterGroup(liveMeeting.users2x, pod.currentPresenter)
|
||||||
|
liveMeeting.users2x.addOldPresenter(pod.currentPresenter)
|
||||||
|
}
|
||||||
|
Users2x.addUserToPresenterGroup(liveMeeting.users2x, newPresenterId)
|
||||||
|
val updatedPod = pod.setCurrentPresenter(newPresenterId)
|
||||||
|
broadcastSetPresenterInPodRespMsg(pod.id, newPresenterId, assignedBy)
|
||||||
|
val pods = state.presentationPodManager.addPod(updatedPod)
|
||||||
|
state.update(pods)
|
||||||
|
}
|
||||||
|
|
||||||
|
newState match {
|
||||||
|
case Some(ns) => ns
|
||||||
|
case None => state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,78 +0,0 @@
|
|||||||
package org.bigbluebutton.core.apps.presentationpod
|
|
||||||
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
|
||||||
import org.bigbluebutton.core.apps.users.AssignPresenterActionHandler
|
|
||||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
|
||||||
import org.bigbluebutton.core.bus.MessageBus
|
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
|
||||||
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
|
||||||
import org.bigbluebutton.core.models.{ PresentationPod, Users2x }
|
|
||||||
|
|
||||||
trait SetPresenterInPodReqMsgHdlr {
|
|
||||||
this: PresentationPodHdlrs =>
|
|
||||||
|
|
||||||
def handle(
|
|
||||||
msg: SetPresenterInPodReqMsg, state: MeetingState2x,
|
|
||||||
liveMeeting: LiveMeeting, bus: MessageBus
|
|
||||||
): MeetingState2x = {
|
|
||||||
if (msg.body.podId == PresentationPod.DEFAULT_PRESENTATION_POD) {
|
|
||||||
// Swith presenter as default presenter pod has changed.
|
|
||||||
log.info("Presenter pod change will trigger a presenter change")
|
|
||||||
AssignPresenterActionHandler.handleAction(liveMeeting, bus.outGW, msg.header.userId, msg.body.nextPresenterId)
|
|
||||||
}
|
|
||||||
SetPresenterInPodActionHandler.handleAction(state, liveMeeting, bus.outGW, msg.header.userId, msg.body.podId, msg.body.nextPresenterId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object SetPresenterInPodActionHandler extends RightsManagementTrait {
|
|
||||||
def handleAction(
|
|
||||||
state: MeetingState2x,
|
|
||||||
liveMeeting: LiveMeeting,
|
|
||||||
outGW: OutMsgRouter,
|
|
||||||
assignedBy: String,
|
|
||||||
podId: String,
|
|
||||||
newPresenterId: String
|
|
||||||
): MeetingState2x = {
|
|
||||||
|
|
||||||
if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, assignedBy)) {
|
|
||||||
val meetingId = liveMeeting.props.meetingProp.intId
|
|
||||||
val reason = "No permission to set presenter in presentation pod."
|
|
||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, assignedBy, reason, outGW, liveMeeting)
|
|
||||||
state
|
|
||||||
} else {
|
|
||||||
def broadcastSetPresenterInPodRespMsg(podId: String, nextPresenterId: String, requesterId: String): Unit = {
|
|
||||||
val routing = Routing.addMsgToClientRouting(
|
|
||||||
MessageTypes.BROADCAST_TO_MEETING,
|
|
||||||
liveMeeting.props.meetingProp.intId, requesterId
|
|
||||||
)
|
|
||||||
val envelope = BbbCoreEnvelope(SetPresenterInPodRespMsg.NAME, routing)
|
|
||||||
val header = BbbClientMsgHeader(SetPresenterInPodRespMsg.NAME, liveMeeting.props.meetingProp.intId, requesterId)
|
|
||||||
|
|
||||||
val body = SetPresenterInPodRespMsgBody(podId, nextPresenterId)
|
|
||||||
val event = SetPresenterInPodRespMsg(header, body)
|
|
||||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
|
||||||
outGW.send(msgEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
val newState = for {
|
|
||||||
user <- Users2x.findWithIntId(liveMeeting.users2x, newPresenterId)
|
|
||||||
pod <- PresentationPodsApp.getPresentationPod(state, podId)
|
|
||||||
} yield {
|
|
||||||
if (pod.currentPresenter != "") {
|
|
||||||
Users2x.removeUserFromPresenterGroup(liveMeeting.users2x, pod.currentPresenter)
|
|
||||||
liveMeeting.users2x.addOldPresenter(pod.currentPresenter)
|
|
||||||
}
|
|
||||||
Users2x.addUserToPresenterGroup(liveMeeting.users2x, newPresenterId)
|
|
||||||
val updatedPod = pod.setCurrentPresenter(newPresenterId)
|
|
||||||
broadcastSetPresenterInPodRespMsg(pod.id, newPresenterId, assignedBy)
|
|
||||||
val pods = state.presentationPodManager.addPod(updatedPod)
|
|
||||||
state.update(pods)
|
|
||||||
}
|
|
||||||
|
|
||||||
newState match {
|
|
||||||
case Some(ns) => ns
|
|
||||||
case None => state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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.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")
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,11 @@ package org.bigbluebutton.core.apps.users
|
|||||||
|
|
||||||
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.apps.groupchats.GroupChatApp
|
||||||
|
import org.bigbluebutton.core.db.ChatMessageDAO
|
||||||
|
import org.bigbluebutton.core.models.{ GroupChatFactory, GroupChatMessage, Roles, UserState, Users2x }
|
||||||
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||||
|
import org.bigbluebutton.core2.MeetingStatus2x
|
||||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||||
|
|
||||||
trait ChangeUserAwayReqMsgHdlr extends RightsManagementTrait {
|
trait ChangeUserAwayReqMsgHdlr extends RightsManagementTrait {
|
||||||
@ -30,6 +33,8 @@ trait ChangeUserAwayReqMsgHdlr extends RightsManagementTrait {
|
|||||||
outGW.send(msgEventChange)
|
outGW.send(msgEventChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
|
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
|
||||||
newUserState <- Users2x.setUserAway(liveMeeting.users2x, user.intId, msg.body.away)
|
newUserState <- Users2x.setUserAway(liveMeeting.users2x, user.intId, msg.body.away)
|
||||||
@ -44,6 +49,14 @@ trait ChangeUserAwayReqMsgHdlr extends RightsManagementTrait {
|
|||||||
outGW.send(MsgBuilder.buildUserEmojiChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.userId, "none"))
|
outGW.send(MsgBuilder.buildUserEmojiChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.userId, "none"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val msgMeta = Map(
|
||||||
|
"away" -> msg.body.away
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!(user.role == Roles.VIEWER_ROLE && user.locked && permissions.disablePubChat) && ((user.away && !msg.body.away) || (!user.away && msg.body.away))) {
|
||||||
|
ChatMessageDAO.insertSystemMsg(liveMeeting.props.meetingProp.intId, GroupChatApp.MAIN_PUBLIC_CHAT, "", GroupChatMessageType.USER_AWAY_STATUS_MSG, msgMeta, user.name)
|
||||||
|
}
|
||||||
|
|
||||||
broadcast(newUserState, msg.body.away)
|
broadcast(newUserState, msg.body.away)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,8 @@ trait RegisterUserReqMsgHdlr {
|
|||||||
|
|
||||||
val regUser = RegisteredUsers.create(msg.body.intUserId, msg.body.extUserId,
|
val regUser = RegisteredUsers.create(msg.body.intUserId, msg.body.extUserId,
|
||||||
msg.body.name, msg.body.role, msg.body.authToken, msg.body.sessionToken,
|
msg.body.name, msg.body.role, msg.body.authToken, msg.body.sessionToken,
|
||||||
msg.body.avatarURL, ColorPicker.nextColor(liveMeeting.props.meetingProp.intId), msg.body.guest, msg.body.authed, guestStatus, msg.body.excludeFromDashboard, msg.body.customParameters, false)
|
msg.body.avatarURL, ColorPicker.nextColor(liveMeeting.props.meetingProp.intId), msg.body.guest, msg.body.authed,
|
||||||
|
guestStatus, msg.body.excludeFromDashboard, msg.body.enforceLayout, msg.body.customParameters, false)
|
||||||
|
|
||||||
checkUserConcurrentAccesses(regUser)
|
checkUserConcurrentAccesses(regUser)
|
||||||
RegisteredUsers.add(liveMeeting.registeredUsers, regUser, liveMeeting.props.meetingProp.intId)
|
RegisteredUsers.add(liveMeeting.registeredUsers, regUser, liveMeeting.props.meetingProp.intId)
|
||||||
|
@ -2,15 +2,14 @@ package org.bigbluebutton.core.apps.users
|
|||||||
|
|
||||||
import org.bigbluebutton.common2.msgs.UserJoinMeetingReqMsg
|
import org.bigbluebutton.common2.msgs.UserJoinMeetingReqMsg
|
||||||
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers
|
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers
|
||||||
import org.bigbluebutton.core.db.{ UserStateDAO }
|
import org.bigbluebutton.core.db.{ UserDAO, UserStateDAO }
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.models.{ RegisteredUser, RegisteredUsers, Users2x, VoiceUsers }
|
import org.bigbluebutton.core.models._
|
||||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, MeetingActor, OutMsgRouter }
|
import org.bigbluebutton.core.running._
|
||||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
import org.bigbluebutton.core2.message.senders._
|
||||||
|
|
||||||
trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
|
trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
|
||||||
this: MeetingActor =>
|
this: MeetingActor =>
|
||||||
|
|
||||||
val liveMeeting: LiveMeeting
|
val liveMeeting: LiveMeeting
|
||||||
val outGW: OutMsgRouter
|
val outGW: OutMsgRouter
|
||||||
|
|
||||||
@ -18,41 +17,116 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
|
|||||||
log.info("Received user joined meeting. user {} meetingId={}", msg.body.userId, msg.header.meetingId)
|
log.info("Received user joined meeting. user {} meetingId={}", msg.body.userId, msg.header.meetingId)
|
||||||
|
|
||||||
Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) match {
|
Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) match {
|
||||||
case Some(reconnectingUser) =>
|
case Some(user) => handleUserReconnecting(user, msg, state)
|
||||||
if (reconnectingUser.userLeftFlag.left) {
|
case None => handleUserJoining(msg, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleUserJoining(msg: UserJoinMeetingReqMsg, state: MeetingState2x): MeetingState2x = {
|
||||||
|
|
||||||
|
val regUser = RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId, liveMeeting.registeredUsers)
|
||||||
|
log.info(s"Number of registered users [${RegisteredUsers.numRegisteredUsers(liveMeeting.registeredUsers)}]")
|
||||||
|
|
||||||
|
regUser.fold {
|
||||||
|
handleFailedUserJoin(msg, "Invalid auth token.", EjectReasonCode.VALIDATE_TOKEN)
|
||||||
|
state
|
||||||
|
} { user =>
|
||||||
|
val validationResult = for {
|
||||||
|
_ <- checkIfUserGuestStatusIsAllowed(user)
|
||||||
|
_ <- checkIfUserIsBanned(user)
|
||||||
|
_ <- checkIfUserLoggedOut(user)
|
||||||
|
_ <- validateMaxParticipants(user)
|
||||||
|
} yield user
|
||||||
|
|
||||||
|
validationResult.fold(
|
||||||
|
reason => handleFailedUserJoin(msg, reason._1, reason._2),
|
||||||
|
validUser => handleSuccessfulUserJoin(msg, validUser)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleSuccessfulUserJoin(msg: UserJoinMeetingReqMsg, regUser: RegisteredUser) = {
|
||||||
|
val newState = userJoinMeeting(outGW, msg.body.authToken, msg.body.clientType, liveMeeting, state)
|
||||||
|
updateParentMeetingWithNewListOfUsers()
|
||||||
|
notifyPreviousUsersWithSameExtId(regUser)
|
||||||
|
clearCachedVoiceUser(regUser)
|
||||||
|
clearExpiredUserState(regUser)
|
||||||
|
invalidateUserGraphqlConnection(regUser)
|
||||||
|
|
||||||
|
newState
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleFailedUserJoin(msg: UserJoinMeetingReqMsg, failReason: String, failReasonCode: String) = {
|
||||||
|
log.info("Ignoring user {} attempt to join in meeting {}. Reason Code: {}, Reason Message: {}", msg.body.userId, msg.header.meetingId, failReasonCode, failReason)
|
||||||
|
UserDAO.updateJoinError(msg.body.userId, failReasonCode, failReason)
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleUserReconnecting(user: UserState, msg: UserJoinMeetingReqMsg, state: MeetingState2x): MeetingState2x = {
|
||||||
|
if (user.userLeftFlag.left) {
|
||||||
|
resetUserLeftFlag(msg)
|
||||||
|
}
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
// User has reconnected. Just reset it's flag. ralam Oct 23, 2018
|
|
||||||
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false)
|
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false)
|
||||||
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
|
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
state
|
private def validateMaxParticipants(regUser: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
case None =>
|
val userHasJoinedAlready = RegisteredUsers.checkUserExtIdHasJoined(regUser.externId, liveMeeting.registeredUsers)
|
||||||
// Check if maxParticipants has been reached
|
val maxParticipants = liveMeeting.props.usersProp.maxUsers - 1
|
||||||
// User are able to reenter if he already joined previously with the same extId
|
|
||||||
val userHasJoinedAlready = RegisteredUsers.findWithUserId(msg.body.userId, liveMeeting.registeredUsers) match {
|
if (maxParticipants > 0 && //0 = no limit
|
||||||
case Some(regUser: RegisteredUser) => RegisteredUsers.checkUserExtIdHasJoined(regUser.externId, liveMeeting.registeredUsers)
|
RegisteredUsers.numUniqueJoinedUsers(liveMeeting.registeredUsers) >= maxParticipants &&
|
||||||
case None => false
|
!userHasJoinedAlready) {
|
||||||
|
Left(("The maximum number of participants allowed for this meeting has been reached.", EjectReasonCode.MAX_PARTICIPANTS))
|
||||||
|
} else {
|
||||||
|
Right(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val hasReachedMaxParticipants = liveMeeting.props.usersProp.maxUsers > 0 &&
|
|
||||||
RegisteredUsers.numUniqueJoinedUsers(liveMeeting.registeredUsers) >= liveMeeting.props.usersProp.maxUsers &&
|
|
||||||
userHasJoinedAlready == false
|
|
||||||
|
|
||||||
if (!hasReachedMaxParticipants) {
|
private def checkIfUserGuestStatusIsAllowed(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
val newState = userJoinMeeting(outGW, msg.body.authToken, msg.body.clientType, liveMeeting, state)
|
if (user.guestStatus != GuestStatus.ALLOW) {
|
||||||
|
Left(("User is not allowed to join", EjectReasonCode.PERMISSION_FAILED))
|
||||||
|
} else {
|
||||||
|
Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def checkIfUserIsBanned(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
|
if (user.banned) {
|
||||||
|
Left(("Banned user rejoining", EjectReasonCode.BANNED_USER_REJOINING))
|
||||||
|
} else {
|
||||||
|
Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def checkIfUserLoggedOut(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
|
if (user.loggedOut) {
|
||||||
|
Left(("User had logged out", EjectReasonCode.USER_LOGGED_OUT))
|
||||||
|
} else {
|
||||||
|
Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def updateParentMeetingWithNewListOfUsers() = {
|
||||||
if (liveMeeting.props.meetingProp.isBreakout) {
|
if (liveMeeting.props.meetingProp.isBreakout) {
|
||||||
BreakoutHdlrHelpers.updateParentMeetingWithUsers(liveMeeting, eventBus)
|
BreakoutHdlrHelpers.updateParentMeetingWithUsers(liveMeeting, eventBus)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Warn previous users that someone connected with same Id
|
private def notifyPreviousUsersWithSameExtId(regUser: RegisteredUser) = {
|
||||||
for {
|
|
||||||
regUser <- RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId,
|
|
||||||
liveMeeting.registeredUsers)
|
|
||||||
} yield {
|
|
||||||
RegisteredUsers.findAllWithExternUserId(regUser.externId, liveMeeting.registeredUsers)
|
RegisteredUsers.findAllWithExternUserId(regUser.externId, liveMeeting.registeredUsers)
|
||||||
.filter(u => u.id != regUser.id)
|
.filter(_.id != regUser.id)
|
||||||
.foreach { previousUser =>
|
.foreach { previousUser =>
|
||||||
|
sendUserConnectedNotification(previousUser, regUser, liveMeeting)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def sendUserConnectedNotification(previousUser: RegisteredUser, newUser: RegisteredUser, liveMeeting: LiveMeeting) = {
|
||||||
val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
|
val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
|
||||||
previousUser.id,
|
previousUser.id,
|
||||||
liveMeeting.props.meetingProp.intId,
|
liveMeeting.props.meetingProp.intId,
|
||||||
@ -60,22 +134,19 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
|
|||||||
"promote",
|
"promote",
|
||||||
"app.mobileAppModal.userConnectedWithSameId",
|
"app.mobileAppModal.userConnectedWithSameId",
|
||||||
"Notification to warn that user connect again from other browser/device",
|
"Notification to warn that user connect again from other browser/device",
|
||||||
Vector(regUser.name)
|
Vector(newUser.name)
|
||||||
)
|
)
|
||||||
outGW.send(notifyUserEvent)
|
outGW.send(notifyUserEvent)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
private def clearCachedVoiceUser(regUser: RegisteredUser) =
|
||||||
// fresh user joined (not due to reconnection). Clear (pop) the cached voice user
|
// fresh user joined (not due to reconnection). Clear (pop) the cached voice user
|
||||||
VoiceUsers.recoverVoiceUser(liveMeeting.voiceUsers, msg.body.userId)
|
VoiceUsers.recoverVoiceUser(liveMeeting.voiceUsers, regUser.id)
|
||||||
UserStateDAO.updateExpired(msg.body.userId, false)
|
|
||||||
|
|
||||||
newState
|
private def clearExpiredUserState(regUser: RegisteredUser) =
|
||||||
} else {
|
UserStateDAO.updateExpired(regUser.id, false)
|
||||||
log.info("Ignoring user {} attempt to join, once the meeting {} has reached max participants: {}", msg.body.userId, msg.header.meetingId, liveMeeting.props.usersProp.maxUsers)
|
|
||||||
state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private def invalidateUserGraphqlConnection(regUser: RegisteredUser) =
|
||||||
|
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, "user_joined", outGW)
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@ import org.bigbluebutton.common2.msgs.UserLeaveReqMsg
|
|||||||
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 }
|
||||||
|
import org.bigbluebutton.core2.message.senders.Sender
|
||||||
|
|
||||||
trait UserLeaveReqMsgHdlr extends HandlerHelpers {
|
trait UserLeaveReqMsgHdlr extends HandlerHelpers {
|
||||||
this: MeetingActor =>
|
this: MeetingActor =>
|
||||||
@ -30,6 +31,7 @@ trait UserLeaveReqMsgHdlr extends HandlerHelpers {
|
|||||||
ru <- RegisteredUsers.findWithUserId(msg.body.userId, liveMeeting.registeredUsers)
|
ru <- RegisteredUsers.findWithUserId(msg.body.userId, liveMeeting.registeredUsers)
|
||||||
} yield {
|
} yield {
|
||||||
RegisteredUsers.setUserLoggedOutFlag(liveMeeting.registeredUsers, ru)
|
RegisteredUsers.setUserLoggedOutFlag(liveMeeting.registeredUsers, ru)
|
||||||
|
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, ru.id, ru.sessionToken, "user_loggedout", outGW)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state
|
state
|
||||||
|
@ -2,12 +2,14 @@ package org.bigbluebutton.core.apps.users
|
|||||||
|
|
||||||
import org.apache.pekko.actor.ActorContext
|
import org.apache.pekko.actor.ActorContext
|
||||||
import org.apache.pekko.event.Logging
|
import org.apache.pekko.event.Logging
|
||||||
|
import org.bigbluebutton.Boot.eventBus
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
|
import org.bigbluebutton.core.api.{SetPresenterInDefaultPodInternalMsg}
|
||||||
import org.bigbluebutton.core.apps.ExternalVideoModel
|
import org.bigbluebutton.core.apps.ExternalVideoModel
|
||||||
import org.bigbluebutton.core.bus.InternalEventBus
|
import org.bigbluebutton.core.bus.{BigBlueButtonEvent, InternalEventBus}
|
||||||
import org.bigbluebutton.core.models._
|
import org.bigbluebutton.core.models._
|
||||||
import org.bigbluebutton.core.running.{LiveMeeting, OutMsgRouter}
|
import org.bigbluebutton.core.running.{LiveMeeting, OutMsgRouter}
|
||||||
import org.bigbluebutton.core2.message.senders.{MsgBuilder, Sender}
|
import org.bigbluebutton.core2.message.senders.{MsgBuilder}
|
||||||
import org.bigbluebutton.core.apps.screenshare.ScreenshareApp2x
|
import org.bigbluebutton.core.apps.screenshare.ScreenshareApp2x
|
||||||
import org.bigbluebutton.core.db.UserStateDAO
|
import org.bigbluebutton.core.db.UserStateDAO
|
||||||
|
|
||||||
@ -67,8 +69,8 @@ object UsersApp {
|
|||||||
moderator <- Users2x.findModerator(liveMeeting.users2x)
|
moderator <- Users2x.findModerator(liveMeeting.users2x)
|
||||||
newPresenter <- Users2x.makePresenter(liveMeeting.users2x, moderator.intId)
|
newPresenter <- Users2x.makePresenter(liveMeeting.users2x, moderator.intId)
|
||||||
} yield {
|
} yield {
|
||||||
// println(s"automaticallyAssignPresenter: moderator=${moderator} newPresenter=${newPresenter.intId}");
|
|
||||||
sendPresenterAssigned(outGW, meetingId, newPresenter.intId, newPresenter.name, newPresenter.intId)
|
sendPresenterAssigned(outGW, meetingId, newPresenter.intId, newPresenter.name, newPresenter.intId)
|
||||||
|
sendPresenterInPodReq(meetingId, newPresenter.intId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,6 +79,10 @@ object UsersApp {
|
|||||||
outGW.send(event)
|
outGW.send(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def sendPresenterInPodReq(meetingId: String, newPresenterIntId: String): Unit = {
|
||||||
|
eventBus.publish(BigBlueButtonEvent(meetingId, SetPresenterInDefaultPodInternalMsg(newPresenterIntId)))
|
||||||
|
}
|
||||||
|
|
||||||
def sendUserLeftMeetingToAllClients(outGW: OutMsgRouter, meetingId: String,
|
def sendUserLeftMeetingToAllClients(outGW: OutMsgRouter, meetingId: String,
|
||||||
userId: String, eject: Boolean = false, ejectedBy: String = "", reason: String = "", reasonCode: String = ""): Unit = {
|
userId: String, eject: Boolean = false, ejectedBy: String = "", reason: String = "", reasonCode: String = ""): Unit = {
|
||||||
// send a user left event for the clients to update
|
// send a user left event for the clients to update
|
||||||
|
@ -2,6 +2,7 @@ package org.bigbluebutton.core.apps.users
|
|||||||
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.InternalEventBus
|
import org.bigbluebutton.core.bus.InternalEventBus
|
||||||
|
import org.bigbluebutton.core.db.UserDAO
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.models._
|
import org.bigbluebutton.core.models._
|
||||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, OutMsgRouter }
|
||||||
@ -15,103 +16,79 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers {
|
|||||||
val eventBus: InternalEventBus
|
val eventBus: InternalEventBus
|
||||||
|
|
||||||
def handleValidateAuthTokenReqMsg(msg: ValidateAuthTokenReqMsg, state: MeetingState2x): MeetingState2x = {
|
def handleValidateAuthTokenReqMsg(msg: ValidateAuthTokenReqMsg, state: MeetingState2x): MeetingState2x = {
|
||||||
log.debug("RECEIVED ValidateAuthTokenReqMsg msg {}", msg)
|
log.debug(s"Received ValidateAuthTokenReqMsg msg $msg")
|
||||||
|
|
||||||
var failReason = "Invalid auth token."
|
val regUser = RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId, liveMeeting.registeredUsers)
|
||||||
var failReasonCode = EjectReasonCode.VALIDATE_TOKEN
|
log.info(s"Number of registered users [${RegisteredUsers.numRegisteredUsers(liveMeeting.registeredUsers)}]")
|
||||||
|
|
||||||
log.info("Number of registered users [{}]", RegisteredUsers.numRegisteredUsers(liveMeeting.registeredUsers))
|
regUser.fold {
|
||||||
val regUser = RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId,
|
sendFailedValidateAuthTokenRespMsg(msg, "Invalid auth token.", EjectReasonCode.VALIDATE_TOKEN)
|
||||||
liveMeeting.registeredUsers)
|
} { user =>
|
||||||
regUser match {
|
val validationResult = for {
|
||||||
case Some(u) =>
|
_ <- checkIfUserGuestStatusIsAllowed(user)
|
||||||
// Check if maxParticipants has been reached
|
_ <- checkIfUserIsBanned(user)
|
||||||
// User are able to reenter if he already joined previously with the same extId
|
_ <- checkIfUserLoggedOut(user)
|
||||||
val hasReachedMaxParticipants = liveMeeting.props.usersProp.maxUsers > 0 &&
|
_ <- validateMaxParticipants(user)
|
||||||
|
} yield user
|
||||||
|
|
||||||
|
validationResult.fold(
|
||||||
|
reason => sendFailedValidateAuthTokenRespMsg(msg, reason._1, reason._2),
|
||||||
|
validUser => sendSuccessfulValidateAuthTokenRespMsg(validUser)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
private def validateMaxParticipants(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
|
if (liveMeeting.props.usersProp.maxUsers > 0 &&
|
||||||
RegisteredUsers.numUniqueJoinedUsers(liveMeeting.registeredUsers) >= liveMeeting.props.usersProp.maxUsers &&
|
RegisteredUsers.numUniqueJoinedUsers(liveMeeting.registeredUsers) >= liveMeeting.props.usersProp.maxUsers &&
|
||||||
RegisteredUsers.checkUserExtIdHasJoined(u.externId, liveMeeting.registeredUsers) == false
|
RegisteredUsers.checkUserExtIdHasJoined(user.externId, liveMeeting.registeredUsers) == false) {
|
||||||
|
Left(("The maximum number of participants allowed for this meeting has been reached.", EjectReasonCode.MAX_PARTICIPANTS))
|
||||||
// Check if banned user is rejoining.
|
|
||||||
// Fail validation if ejected user is rejoining.
|
|
||||||
// ralam april 21, 2020
|
|
||||||
if (u.guestStatus == GuestStatus.ALLOW && !u.banned && !u.loggedOut && !hasReachedMaxParticipants) {
|
|
||||||
userValidated(u, state)
|
|
||||||
} else {
|
} else {
|
||||||
if (u.banned) {
|
Right(())
|
||||||
failReason = "Banned user rejoining"
|
|
||||||
failReasonCode = EjectReasonCode.BANNED_USER_REJOINING
|
|
||||||
} else if (u.loggedOut) {
|
|
||||||
failReason = "User had logged out"
|
|
||||||
failReasonCode = EjectReasonCode.USER_LOGGED_OUT
|
|
||||||
} else if (hasReachedMaxParticipants) {
|
|
||||||
failReason = "The maximum number of participants allowed for this meeting has been reached."
|
|
||||||
failReasonCode = EjectReasonCode.MAX_PARTICIPANTS
|
|
||||||
}
|
|
||||||
validateTokenFailed(
|
|
||||||
outGW,
|
|
||||||
meetingId = liveMeeting.props.meetingProp.intId,
|
|
||||||
userId = msg.header.userId,
|
|
||||||
authToken = msg.body.authToken,
|
|
||||||
valid = false,
|
|
||||||
waitForApproval = false,
|
|
||||||
failReason,
|
|
||||||
failReasonCode,
|
|
||||||
state
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case None =>
|
|
||||||
validateTokenFailed(
|
|
||||||
outGW,
|
|
||||||
meetingId = liveMeeting.props.meetingProp.intId,
|
|
||||||
userId = msg.header.userId,
|
|
||||||
authToken = msg.body.authToken,
|
|
||||||
valid = false,
|
|
||||||
waitForApproval = false,
|
|
||||||
failReason,
|
|
||||||
failReasonCode,
|
|
||||||
state
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def validateTokenFailed(
|
private def checkIfUserGuestStatusIsAllowed(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
outGW: OutMsgRouter,
|
if (user.guestStatus != GuestStatus.ALLOW) {
|
||||||
meetingId: String,
|
Left(("User is not allowed to join", EjectReasonCode.PERMISSION_FAILED))
|
||||||
userId: String,
|
} else {
|
||||||
authToken: String,
|
Right(())
|
||||||
valid: Boolean,
|
}
|
||||||
waitForApproval: Boolean,
|
|
||||||
reason: String,
|
|
||||||
reasonCode: String,
|
|
||||||
state: MeetingState2x
|
|
||||||
): MeetingState2x = {
|
|
||||||
val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, userId, authToken, valid, waitForApproval, 0,
|
|
||||||
0, reasonCode, reason)
|
|
||||||
outGW.send(event)
|
|
||||||
|
|
||||||
// send a system message to force disconnection
|
|
||||||
// Comment out as meteor will disconnect the client. Requested by Tiago (ralam apr 28, 2020)
|
|
||||||
//Sender.sendDisconnectClientSysMsg(meetingId, userId, SystemUser.ID, reasonCode, outGW)
|
|
||||||
|
|
||||||
state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def sendValidateAuthTokenRespMsg(meetingId: String, userId: String, authToken: String,
|
private def checkIfUserIsBanned(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
valid: Boolean, waitForApproval: Boolean, registeredOn: Long, authTokenValidatedOn: Long,
|
if (user.banned) {
|
||||||
reasonCode: String = EjectReasonCode.NOT_EJECT, reason: String = "User not ejected"): Unit = {
|
Left(("Banned user rejoining", EjectReasonCode.BANNED_USER_REJOINING))
|
||||||
val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, userId, authToken, valid, waitForApproval, registeredOn,
|
} else {
|
||||||
authTokenValidatedOn, reasonCode, reason)
|
Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def checkIfUserLoggedOut(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
|
if (user.loggedOut) {
|
||||||
|
Left(("User had logged out", EjectReasonCode.USER_LOGGED_OUT))
|
||||||
|
} else {
|
||||||
|
Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def sendFailedValidateAuthTokenRespMsg(msg: ValidateAuthTokenReqMsg, failReason: String, failReasonCode: String) = {
|
||||||
|
UserDAO.updateJoinError(msg.body.userId, failReasonCode, failReason)
|
||||||
|
|
||||||
|
val event = MsgBuilder.buildValidateAuthTokenRespMsg(liveMeeting.props.meetingProp.intId, msg.header.userId, msg.body.authToken, false, false, 0,
|
||||||
|
0, failReasonCode, failReason)
|
||||||
outGW.send(event)
|
outGW.send(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
def userValidated(user: RegisteredUser, state: MeetingState2x): MeetingState2x = {
|
def sendSuccessfulValidateAuthTokenRespMsg(user: RegisteredUser) = {
|
||||||
val meetingId = liveMeeting.props.meetingProp.intId
|
val meetingId = liveMeeting.props.meetingProp.intId
|
||||||
val updatedUser = RegisteredUsers.updateUserLastAuthTokenValidated(liveMeeting.registeredUsers, user)
|
val updatedUser = RegisteredUsers.updateUserLastAuthTokenValidated(liveMeeting.registeredUsers, user)
|
||||||
|
|
||||||
sendValidateAuthTokenRespMsg(meetingId, updatedUser.id, updatedUser.authToken, valid = true, waitForApproval = false, updatedUser.registeredOn, updatedUser.lastAuthTokenValidatedOn)
|
val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, updatedUser.id, updatedUser.authToken, true, false, updatedUser.registeredOn,
|
||||||
state
|
updatedUser.lastAuthTokenValidatedOn, EjectReasonCode.NOT_EJECT, "User not ejected")
|
||||||
|
outGW.send(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
def sendAllUsersInMeeting(requesterId: String): Unit = {
|
def sendAllUsersInMeeting(requesterId: String): Unit = {
|
||||||
|
@ -33,8 +33,8 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
|
|||||||
|
|
||||||
def registerUserInRegisteredUsers() = {
|
def registerUserInRegisteredUsers() = {
|
||||||
val regUser = RegisteredUsers.create(msg.body.intId, msg.body.voiceUserId,
|
val regUser = RegisteredUsers.create(msg.body.intId, msg.body.voiceUserId,
|
||||||
msg.body.callerIdName, Roles.VIEWER_ROLE, "", "", "", userColor,
|
msg.body.callerIdName, Roles.VIEWER_ROLE, msg.body.intId, "", "", userColor,
|
||||||
true, true, GuestStatus.WAIT, true, Map(), false)
|
true, true, GuestStatus.WAIT, true, "", Map(), false)
|
||||||
RegisteredUsers.add(liveMeeting.registeredUsers, regUser, liveMeeting.props.meetingProp.intId)
|
RegisteredUsers.add(liveMeeting.registeredUsers, regUser, liveMeeting.props.meetingProp.intId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
|
|||||||
locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin,
|
locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin,
|
||||||
avatar = "",
|
avatar = "",
|
||||||
color = userColor,
|
color = userColor,
|
||||||
clientType = "",
|
clientType = if (isDialInUser) "dial-in-user" else "",
|
||||||
pickExempted = false,
|
pickExempted = false,
|
||||||
userLeftFlag = UserLeftFlag(false, 0)
|
userLeftFlag = UserLeftFlag(false, 0)
|
||||||
)
|
)
|
||||||
|
@ -4,6 +4,7 @@ import org.bigbluebutton.common2.msgs._
|
|||||||
import org.bigbluebutton.core.models._
|
import org.bigbluebutton.core.models._
|
||||||
import org.bigbluebutton.core.apps.users.UsersApp
|
import org.bigbluebutton.core.apps.users.UsersApp
|
||||||
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers
|
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers
|
||||||
|
import org.bigbluebutton.core.db.UserDAO
|
||||||
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
|
||||||
|
|
||||||
trait UserLeftVoiceConfEvtMsgHdlr {
|
trait UserLeftVoiceConfEvtMsgHdlr {
|
||||||
@ -39,6 +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)
|
||||||
VoiceApp.removeUserFromVoiceConf(liveMeeting, outGW, msg.body.voiceUserId)
|
VoiceApp.removeUserFromVoiceConf(liveMeeting, outGW, msg.body.voiceUserId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,8 +11,10 @@ import org.bigbluebutton.common2.msgs._
|
|||||||
import org.bigbluebutton.core.running.{LiveMeeting, MeetingActor, OutMsgRouter}
|
import org.bigbluebutton.core.running.{LiveMeeting, MeetingActor, OutMsgRouter}
|
||||||
import org.bigbluebutton.core.models._
|
import org.bigbluebutton.core.models._
|
||||||
import org.bigbluebutton.core.apps.users.UsersApp
|
import org.bigbluebutton.core.apps.users.UsersApp
|
||||||
|
import org.bigbluebutton.core.db.{UserDAO, UserVoiceDAO}
|
||||||
import org.bigbluebutton.core.util.ColorPicker
|
import org.bigbluebutton.core.util.ColorPicker
|
||||||
import org.bigbluebutton.core.util.TimeUtil
|
import org.bigbluebutton.core.util.TimeUtil
|
||||||
|
|
||||||
import scala.collection.immutable.Map
|
import scala.collection.immutable.Map
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
@ -323,6 +325,8 @@ object VoiceApp extends SystemConfiguration {
|
|||||||
uuid
|
uuid
|
||||||
)
|
)
|
||||||
VoiceUsers.add(liveMeeting.voiceUsers, voiceUserState)
|
VoiceUsers.add(liveMeeting.voiceUsers, voiceUserState)
|
||||||
|
UserVoiceDAO.update(voiceUserState)
|
||||||
|
UserDAO.updateVoiceUserJoined(voiceUserState)
|
||||||
|
|
||||||
broadcastEvent(voiceUserState)
|
broadcastEvent(voiceUserState)
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package org.bigbluebutton.core.apps.voice
|
package org.bigbluebutton.core.apps.voice
|
||||||
|
|
||||||
import org.bigbluebutton.common2.msgs.{ BbbClientMsgHeader, BbbCommonEnvCoreMsg, BbbCoreEnvelope, MessageTypes, Routing, VoiceCallStateEvtMsg, VoiceCallStateEvtMsgBody, VoiceConfCallStateEvtMsg }
|
import org.bigbluebutton.common2.msgs.{ BbbClientMsgHeader, BbbCommonEnvCoreMsg, BbbCoreEnvelope, MessageTypes, Routing, VoiceCallStateEvtMsg, VoiceCallStateEvtMsgBody, VoiceConfCallStateEvtMsg }
|
||||||
import org.bigbluebutton.core.models.{ VoiceUserState, VoiceUsers }
|
import org.bigbluebutton.core.db.{ UserVoiceConfStateDAO, UserVoiceDAO }
|
||||||
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
|
||||||
|
|
||||||
trait VoiceConfCallStateEvtMsgHdlr {
|
trait VoiceConfCallStateEvtMsgHdlr {
|
||||||
@ -38,5 +38,9 @@ trait VoiceConfCallStateEvtMsgHdlr {
|
|||||||
val event = VoiceCallStateEvtMsg(header, body)
|
val event = VoiceCallStateEvtMsg(header, body)
|
||||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||||
outGW.send(msgEvent)
|
outGW.send(msgEvent)
|
||||||
|
|
||||||
|
if (msg.body.userId.nonEmpty) {
|
||||||
|
UserVoiceConfStateDAO.insertOrUpdate(msg.body.userId, msg.body.voiceConf, msg.body.callSession, msg.body.clientSession, msg.body.callState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,9 @@ package org.bigbluebutton.core.apps.webcam
|
|||||||
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
|
||||||
|
import org.bigbluebutton.core.db.MeetingUsersPoliciesDAO
|
||||||
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
|
||||||
|
|
||||||
trait UpdateWebcamsOnlyForModeratorCmdMsgHdlr {
|
trait UpdateWebcamsOnlyForModeratorCmdMsgHdlr {
|
||||||
this: WebcamApp2x =>
|
this: WebcamApp2x =>
|
||||||
@ -50,6 +51,8 @@ trait UpdateWebcamsOnlyForModeratorCmdMsgHdlr {
|
|||||||
case Some(value) => {
|
case Some(value) => {
|
||||||
log.info(s"Change webcams only for moderator status. meetingId=${meetingId} value=${value}")
|
log.info(s"Change webcams only for moderator status. meetingId=${meetingId} value=${value}")
|
||||||
|
|
||||||
|
MeetingUsersPoliciesDAO.updateWebcamsOnlyForModerator(meetingId, msg.body.webcamsOnlyForModerator)
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
|
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
|
||||||
meetingId,
|
meetingId,
|
||||||
|
@ -38,7 +38,16 @@ trait DeleteWhiteboardAnnotationsPubMsgHdlr extends RightsManagementTrait {
|
|||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val deletedAnnotations = deleteWhiteboardAnnotations(msg.body.whiteboardId, msg.header.userId, msg.body.annotationsIds, liveMeeting, isUserAmongPresenters, isUserModerator)
|
|
||||||
|
val annotationsIds = {
|
||||||
|
if (msg.body.annotationsIds.size > 0) {
|
||||||
|
msg.body.annotationsIds
|
||||||
|
} else {
|
||||||
|
getWhiteboardAnnotations(msg.body.whiteboardId, liveMeeting).map(a => a.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val deletedAnnotations = deleteWhiteboardAnnotations(msg.body.whiteboardId, msg.header.userId, annotationsIds, liveMeeting, isUserAmongPresenters, isUserModerator)
|
||||||
if (!deletedAnnotations.isEmpty) {
|
if (!deletedAnnotations.isEmpty) {
|
||||||
broadcastEvent(msg, deletedAnnotations)
|
broadcastEvent(msg, deletedAnnotations)
|
||||||
}
|
}
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
package org.bigbluebutton.core.db
|
|
||||||
|
|
||||||
import slick.jdbc.PostgresProfile.api._
|
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
import scala.util.{ Failure, Success }
|
|
||||||
|
|
||||||
case class AudioCaptionDbModel(
|
|
||||||
transcriptId: String,
|
|
||||||
meetingId: String,
|
|
||||||
userId: String,
|
|
||||||
transcript: String,
|
|
||||||
createdAt: java.sql.Timestamp
|
|
||||||
)
|
|
||||||
|
|
||||||
class AudioCaptionTableDef(tag: Tag) extends Table[AudioCaptionDbModel](tag, None, "audio_caption") {
|
|
||||||
val transcriptId = column[String]("transcriptId", O.PrimaryKey)
|
|
||||||
val meetingId = column[String]("meetingId")
|
|
||||||
val userId = column[String]("userId")
|
|
||||||
val transcript = column[String]("transcript")
|
|
||||||
val createdAt = column[java.sql.Timestamp]("createdAt")
|
|
||||||
def * = (transcriptId, meetingId, userId, transcript, createdAt) <> (AudioCaptionDbModel.tupled, AudioCaptionDbModel.unapply)
|
|
||||||
}
|
|
||||||
|
|
||||||
object AudioCaptionDAO {
|
|
||||||
|
|
||||||
def insertOrUpdateAudioCaption(transcriptId: String, meetingId: String, userId: String, transcript: String, createdAt: java.sql.Timestamp) = {
|
|
||||||
DatabaseConnection.db.run(
|
|
||||||
TableQuery[AudioCaptionTableDef].insertOrUpdate(
|
|
||||||
AudioCaptionDbModel(
|
|
||||||
transcriptId = transcriptId,
|
|
||||||
meetingId = meetingId,
|
|
||||||
userId = userId,
|
|
||||||
transcript = transcript,
|
|
||||||
createdAt = createdAt
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).onComplete {
|
|
||||||
case Success(_) => DatabaseConnection.logger.debug(s"Upserted audio caption with ID $transcriptId on AudioCaption table")
|
|
||||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error upserting audio caption on AudioCaption: $e")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -175,10 +175,11 @@ object BreakoutRoomDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def updateRoomsDuration(meetingId: String, newDurationInSeconds: Int) = {
|
def updateRoomsDuration(parentMeetingId: String, newDurationInSeconds: Int) = {
|
||||||
DatabaseConnection.db.run(
|
DatabaseConnection.db.run(
|
||||||
TableQuery[BreakoutRoomDbTableDef]
|
TableQuery[BreakoutRoomDbTableDef]
|
||||||
.filter(_.parentMeetingId === meetingId)
|
.filter(_.parentMeetingId === parentMeetingId)
|
||||||
|
.filter(_.endedAt.isEmpty)
|
||||||
.map(u => u.durationInSeconds)
|
.map(u => u.durationInSeconds)
|
||||||
.update(newDurationInSeconds)
|
.update(newDurationInSeconds)
|
||||||
).onComplete {
|
).onComplete {
|
||||||
|
104
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/CaptionDAO.scala
Executable file
104
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/CaptionDAO.scala
Executable file
@ -0,0 +1,104 @@
|
|||||||
|
package org.bigbluebutton.core.db
|
||||||
|
|
||||||
|
import slick.jdbc.PostgresProfile.api._
|
||||||
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
import scala.util.{ Failure, Success }
|
||||||
|
|
||||||
|
case class CaptionDbModel(
|
||||||
|
captionId: String,
|
||||||
|
meetingId: String,
|
||||||
|
captionType: String,
|
||||||
|
userId: String,
|
||||||
|
lang: String,
|
||||||
|
captionText: String,
|
||||||
|
createdAt: java.sql.Timestamp
|
||||||
|
)
|
||||||
|
|
||||||
|
class CaptionTableDef(tag: Tag) extends Table[CaptionDbModel](tag, None, "caption") {
|
||||||
|
val captionId = column[String]("captionId", O.PrimaryKey)
|
||||||
|
val meetingId = column[String]("meetingId")
|
||||||
|
val captionType = column[String]("captionType")
|
||||||
|
val userId = column[String]("userId")
|
||||||
|
val lang = column[String]("lang")
|
||||||
|
val captionText = column[String]("captionText")
|
||||||
|
val createdAt = column[java.sql.Timestamp]("createdAt")
|
||||||
|
def * = (captionId, meetingId, captionType, userId, lang, captionText, createdAt) <> (CaptionDbModel.tupled, CaptionDbModel.unapply)
|
||||||
|
}
|
||||||
|
|
||||||
|
object CaptionTypes {
|
||||||
|
val AUDIO_TRANSCRIPTION = "AUDIO_TRANSCRIPTION"
|
||||||
|
val TYPED = "TYPED"
|
||||||
|
}
|
||||||
|
|
||||||
|
object CaptionDAO {
|
||||||
|
|
||||||
|
def insertOrUpdateAudioCaption(captionId: String, meetingId: String, userId: String, transcript: String, lang: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[CaptionTableDef].insertOrUpdate(
|
||||||
|
CaptionDbModel(
|
||||||
|
captionId = captionId,
|
||||||
|
meetingId = meetingId,
|
||||||
|
captionType = CaptionTypes.AUDIO_TRANSCRIPTION,
|
||||||
|
userId = userId,
|
||||||
|
lang = lang,
|
||||||
|
captionText = transcript,
|
||||||
|
createdAt = new java.sql.Timestamp(System.currentTimeMillis())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).onComplete {
|
||||||
|
case Success(_) => DatabaseConnection.logger.debug(s"Upserted caption with ID $captionId on Caption table")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error upserting caption on Caption: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def insertOrUpdatePadCaption(meetingId: String, locale: String, userId: String, text: String) = {
|
||||||
|
|
||||||
|
val lines: Array[String] = text.split("\\r?\\n").filter(_.trim.nonEmpty)
|
||||||
|
val lastTwoLines = lines.takeRight(2)
|
||||||
|
|
||||||
|
val actions: Seq[DBIO[Int]] = lastTwoLines.map { line =>
|
||||||
|
sqlu"""
|
||||||
|
WITH upsert AS (
|
||||||
|
UPDATE caption
|
||||||
|
SET "captionText"=${line},
|
||||||
|
"createdAt" = current_timestamp
|
||||||
|
WHERE "captionId" in (
|
||||||
|
SELECT "captionId"
|
||||||
|
FROM (
|
||||||
|
SELECT "captionId", "captionText", "createdAt"
|
||||||
|
FROM caption
|
||||||
|
WHERE "meetingId" = ${meetingId}
|
||||||
|
AND lang = ${locale}
|
||||||
|
AND "captionType" = ${CaptionTypes.TYPED}
|
||||||
|
order by "createdAt" desc
|
||||||
|
limit 2
|
||||||
|
) a
|
||||||
|
WHERE ${line} != "captionText"
|
||||||
|
AND (${line} LIKE "captionText" || '%' or "captionText" LIKE ${line} || '%')
|
||||||
|
AND ABS(length("captionText") - length(${line})) < 25
|
||||||
|
ORDER BY "createdAt" desc
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
RETURNING *)
|
||||||
|
INSERT INTO caption ("captionId", "meetingId", "captionType", "userId", "lang", "captionText", "createdAt")
|
||||||
|
SELECT md5(random()::text || clock_timestamp()::text), ${meetingId}, 'TYPED', ${userId}, ${locale}, ${line}, current_timestamp
|
||||||
|
WHERE NOT EXISTS (SELECT * FROM upsert)
|
||||||
|
AND ${line} NOT IN (SELECT "captionText"
|
||||||
|
FROM caption
|
||||||
|
WHERE "meetingId" = ${meetingId}
|
||||||
|
AND lang = ${locale}
|
||||||
|
AND "captionType" = ${CaptionTypes.TYPED}
|
||||||
|
order by "createdAt" desc
|
||||||
|
limit 2
|
||||||
|
)"""
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseConnection.db.run(DBIO.sequence(actions).transactionally).onComplete {
|
||||||
|
case Success(rowsAffected) =>
|
||||||
|
val total = rowsAffected.sum
|
||||||
|
DatabaseConnection.logger.debug(s"$total row(s) affected in caption table!")
|
||||||
|
case Failure(e) =>
|
||||||
|
DatabaseConnection.logger.error(s"Error executing action: ", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ case class ChatMessageDbModel(
|
|||||||
chatId: String,
|
chatId: String,
|
||||||
meetingId: String,
|
meetingId: String,
|
||||||
correlationId: String,
|
correlationId: String,
|
||||||
createdTime: Long,
|
createdAt: java.sql.Timestamp,
|
||||||
chatEmphasizedText: Boolean,
|
chatEmphasizedText: Boolean,
|
||||||
message: String,
|
message: String,
|
||||||
messageType: String,
|
messageType: String,
|
||||||
@ -26,7 +26,7 @@ class ChatMessageDbTableDef(tag: Tag) extends Table[ChatMessageDbModel](tag, Non
|
|||||||
val chatId = column[String]("chatId")
|
val chatId = column[String]("chatId")
|
||||||
val meetingId = column[String]("meetingId")
|
val meetingId = column[String]("meetingId")
|
||||||
val correlationId = column[String]("correlationId")
|
val correlationId = column[String]("correlationId")
|
||||||
val createdTime = column[Long]("createdTime")
|
val createdAt = column[java.sql.Timestamp]("createdAt")
|
||||||
val chatEmphasizedText = column[Boolean]("chatEmphasizedText")
|
val chatEmphasizedText = column[Boolean]("chatEmphasizedText")
|
||||||
val message = column[String]("message")
|
val message = column[String]("message")
|
||||||
val messageType = column[String]("messageType")
|
val messageType = column[String]("messageType")
|
||||||
@ -37,7 +37,7 @@ class ChatMessageDbTableDef(tag: Tag) extends Table[ChatMessageDbModel](tag, Non
|
|||||||
// val chat = foreignKey("chat_message_chat_fk", (chatId, meetingId), ChatTable.chats)(c => (c.chatId, c.meetingId), onDelete = ForeignKeyAction.Cascade)
|
// val chat = foreignKey("chat_message_chat_fk", (chatId, meetingId), ChatTable.chats)(c => (c.chatId, c.meetingId), onDelete = ForeignKeyAction.Cascade)
|
||||||
// val sender = foreignKey("chat_message_sender_fk", senderId, UserTable.users)(_.userId, onDelete = ForeignKeyAction.SetNull)
|
// val sender = foreignKey("chat_message_sender_fk", senderId, UserTable.users)(_.userId, onDelete = ForeignKeyAction.SetNull)
|
||||||
|
|
||||||
override def * = (messageId, chatId, meetingId, correlationId, createdTime, chatEmphasizedText, message, messageType, messageMetadata, senderId, senderName, senderRole) <> (ChatMessageDbModel.tupled, ChatMessageDbModel.unapply)
|
override def * = (messageId, chatId, meetingId, correlationId, createdAt, chatEmphasizedText, message, messageType, messageMetadata, senderId, senderName, senderRole) <> (ChatMessageDbModel.tupled, ChatMessageDbModel.unapply)
|
||||||
}
|
}
|
||||||
|
|
||||||
object ChatMessageDAO {
|
object ChatMessageDAO {
|
||||||
@ -49,7 +49,7 @@ object ChatMessageDAO {
|
|||||||
chatId = chatId,
|
chatId = chatId,
|
||||||
meetingId = meetingId,
|
meetingId = meetingId,
|
||||||
correlationId = groupChatMessage.correlationId,
|
correlationId = groupChatMessage.correlationId,
|
||||||
createdTime = groupChatMessage.timestamp,
|
createdAt = new java.sql.Timestamp(System.currentTimeMillis()),
|
||||||
chatEmphasizedText = groupChatMessage.chatEmphasizedText,
|
chatEmphasizedText = groupChatMessage.chatEmphasizedText,
|
||||||
message = groupChatMessage.message,
|
message = groupChatMessage.message,
|
||||||
messageType = "default",
|
messageType = "default",
|
||||||
@ -78,11 +78,11 @@ object ChatMessageDAO {
|
|||||||
chatId = chatId,
|
chatId = chatId,
|
||||||
meetingId = meetingId,
|
meetingId = meetingId,
|
||||||
correlationId = "",
|
correlationId = "",
|
||||||
createdTime = System.currentTimeMillis(),
|
createdAt = new java.sql.Timestamp(System.currentTimeMillis()),
|
||||||
chatEmphasizedText = false,
|
chatEmphasizedText = false,
|
||||||
message = message,
|
message = message,
|
||||||
messageType = messageType,
|
messageType = messageType,
|
||||||
messageMetadata = Some(JsonUtils.mapToJson(messageMetadata)),
|
messageMetadata = Some(JsonUtils.mapToJson(messageMetadata).compactPrint),
|
||||||
senderId = None,
|
senderId = None,
|
||||||
senderName = senderName,
|
senderName = senderName,
|
||||||
senderRole = None
|
senderRole = None
|
||||||
|
@ -11,8 +11,9 @@ case class ChatUserDbModel(
|
|||||||
chatId: String,
|
chatId: String,
|
||||||
meetingId: String,
|
meetingId: String,
|
||||||
userId: String,
|
userId: String,
|
||||||
lastSeenAt: Long,
|
lastSeenAt: Option[java.sql.Timestamp],
|
||||||
typingAt: Option[java.sql.Timestamp],
|
startedTypingAt: Option[java.sql.Timestamp],
|
||||||
|
lastTypingAt: Option[java.sql.Timestamp],
|
||||||
visible: Boolean
|
visible: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,13 +21,14 @@ class ChatUserDbTableDef(tag: Tag) extends Table[ChatUserDbModel](tag, None, "ch
|
|||||||
val chatId = column[String]("chatId", O.PrimaryKey)
|
val chatId = column[String]("chatId", O.PrimaryKey)
|
||||||
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||||
val userId = column[String]("userId", O.PrimaryKey)
|
val userId = column[String]("userId", O.PrimaryKey)
|
||||||
val lastSeenAt = column[Long]("lastSeenAt")
|
val lastSeenAt = column[Option[java.sql.Timestamp]]("lastSeenAt")
|
||||||
val typingAt = column[Option[java.sql.Timestamp]]("typingAt")
|
val startedTypingAt = column[Option[java.sql.Timestamp]]("startedTypingAt")
|
||||||
|
val lastTypingAt = column[Option[java.sql.Timestamp]]("lastTypingAt")
|
||||||
val visible = column[Boolean]("visible")
|
val visible = column[Boolean]("visible")
|
||||||
// val chat = foreignKey("chat_message_chat_fk", (chatId, meetingId), ChatTable.chats)(c => (c.chatId, c.meetingId), onDelete = ForeignKeyAction.Cascade)
|
// val chat = foreignKey("chat_message_chat_fk", (chatId, meetingId), ChatTable.chats)(c => (c.chatId, c.meetingId), onDelete = ForeignKeyAction.Cascade)
|
||||||
// val sender = foreignKey("chat_message_sender_fk", senderId, UserTable.users)(_.userId, onDelete = ForeignKeyAction.SetNull)
|
// val sender = foreignKey("chat_message_sender_fk", senderId, UserTable.users)(_.userId, onDelete = ForeignKeyAction.SetNull)
|
||||||
|
|
||||||
override def * = (chatId, meetingId, userId, lastSeenAt, typingAt, visible) <> (ChatUserDbModel.tupled, ChatUserDbModel.unapply)
|
override def * = (chatId, meetingId, userId, lastSeenAt, startedTypingAt, lastTypingAt, visible) <> (ChatUserDbModel.tupled, ChatUserDbModel.unapply)
|
||||||
}
|
}
|
||||||
|
|
||||||
object ChatUserDAO {
|
object ChatUserDAO {
|
||||||
@ -46,8 +48,9 @@ object ChatUserDAO {
|
|||||||
userId = userId,
|
userId = userId,
|
||||||
chatId = chatId,
|
chatId = chatId,
|
||||||
meetingId = meetingId,
|
meetingId = meetingId,
|
||||||
lastSeenAt = 0,
|
lastSeenAt = None,
|
||||||
typingAt = None,
|
startedTypingAt = None,
|
||||||
|
lastTypingAt = None,
|
||||||
visible = visible
|
visible = visible
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -63,11 +66,11 @@ object ChatUserDAO {
|
|||||||
.filter(_.meetingId === meetingId)
|
.filter(_.meetingId === meetingId)
|
||||||
.filter(_.chatId === (if (chatId == "public") "MAIN-PUBLIC-GROUP-CHAT" else chatId))
|
.filter(_.chatId === (if (chatId == "public") "MAIN-PUBLIC-GROUP-CHAT" else chatId))
|
||||||
.filter(_.userId === userId)
|
.filter(_.userId === userId)
|
||||||
.map(u => (u.typingAt))
|
.map(u => (u.lastTypingAt))
|
||||||
.update(Some(new java.sql.Timestamp(System.currentTimeMillis())))
|
.update(Some(new java.sql.Timestamp(System.currentTimeMillis())))
|
||||||
).onComplete {
|
).onComplete {
|
||||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated typingAt on chat_user table!")
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated lastTypingAt on chat_user table!")
|
||||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating typingAt on chat_user table: $e")
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating lastTypingAt on chat_user table: $e")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package org.bigbluebutton.core.db
|
||||||
|
|
||||||
|
import PostgresProfile.api._
|
||||||
|
import slick.lifted.ProvenShape
|
||||||
|
import spray.json.JsValue
|
||||||
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
|
case class MeetingClientSettingsDbModel(
|
||||||
|
meetingId: String,
|
||||||
|
clientSettingsJson: JsValue,
|
||||||
|
)
|
||||||
|
|
||||||
|
class MeetingClientSettingsDbTableDef(tag: Tag) extends Table[MeetingClientSettingsDbModel](tag, "meeting_clientSettings") {
|
||||||
|
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||||
|
val clientSettingsJson = column[JsValue]("clientSettingsJson")
|
||||||
|
|
||||||
|
override def * : ProvenShape[MeetingClientSettingsDbModel] = (meetingId, clientSettingsJson) <> (MeetingClientSettingsDbModel.tupled, MeetingClientSettingsDbModel.unapply)
|
||||||
|
}
|
||||||
|
|
||||||
|
object MeetingClientSettingsDAO {
|
||||||
|
def insert(meetingId: String, clientSettingsJson: JsValue) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[MeetingClientSettingsDbTableDef].forceInsert(
|
||||||
|
MeetingClientSettingsDbModel(
|
||||||
|
meetingId = meetingId,
|
||||||
|
clientSettingsJson = clientSettingsJson
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => {
|
||||||
|
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted in MeetingClientSettings table!")
|
||||||
|
}
|
||||||
|
case Failure(e) => DatabaseConnection.logger.error(s"Error inserting MeetingClientSettings: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -20,7 +20,7 @@ case class MeetingDbModel(
|
|||||||
presentationUploadExternalUrl: String,
|
presentationUploadExternalUrl: String,
|
||||||
learningDashboardAccessToken: String,
|
learningDashboardAccessToken: String,
|
||||||
createdTime: Long,
|
createdTime: Long,
|
||||||
duration: Int
|
durationInSeconds: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
class MeetingDbTableDef(tag: Tag) extends Table[MeetingDbModel](tag, None, "meeting") {
|
class MeetingDbTableDef(tag: Tag) extends Table[MeetingDbModel](tag, None, "meeting") {
|
||||||
@ -37,7 +37,7 @@ class MeetingDbTableDef(tag: Tag) extends Table[MeetingDbModel](tag, None, "meet
|
|||||||
presentationUploadExternalUrl,
|
presentationUploadExternalUrl,
|
||||||
learningDashboardAccessToken,
|
learningDashboardAccessToken,
|
||||||
createdTime,
|
createdTime,
|
||||||
duration
|
durationInSeconds
|
||||||
) <> (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")
|
||||||
@ -51,11 +51,11 @@ 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 createdTime = column[Long]("createdTime")
|
val createdTime = column[Long]("createdTime")
|
||||||
val duration = column[Int]("duration")
|
val durationInSeconds = column[Int]("durationInSeconds")
|
||||||
}
|
}
|
||||||
|
|
||||||
object MeetingDAO {
|
object MeetingDAO {
|
||||||
def insert(meetingProps: DefaultProps) = {
|
def insert(meetingProps: DefaultProps, clientSettings: Map[String, Object]) = {
|
||||||
DatabaseConnection.db.run(
|
DatabaseConnection.db.run(
|
||||||
TableQuery[MeetingDbTableDef].forceInsert(
|
TableQuery[MeetingDbTableDef].forceInsert(
|
||||||
MeetingDbModel(
|
MeetingDbModel(
|
||||||
@ -71,28 +71,46 @@ object MeetingDAO {
|
|||||||
presentationUploadExternalUrl = meetingProps.meetingProp.presentationUploadExternalUrl,
|
presentationUploadExternalUrl = meetingProps.meetingProp.presentationUploadExternalUrl,
|
||||||
learningDashboardAccessToken = meetingProps.password.learningDashboardAccessToken,
|
learningDashboardAccessToken = meetingProps.password.learningDashboardAccessToken,
|
||||||
createdTime = meetingProps.durationProps.createdTime,
|
createdTime = meetingProps.durationProps.createdTime,
|
||||||
duration = meetingProps.durationProps.duration
|
durationInSeconds = meetingProps.durationProps.duration * 60
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).onComplete {
|
).onComplete {
|
||||||
case Success(rowsAffected) => {
|
case Success(rowsAffected) => {
|
||||||
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted in Meeting table!")
|
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted in Meeting table!")
|
||||||
|
ChatDAO.insert(meetingProps.meetingProp.intId, GroupChatApp.createDefaultPublicGroupChat())
|
||||||
MeetingUsersPoliciesDAO.insert(meetingProps.meetingProp.intId, meetingProps.usersProp)
|
MeetingUsersPoliciesDAO.insert(meetingProps.meetingProp.intId, meetingProps.usersProp)
|
||||||
MeetingLockSettingsDAO.insert(meetingProps.meetingProp.intId, meetingProps.lockSettingsProps)
|
MeetingLockSettingsDAO.insert(meetingProps.meetingProp.intId, meetingProps.lockSettingsProps)
|
||||||
MeetingMetadataDAO.insert(meetingProps.meetingProp.intId, meetingProps.metadataProp)
|
MeetingMetadataDAO.insert(meetingProps.meetingProp.intId, meetingProps.metadataProp)
|
||||||
MeetingRecordingPoliciesDAO.insert(meetingProps.meetingProp.intId, meetingProps.recordProp)
|
MeetingRecordingPoliciesDAO.insert(meetingProps.meetingProp.intId, meetingProps.recordProp)
|
||||||
MeetingVoiceDAO.insert(meetingProps.meetingProp.intId, meetingProps.voiceProp)
|
MeetingVoiceDAO.insert(meetingProps.meetingProp.intId, meetingProps.voiceProp)
|
||||||
ChatDAO.insert(meetingProps.meetingProp.intId, GroupChatApp.createDefaultPublicGroupChat())
|
|
||||||
MeetingWelcomeDAO.insert(meetingProps.meetingProp.intId, meetingProps.welcomeProp)
|
MeetingWelcomeDAO.insert(meetingProps.meetingProp.intId, meetingProps.welcomeProp)
|
||||||
MeetingGroupDAO.insert(meetingProps.meetingProp.intId, meetingProps.groups)
|
MeetingGroupDAO.insert(meetingProps.meetingProp.intId, meetingProps.groups)
|
||||||
MeetingBreakoutDAO.insert(meetingProps.meetingProp.intId, meetingProps.breakoutProps)
|
MeetingBreakoutDAO.insert(meetingProps.meetingProp.intId, meetingProps.breakoutProps)
|
||||||
TimerDAO.insert(meetingProps.meetingProp.intId)
|
TimerDAO.insert(meetingProps.meetingProp.intId)
|
||||||
LayoutDAO.insert(meetingProps.meetingProp.intId, meetingProps.usersProp.meetingLayout)
|
LayoutDAO.insert(meetingProps.meetingProp.intId, meetingProps.usersProp.meetingLayout)
|
||||||
|
MeetingClientSettingsDAO.insert(meetingProps.meetingProp.intId, JsonUtils.mapToJson(clientSettings))
|
||||||
}
|
}
|
||||||
case Failure(e) => DatabaseConnection.logger.error(s"Error inserting Meeting: $e")
|
case Failure(e) => DatabaseConnection.logger.error(s"Error inserting Meeting: $e")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def updateMeetingDurationByParentMeeting(parentMeetingId: String, newDurationInSeconds: Int) = {
|
||||||
|
val subqueryBreakoutRooms = TableQuery[BreakoutRoomDbTableDef]
|
||||||
|
.filter(_.parentMeetingId === parentMeetingId)
|
||||||
|
.filter(_.endedAt.isEmpty)
|
||||||
|
.map(_.externalId)
|
||||||
|
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[MeetingDbTableDef]
|
||||||
|
.filter(_.extId in subqueryBreakoutRooms)
|
||||||
|
.map(u => u.durationInSeconds)
|
||||||
|
.update(newDurationInSeconds)
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated durationInSeconds on Meeting table")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating durationInSeconds on Meeting: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def delete(meetingId: String) = {
|
def delete(meetingId: String) = {
|
||||||
DatabaseConnection.db.run(
|
DatabaseConnection.db.run(
|
||||||
TableQuery[MeetingDbTableDef]
|
TableQuery[MeetingDbTableDef]
|
||||||
|
@ -18,7 +18,8 @@ case class MeetingLockSettingsDbModel(
|
|||||||
hideUserList: Boolean,
|
hideUserList: Boolean,
|
||||||
lockOnJoin: Boolean,
|
lockOnJoin: Boolean,
|
||||||
lockOnJoinConfigurable: Boolean,
|
lockOnJoinConfigurable: Boolean,
|
||||||
hideViewersCursor: Boolean
|
hideViewersCursor: Boolean,
|
||||||
|
hideViewersAnnotation: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
class MeetingLockSettingsDbTableDef(tag: Tag) extends Table[MeetingLockSettingsDbModel](tag, "meeting_lockSettings") {
|
class MeetingLockSettingsDbTableDef(tag: Tag) extends Table[MeetingLockSettingsDbModel](tag, "meeting_lockSettings") {
|
||||||
@ -32,10 +33,11 @@ class MeetingLockSettingsDbTableDef(tag: Tag) extends Table[MeetingLockSettingsD
|
|||||||
val lockOnJoin = column[Boolean]("lockOnJoin")
|
val lockOnJoin = column[Boolean]("lockOnJoin")
|
||||||
val lockOnJoinConfigurable = column[Boolean]("lockOnJoinConfigurable")
|
val lockOnJoinConfigurable = column[Boolean]("lockOnJoinConfigurable")
|
||||||
val hideViewersCursor = column[Boolean]("hideViewersCursor")
|
val hideViewersCursor = column[Boolean]("hideViewersCursor")
|
||||||
|
val hideViewersAnnotation = column[Boolean]("hideViewersAnnotation")
|
||||||
|
|
||||||
// def fk_meetingId: ForeignKeyQuery[MeetingDbTableDef, MeetingDbModel] = foreignKey("fk_meetingId", meetingId, TableQuery[MeetingDbTableDef])(_.meetingId)
|
// def fk_meetingId: ForeignKeyQuery[MeetingDbTableDef, MeetingDbModel] = foreignKey("fk_meetingId", meetingId, TableQuery[MeetingDbTableDef])(_.meetingId)
|
||||||
|
|
||||||
override def * : ProvenShape[MeetingLockSettingsDbModel] = (meetingId, disableCam, disableMic, disablePrivateChat, disablePublicChat, disableNotes, hideUserList, lockOnJoin, lockOnJoinConfigurable, hideViewersCursor) <> (MeetingLockSettingsDbModel.tupled, MeetingLockSettingsDbModel.unapply)
|
override def * : ProvenShape[MeetingLockSettingsDbModel] = (meetingId, disableCam, disableMic, disablePrivateChat, disablePublicChat, disableNotes, hideUserList, lockOnJoin, lockOnJoinConfigurable, hideViewersCursor, hideViewersAnnotation) <> (MeetingLockSettingsDbModel.tupled, MeetingLockSettingsDbModel.unapply)
|
||||||
}
|
}
|
||||||
|
|
||||||
object MeetingLockSettingsDAO {
|
object MeetingLockSettingsDAO {
|
||||||
@ -52,7 +54,8 @@ object MeetingLockSettingsDAO {
|
|||||||
hideUserList = lockSettingsProps.hideUserList,
|
hideUserList = lockSettingsProps.hideUserList,
|
||||||
lockOnJoin = lockSettingsProps.lockOnJoin,
|
lockOnJoin = lockSettingsProps.lockOnJoin,
|
||||||
lockOnJoinConfigurable = lockSettingsProps.lockOnJoinConfigurable,
|
lockOnJoinConfigurable = lockSettingsProps.lockOnJoinConfigurable,
|
||||||
hideViewersCursor = lockSettingsProps.hideViewersCursor
|
hideViewersCursor = lockSettingsProps.hideViewersCursor,
|
||||||
|
hideViewersAnnotation = lockSettingsProps.hideViewersAnnotation,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).onComplete {
|
).onComplete {
|
||||||
@ -76,7 +79,8 @@ object MeetingLockSettingsDAO {
|
|||||||
hideUserList = permissions.hideUserList,
|
hideUserList = permissions.hideUserList,
|
||||||
lockOnJoin = permissions.lockOnJoin,
|
lockOnJoin = permissions.lockOnJoin,
|
||||||
lockOnJoinConfigurable = permissions.lockOnJoinConfigurable,
|
lockOnJoinConfigurable = permissions.lockOnJoinConfigurable,
|
||||||
hideViewersCursor = permissions.hideViewersCursor
|
hideViewersCursor = permissions.hideViewersCursor,
|
||||||
|
hideViewersAnnotation = permissions.hideViewersAnnotation,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
).onComplete {
|
).onComplete {
|
||||||
|
@ -73,8 +73,8 @@ object MeetingUsersPoliciesDAO {
|
|||||||
.map(u => u.guestPolicy)
|
.map(u => u.guestPolicy)
|
||||||
.update(policy.policy)
|
.update(policy.policy)
|
||||||
).onComplete {
|
).onComplete {
|
||||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on meeting_usersPolicies table!")
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated guestPolicy on meeting_usersPolicies table!")
|
||||||
case Failure(e) => DatabaseConnection.logger.error(s"Error updating meeting_usersPolicies: $e")
|
case Failure(e) => DatabaseConnection.logger.error(s"Error updating guestPolicy on meeting_usersPolicies: $e")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,8 +90,20 @@ object MeetingUsersPoliciesDAO {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
).onComplete {
|
).onComplete {
|
||||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on meeting_usersPolicies table!")
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated guestLobbyMessage on meeting_usersPolicies table!")
|
||||||
case Failure(e) => DatabaseConnection.logger.error(s"Error updating meeting_usersPolicies: $e")
|
case Failure(e) => DatabaseConnection.logger.error(s"Error updating guestLobbyMessage on meeting_usersPolicies: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateWebcamsOnlyForModerator(meetingId: String, webcamsOnlyForModerator: Boolean) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[MeetingUsersPoliciesDbTableDef]
|
||||||
|
.filter(_.meetingId === meetingId)
|
||||||
|
.map(u => u.webcamsOnlyForModerator)
|
||||||
|
.update(webcamsOnlyForModerator)
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated webcamsOnlyForModerator on meeting_usersPolicies table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.error(s"Error updating webcamsOnlyForModerator on meeting_usersPolicies: $e")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
package org.bigbluebutton.core.db
|
||||||
|
|
||||||
|
import PostgresProfile.api._
|
||||||
|
import spray.json.JsValue
|
||||||
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
|
object Permission {
|
||||||
|
val allowedRoles = List("MODERATOR","VIEWER","PRESENTER")
|
||||||
|
}
|
||||||
|
|
||||||
|
case class PluginDataChannelMessageDbModel(
|
||||||
|
meetingId: String,
|
||||||
|
pluginName: String,
|
||||||
|
dataChannel: String,
|
||||||
|
// messageId: Option[String] = None,
|
||||||
|
payloadJson: JsValue,
|
||||||
|
fromUserId: String,
|
||||||
|
toRoles: Option[List[String]],
|
||||||
|
toUserIds: Option[List[String]],
|
||||||
|
createdAt: java.sql.Timestamp,
|
||||||
|
)
|
||||||
|
|
||||||
|
class PluginDataChannelMessageDbTableDef(tag: Tag) extends Table[PluginDataChannelMessageDbModel](tag, None, "pluginDataChannelMessage") {
|
||||||
|
val meetingId = column[String]("meetingId", O.PrimaryKey)
|
||||||
|
val pluginName = column[String]("pluginName", O.PrimaryKey)
|
||||||
|
val dataChannel = column[String]("dataChannel", O.PrimaryKey)
|
||||||
|
// val messageId = column[Option[String]]("messageId", O.PrimaryKey) //// The messageId is generated by the database
|
||||||
|
val payloadJson = column[JsValue]("payloadJson")
|
||||||
|
val fromUserId = column[String]("fromUserId")
|
||||||
|
val toRoles = column[Option[List[String]]]("toRoles")
|
||||||
|
val toUserIds = column[Option[List[String]]]("toUserIds")
|
||||||
|
val createdAt = column[java.sql.Timestamp]("createdAt")
|
||||||
|
override def * = (meetingId, pluginName, dataChannel, payloadJson, fromUserId, toRoles, toUserIds, createdAt) <> (PluginDataChannelMessageDbModel.tupled, PluginDataChannelMessageDbModel.unapply)
|
||||||
|
}
|
||||||
|
|
||||||
|
object PluginDataChannelMessageDAO {
|
||||||
|
def insert(meetingId: String, pluginName: String, dataChannel: String, senderUserId: String, payloadJson: String, toRoles: List[String], toUserIds: List[String]) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[PluginDataChannelMessageDbTableDef].forceInsert(
|
||||||
|
PluginDataChannelMessageDbModel(
|
||||||
|
meetingId = meetingId,
|
||||||
|
pluginName = pluginName,
|
||||||
|
dataChannel = dataChannel,
|
||||||
|
payloadJson = JsonUtils.stringToJson(payloadJson),
|
||||||
|
fromUserId = senderUserId,
|
||||||
|
toRoles = toRoles.map(_.toUpperCase).filter(Permission.allowedRoles.contains) match {
|
||||||
|
case Nil => None
|
||||||
|
case filtered => Some(filtered)
|
||||||
|
},
|
||||||
|
toUserIds = if(toUserIds.isEmpty) None else Some(toUserIds),
|
||||||
|
createdAt = new java.sql.Timestamp(System.currentTimeMillis())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on PluginDataChannelMessage table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting PluginDataChannelMessage: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,16 @@
|
|||||||
package org.bigbluebutton.core.db
|
package org.bigbluebutton.core.db
|
||||||
|
|
||||||
import com.github.tminglei.slickpg._
|
import com.github.tminglei.slickpg._
|
||||||
import spray.json.{ JsArray, JsBoolean, JsNumber, JsObject, JsString, JsValue, JsonWriter }
|
import org.apache.pekko.http.scaladsl.model.ParsingException
|
||||||
import spray.json.{ _ }
|
import org.bigbluebutton.common2.domain.SimpleVoteOutVO
|
||||||
|
import spray.json.{ JsArray, JsBoolean, JsNumber, JsObject, JsString, JsValue, JsonWriter, _ }
|
||||||
|
|
||||||
|
import scala.util.{ Failure, Success, Try }
|
||||||
|
|
||||||
trait PostgresProfile extends ExPostgresProfile
|
trait PostgresProfile extends ExPostgresProfile
|
||||||
with PgArraySupport {
|
with PgArraySupport
|
||||||
// def pgjson = "jsonb" // jsonb support is in postgres 9.4.0 onward; for 9.3.x use "json"
|
with PgSprayJsonSupport {
|
||||||
|
def pgjson = "jsonb" // jsonb support is in postgres 9.4.0 onward; for 9.3.x use "json"
|
||||||
|
|
||||||
// Add back `capabilities.insertOrUpdate` to enable native `upsert` support; for postgres 9.5+
|
// Add back `capabilities.insertOrUpdate` to enable native `upsert` support; for postgres 9.5+
|
||||||
override protected def computeCapabilities: Set[slick.basic.Capability] =
|
override protected def computeCapabilities: Set[slick.basic.Capability] =
|
||||||
@ -14,7 +18,7 @@ trait PostgresProfile extends ExPostgresProfile
|
|||||||
|
|
||||||
override val api = PgAPI
|
override val api = PgAPI
|
||||||
|
|
||||||
object PgAPI extends API with ArrayImplicits // with DateTimeImplicits
|
object PgAPI extends API with ArrayImplicits with SprayJsonImplicits // with DateTimeImplicits
|
||||||
{
|
{
|
||||||
implicit val strListTypeMapper = new SimpleArrayJdbcType[String]("text").to(_.toList)
|
implicit val strListTypeMapper = new SimpleArrayJdbcType[String]("text").to(_.toList)
|
||||||
}
|
}
|
||||||
@ -34,6 +38,8 @@ object JsonUtils {
|
|||||||
case m: Map[_, _] => JsObject(m.asInstanceOf[Map[String, Any]].map { case (k, v) => k -> write(v) })
|
case m: Map[_, _] => JsObject(m.asInstanceOf[Map[String, Any]].map { case (k, v) => k -> write(v) })
|
||||||
case l: List[_] => JsArray(l.map(write).toVector)
|
case l: List[_] => JsArray(l.map(write).toVector)
|
||||||
case a: Array[_] => JsArray(a.map(write).toVector)
|
case a: Array[_] => JsArray(a.map(write).toVector)
|
||||||
|
case v: SimpleVoteOutVO => JsObject("id" -> JsNumber(v.id), "key" -> JsString(v.key), "numVotes" -> JsNumber(v.numVotes))
|
||||||
|
case null => JsNull
|
||||||
case _ => throw new IllegalArgumentException(s"Unsupported type: ${x.getClass.getName}")
|
case _ => throw new IllegalArgumentException(s"Unsupported type: ${x.getClass.getName}")
|
||||||
// case _ => JsNull
|
// case _ => JsNull
|
||||||
}
|
}
|
||||||
@ -46,7 +52,16 @@ object JsonUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def mapToJson(genericMap: Map[String, Any]) = {
|
def mapToJson(genericMap: Map[String, Any]) = {
|
||||||
genericMap.toJson.compactPrint
|
genericMap.toJson
|
||||||
|
}
|
||||||
|
|
||||||
|
def stringToJson(jsonString: String): JsValue = {
|
||||||
|
Try(jsonString.parseJson) match {
|
||||||
|
case Success(jsValue) => jsValue
|
||||||
|
case Failure(exception) =>
|
||||||
|
println(s"Failed to parse JSON string: $exception")
|
||||||
|
"{}".parseJson
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -36,7 +36,7 @@ object PresAnnotationDAO {
|
|||||||
annotationId = annotation.id,
|
annotationId = annotation.id,
|
||||||
pageId = annotation.wbId,
|
pageId = annotation.wbId,
|
||||||
userId = annotation.userId,
|
userId = annotation.userId,
|
||||||
annotationInfo = JsonUtils.mapToJson(annotation.annotationInfo),
|
annotationInfo = JsonUtils.mapToJson(annotation.annotationInfo).compactPrint,
|
||||||
lastHistorySequence = sequence.getOrElse(0),
|
lastHistorySequence = sequence.getOrElse(0),
|
||||||
lastUpdatedAt = new java.sql.Timestamp(System.currentTimeMillis())
|
lastUpdatedAt = new java.sql.Timestamp(System.currentTimeMillis())
|
||||||
)
|
)
|
||||||
|
@ -34,7 +34,7 @@ object PresAnnotationHistoryDAO {
|
|||||||
annotationId = annotationDiff.id,
|
annotationId = annotationDiff.id,
|
||||||
pageId = annotationDiff.wbId,
|
pageId = annotationDiff.wbId,
|
||||||
userId = annotationDiff.userId,
|
userId = annotationDiff.userId,
|
||||||
annotationInfo = JsonUtils.mapToJson(annotationDiff.annotationInfo)
|
annotationInfo = JsonUtils.mapToJson(annotationDiff.annotationInfo).compactPrint
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package org.bigbluebutton.core.db
|
package org.bigbluebutton.core.db
|
||||||
|
|
||||||
import org.bigbluebutton.core.models.{ PresentationInPod, PresentationPage }
|
import org.bigbluebutton.core.models.{ PresentationInPod, PresentationPage }
|
||||||
|
import PostgresProfile.api._
|
||||||
import org.bigbluebutton.core.models.PresentationInPod
|
import spray.json.JsValue
|
||||||
import slick.jdbc.PostgresProfile.api._
|
import spray.json._
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.util.{ Failure, Success }
|
import scala.util.{ Failure, Success }
|
||||||
@ -12,7 +12,8 @@ case class PresPageDbModel(
|
|||||||
pageId: String,
|
pageId: String,
|
||||||
presentationId: String,
|
presentationId: String,
|
||||||
num: Int,
|
num: Int,
|
||||||
urls: String,
|
urlsJson: JsValue,
|
||||||
|
content: String,
|
||||||
slideRevealed: Boolean,
|
slideRevealed: Boolean,
|
||||||
current: Boolean,
|
current: Boolean,
|
||||||
xOffset: Double,
|
xOffset: Double,
|
||||||
@ -25,14 +26,15 @@ case class PresPageDbModel(
|
|||||||
viewBoxHeight: Double,
|
viewBoxHeight: Double,
|
||||||
maxImageWidth: Int,
|
maxImageWidth: Int,
|
||||||
maxImageHeight: Int,
|
maxImageHeight: Int,
|
||||||
converted: Boolean
|
uploadCompleted: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
class PresPageDbTableDef(tag: Tag) extends Table[PresPageDbModel](tag, None, "pres_page") {
|
class PresPageDbTableDef(tag: Tag) extends Table[PresPageDbModel](tag, None, "pres_page") {
|
||||||
val pageId = column[String]("pageId", O.PrimaryKey)
|
val pageId = column[String]("pageId", O.PrimaryKey)
|
||||||
val presentationId = column[String]("presentationId")
|
val presentationId = column[String]("presentationId")
|
||||||
val num = column[Int]("num")
|
val num = column[Int]("num")
|
||||||
val urls = column[String]("urls")
|
val urlsJson = column[JsValue]("urlsJson")
|
||||||
|
val content = column[String]("content")
|
||||||
val slideRevealed = column[Boolean]("slideRevealed")
|
val slideRevealed = column[Boolean]("slideRevealed")
|
||||||
val current = column[Boolean]("current")
|
val current = column[Boolean]("current")
|
||||||
val xOffset = column[Double]("xOffset")
|
val xOffset = column[Double]("xOffset")
|
||||||
@ -45,12 +47,46 @@ class PresPageDbTableDef(tag: Tag) extends Table[PresPageDbModel](tag, None, "pr
|
|||||||
val viewBoxHeight = column[Double]("viewBoxHeight")
|
val viewBoxHeight = column[Double]("viewBoxHeight")
|
||||||
val maxImageWidth = column[Int]("maxImageWidth")
|
val maxImageWidth = column[Int]("maxImageWidth")
|
||||||
val maxImageHeight = column[Int]("maxImageHeight")
|
val maxImageHeight = column[Int]("maxImageHeight")
|
||||||
val converted = column[Boolean]("converted")
|
val uploadCompleted = column[Boolean]("uploadCompleted")
|
||||||
// val presentation = foreignKey("presentation_fk", presentationId, Presentations)(_.presentationId, onDelete = ForeignKeyAction.Cascade)
|
// val presentation = foreignKey("presentation_fk", presentationId, Presentations)(_.presentationId, onDelete = ForeignKeyAction.Cascade)
|
||||||
def * = (pageId, presentationId, num, urls, slideRevealed, current, xOffset, yOffset, widthRatio, heightRatio, width, height, viewBoxWidth, viewBoxHeight, maxImageWidth, maxImageHeight, converted) <> (PresPageDbModel.tupled, PresPageDbModel.unapply)
|
def * = (pageId, presentationId, num, urlsJson, content, slideRevealed, current, xOffset, yOffset, widthRatio, heightRatio, width, height, viewBoxWidth, viewBoxHeight, maxImageWidth, maxImageHeight, uploadCompleted) <> (PresPageDbModel.tupled, PresPageDbModel.unapply)
|
||||||
}
|
}
|
||||||
|
|
||||||
object PresPageDAO {
|
object PresPageDAO {
|
||||||
|
implicit val mapFormat: JsonWriter[Map[String, String]] = new JsonWriter[Map[String, String]] {
|
||||||
|
def write(m: Map[String, String]): JsValue = {
|
||||||
|
JsObject(m.map { case (k, v) => k -> JsString(v) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def insert(presentationId: String, page: PresentationPage) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[PresPageDbTableDef].insertOrUpdate(
|
||||||
|
PresPageDbModel(
|
||||||
|
pageId = page.id,
|
||||||
|
presentationId = presentationId,
|
||||||
|
num = page.num,
|
||||||
|
urlsJson = page.urls.toJson,
|
||||||
|
content = page.content,
|
||||||
|
slideRevealed = page.current,
|
||||||
|
current = page.current,
|
||||||
|
xOffset = page.xOffset,
|
||||||
|
yOffset = page.yOffset,
|
||||||
|
widthRatio = page.widthRatio,
|
||||||
|
heightRatio = page.heightRatio,
|
||||||
|
width = page.width,
|
||||||
|
height = page.height,
|
||||||
|
viewBoxWidth = 1,
|
||||||
|
viewBoxHeight = 1,
|
||||||
|
maxImageWidth = 1440,
|
||||||
|
maxImageHeight = 1080,
|
||||||
|
uploadCompleted = page.converted
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on PresentationPage table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting PresentationPage: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def setCurrentPage(presentation: PresentationInPod, pageId: String) = {
|
def setCurrentPage(presentation: PresentationInPod, pageId: String) = {
|
||||||
DatabaseConnection.db.run(
|
DatabaseConnection.db.run(
|
||||||
|
@ -1,27 +1,70 @@
|
|||||||
package org.bigbluebutton.core.db
|
package org.bigbluebutton.core.db
|
||||||
|
|
||||||
import slick.jdbc.PostgresProfile.api._
|
import PostgresProfile.api._
|
||||||
import org.bigbluebutton.core.models.{ PresentationInPod }
|
import org.bigbluebutton.core.models.PresentationInPod
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
import spray.json._
|
import spray.json._
|
||||||
|
|
||||||
case class PresPresentationDbModel(presentationId: String, meetingId: String, current: Boolean, downloadable: Boolean, removable: Boolean, uploadCompleted: Boolean, numPages: Int, errorMsgKey: String, errorDetails: String)
|
import scala.concurrent.Future
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
case class PresPresentationDbModel(
|
||||||
|
presentationId: String,
|
||||||
|
meetingId: String,
|
||||||
|
uploadUserId: Option[String],
|
||||||
|
uploadTemporaryId: Option[String],
|
||||||
|
uploadToken: Option[String],
|
||||||
|
name: String,
|
||||||
|
filenameConverted: String,
|
||||||
|
isDefault: Boolean,
|
||||||
|
current: Boolean,
|
||||||
|
downloadable: Boolean,
|
||||||
|
downloadFileExtension: Option[String],
|
||||||
|
downloadFileUri: Option[String],
|
||||||
|
removable: Boolean,
|
||||||
|
uploadInProgress: Boolean,
|
||||||
|
uploadCompleted: Boolean,
|
||||||
|
uploadErrorMsgKey: Option[String],
|
||||||
|
uploadErrorDetailsJson: Option[JsValue],
|
||||||
|
totalPages: Int,
|
||||||
|
exportToChatStatus: Option[String],
|
||||||
|
exportToChatCurrentPage: Option[Int],
|
||||||
|
exportToChatHasError: Option[Boolean]
|
||||||
|
)
|
||||||
|
|
||||||
class PresPresentationDbTableDef(tag: Tag) extends Table[PresPresentationDbModel](tag, None, "pres_presentation") {
|
class PresPresentationDbTableDef(tag: Tag) extends Table[PresPresentationDbModel](tag, None, "pres_presentation") {
|
||||||
val presentationId = column[String]("presentationId", O.PrimaryKey)
|
val presentationId = column[String]("presentationId", O.PrimaryKey)
|
||||||
val meetingId = column[String]("meetingId")
|
val meetingId = column[String]("meetingId")
|
||||||
|
val uploadUserId = column[Option[String]]("uploadUserId")
|
||||||
|
val uploadTemporaryId = column[Option[String]]("uploadTemporaryId")
|
||||||
|
val uploadToken = column[Option[String]]("uploadToken")
|
||||||
|
val name = column[String]("name")
|
||||||
|
val filenameConverted = column[String]("filenameConverted")
|
||||||
|
val isDefault = column[Boolean]("isDefault")
|
||||||
val current = column[Boolean]("current")
|
val current = column[Boolean]("current")
|
||||||
val downloadable = column[Boolean]("downloadable")
|
val downloadable = column[Boolean]("downloadable")
|
||||||
|
val downloadFileExtension = column[Option[String]]("downloadFileExtension")
|
||||||
|
val downloadFileUri = column[Option[String]]("downloadFileUri")
|
||||||
val removable = column[Boolean]("removable")
|
val removable = column[Boolean]("removable")
|
||||||
|
val uploadInProgress = column[Boolean]("uploadInProgress")
|
||||||
val uploadCompleted = column[Boolean]("uploadCompleted")
|
val uploadCompleted = column[Boolean]("uploadCompleted")
|
||||||
val numPages = column[Int]("numPages")
|
val uploadErrorMsgKey = column[Option[String]]("uploadErrorMsgKey")
|
||||||
val errorMsgKey = column[String]("errorMsgKey")
|
val uploadErrorDetailsJson = column[Option[JsValue]]("uploadErrorDetailsJson")
|
||||||
val errorDetails = column[String]("errorDetails")
|
val totalPages = column[Int]("totalPages")
|
||||||
|
val exportToChatStatus = column[Option[String]]("exportToChatStatus")
|
||||||
|
val exportToChatCurrentPage = column[Option[Int]]("exportToChatCurrentPage")
|
||||||
|
val exportToChatHasError = column[Option[Boolean]]("exportToChatHasError")
|
||||||
|
|
||||||
// val meeting = foreignKey("meeting_fk", meetingId, Meetings)(_.meetingId, onDelete = ForeignKeyAction.Cascade)
|
// val meeting = foreignKey("meeting_fk", meetingId, Meetings)(_.meetingId, onDelete = ForeignKeyAction.Cascade)
|
||||||
|
|
||||||
def * = (presentationId, meetingId, current, downloadable, removable, uploadCompleted, numPages, errorMsgKey, errorDetails) <> (PresPresentationDbModel.tupled, PresPresentationDbModel.unapply)
|
def * = (
|
||||||
|
presentationId, meetingId, uploadUserId, uploadTemporaryId, uploadToken, name, filenameConverted, isDefault, current, downloadable, downloadFileExtension, downloadFileUri, removable,
|
||||||
|
uploadInProgress, uploadCompleted, uploadErrorMsgKey, uploadErrorDetailsJson, totalPages,
|
||||||
|
exportToChatStatus, exportToChatCurrentPage, exportToChatHasError
|
||||||
|
) <> (PresPresentationDbModel.tupled, PresPresentationDbModel.unapply)
|
||||||
}
|
}
|
||||||
|
|
||||||
object PresPresentationDAO {
|
object PresPresentationDAO {
|
||||||
@ -31,24 +74,107 @@ object PresPresentationDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def insertOrUpdate(meetingId: String, presentation: PresentationInPod) = {
|
def insertToken(meetingId: String, userId: String, temporaryId: String, presentationId: String, uploadToken: String, filename: String) = {
|
||||||
DatabaseConnection.db.run(
|
val dbRun = DatabaseConnection.db.run(
|
||||||
TableQuery[PresPresentationDbTableDef].insertOrUpdate(
|
TableQuery[PresPresentationDbTableDef].forceInsert(
|
||||||
PresPresentationDbModel(
|
PresPresentationDbModel(
|
||||||
presentationId = presentation.id,
|
presentationId = presentationId,
|
||||||
meetingId = meetingId,
|
meetingId = meetingId,
|
||||||
|
uploadUserId = Some(userId),
|
||||||
|
uploadTemporaryId = Some(temporaryId),
|
||||||
|
uploadToken = Some(uploadToken),
|
||||||
|
name = filename,
|
||||||
|
filenameConverted = "",
|
||||||
|
isDefault = false,
|
||||||
current = false, //Set after pages were inserted
|
current = false, //Set after pages were inserted
|
||||||
downloadable = presentation.downloadable,
|
downloadable = false,
|
||||||
removable = presentation.removable,
|
downloadFileExtension = None,
|
||||||
uploadCompleted = presentation.uploadCompleted,
|
downloadFileUri = None,
|
||||||
numPages = presentation.numPages,
|
removable = false,
|
||||||
errorMsgKey = presentation.errorMsgKey,
|
uploadInProgress = false,
|
||||||
errorDetails = presentation.errorDetails.toJson.asJsObject.compactPrint
|
uploadCompleted = false,
|
||||||
|
totalPages = 0,
|
||||||
|
uploadErrorMsgKey = None,
|
||||||
|
uploadErrorDetailsJson = None,
|
||||||
|
exportToChatStatus = None,
|
||||||
|
exportToChatCurrentPage = None,
|
||||||
|
exportToChatHasError = None,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
dbRun.onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on Presentation table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.error(s"Error inserting Presentation: $e")
|
||||||
|
}
|
||||||
|
|
||||||
|
dbRun
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateConversionStarted(meetingId: String, presentation: PresentationInPod) = {
|
||||||
|
val checkAndInsert = DatabaseConnection.db.run(
|
||||||
|
TableQuery[PresPresentationDbTableDef]
|
||||||
|
.filter(_.presentationId === presentation.id).exists.result).flatMap { exists =>
|
||||||
|
if (!exists) {
|
||||||
|
insertToken(meetingId, "", "", presentation.id, "", presentation.name)
|
||||||
|
} else {
|
||||||
|
Future.successful(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAndInsert.flatMap { _ =>
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[PresPresentationDbTableDef]
|
||||||
|
.filter(_.presentationId === presentation.id)
|
||||||
|
.map(p => (
|
||||||
|
p.name,
|
||||||
|
p.filenameConverted,
|
||||||
|
p.isDefault,
|
||||||
|
p.downloadable,
|
||||||
|
p.downloadFileExtension,
|
||||||
|
p.removable,
|
||||||
|
p.uploadInProgress,
|
||||||
|
p.uploadCompleted,
|
||||||
|
p.totalPages))
|
||||||
|
.update(
|
||||||
|
(presentation.name,
|
||||||
|
presentation.filenameConverted,
|
||||||
|
presentation.default,
|
||||||
|
presentation.downloadable,
|
||||||
|
presentation.downloadFileExtension match {
|
||||||
|
case "" => None
|
||||||
|
case downloadFileExtension => Some(downloadFileExtension)
|
||||||
|
},
|
||||||
|
presentation.removable,
|
||||||
|
!presentation.uploadCompleted,
|
||||||
|
presentation.uploadCompleted,
|
||||||
|
presentation.numPages
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}.onComplete {
|
||||||
|
case Success(rowAffected) => DatabaseConnection.logger.debug(s"$rowAffected row(s) updated basicData on PresPresentation table")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.error(s"Error updating basicData on PresPresentation: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def updatePages(presentation: PresentationInPod) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[PresPresentationDbTableDef]
|
||||||
|
.filter(_.presentationId === presentation.id)
|
||||||
|
.map(p => (p.downloadFileExtension, p.uploadInProgress, p.uploadCompleted, p.totalPages))
|
||||||
|
.update((
|
||||||
|
presentation.downloadFileExtension match {
|
||||||
|
case "" => None
|
||||||
|
case downloadFileExtension => Some(downloadFileExtension)
|
||||||
|
},
|
||||||
|
!presentation.uploadCompleted,
|
||||||
|
presentation.uploadCompleted,
|
||||||
|
presentation.numPages,
|
||||||
|
))
|
||||||
).onComplete {
|
).onComplete {
|
||||||
case Success(rowsAffected) => {
|
case Success(rowsAffected) => {
|
||||||
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on Presentation table!")
|
DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on PresPresentation table!")
|
||||||
|
|
||||||
DatabaseConnection.db.run(DBIO.sequence(
|
DatabaseConnection.db.run(DBIO.sequence(
|
||||||
for {
|
for {
|
||||||
@ -59,7 +185,8 @@ object PresPresentationDAO {
|
|||||||
pageId = page._2.id,
|
pageId = page._2.id,
|
||||||
presentationId = presentation.id,
|
presentationId = presentation.id,
|
||||||
num = page._2.num,
|
num = page._2.num,
|
||||||
urls = page._2.urls.toJson.asJsObject.compactPrint,
|
urlsJson = page._2.urls.toJson,
|
||||||
|
content = page._2.content,
|
||||||
slideRevealed = page._2.current,
|
slideRevealed = page._2.current,
|
||||||
current = page._2.current,
|
current = page._2.current,
|
||||||
xOffset = page._2.xOffset,
|
xOffset = page._2.xOffset,
|
||||||
@ -72,22 +199,23 @@ object PresPresentationDAO {
|
|||||||
viewBoxHeight = 1,
|
viewBoxHeight = 1,
|
||||||
maxImageWidth = 1440,
|
maxImageWidth = 1440,
|
||||||
maxImageHeight = 1080,
|
maxImageHeight = 1080,
|
||||||
converted = page._2.converted
|
uploadCompleted = page._2.converted
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
).transactionally)
|
).transactionally)
|
||||||
.onComplete {
|
.onComplete {
|
||||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on PresentationPage table!")
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on PresentationPage table!")
|
||||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting PresentationPage: $e")
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating PresentationPage: $e")
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set current
|
//Set current
|
||||||
if (presentation.current) {
|
if (presentation.current) {
|
||||||
setCurrentPres(presentation.id)
|
setCurrentPres(presentation.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
case Failure(e) => DatabaseConnection.logger.error(s"Error inserting Presentation: $e")
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating user: $e")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,23 +223,83 @@ object PresPresentationDAO {
|
|||||||
DatabaseConnection.db.run(
|
DatabaseConnection.db.run(
|
||||||
sqlu"""UPDATE pres_presentation SET
|
sqlu"""UPDATE pres_presentation SET
|
||||||
"current" = (case when "presentationId" = ${presentationId} then true else false end)
|
"current" = (case when "presentationId" = ${presentationId} then true else false end)
|
||||||
WHERE "meetingId" = (select "meetingId" from pres_presentation where "presentationId" = ${presentationId})"""
|
WHERE "meetingId" = (select "meetingId" from pres_presentation where "presentationId" = ${presentationId})
|
||||||
|
AND exists (select 1 from pres_page where "presentationId" = ${presentationId} AND "current" IS true)"""
|
||||||
).onComplete {
|
).onComplete {
|
||||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated current on PresPresentation table")
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated current on PresPresentation table")
|
||||||
case Failure(e) => DatabaseConnection.logger.error(s"Error updating current on PresPresentation: $e")
|
case Failure(e) => DatabaseConnection.logger.error(s"Error updating current on PresPresentation: $e")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def updateDownloadable(presentationId: String, downloadable : Boolean, downloadableExtension: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[PresPresentationDbTableDef]
|
||||||
|
.filter(_.presentationId === presentationId)
|
||||||
|
.map(p => (p.downloadable, p.downloadFileExtension))
|
||||||
|
.update((downloadable, Some(downloadableExtension)))
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowAffected) => DatabaseConnection.logger.debug(s"$rowAffected row(s) updated downloadable on PresPresentation table")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.error(s"Error updating downloadable on PresPresentation: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateDownloadUri(presentationId: String, downloadFileUri: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[PresPresentationDbTableDef]
|
||||||
|
.filter(_.presentationId === presentationId)
|
||||||
|
.map(p => p.downloadFileUri)
|
||||||
|
.update(Some(downloadFileUri))
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowAffected) => DatabaseConnection.logger.debug(s"$rowAffected row(s) updated originalFileURI on PresPresentation table")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.error(s"Error updating originalFileURI on PresPresentation: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def updateErrors(presentationId: String, errorMsgKey: String, errorDetails: scala.collection.immutable.Map[String, String]) = {
|
def updateErrors(presentationId: String, errorMsgKey: String, errorDetails: scala.collection.immutable.Map[String, String]) = {
|
||||||
DatabaseConnection.db.run(
|
DatabaseConnection.db.run(
|
||||||
TableQuery[PresPresentationDbTableDef]
|
TableQuery[PresPresentationDbTableDef]
|
||||||
.filter(_.presentationId === presentationId)
|
.filter(_.presentationId === presentationId)
|
||||||
.map(p => (p.errorMsgKey, p.errorDetails))
|
.map(p => (p.uploadErrorMsgKey, p.uploadErrorDetailsJson))
|
||||||
.update(errorMsgKey, errorDetails.toJson.asJsObject.compactPrint)
|
.update(Some(errorMsgKey), Some(errorDetails.toJson))
|
||||||
).onComplete {
|
).onComplete {
|
||||||
case Success(rowAffected) => DatabaseConnection.logger.debug(s"$rowAffected row(s) updated errorMsgKey on PresPresentation table")
|
case Success(rowAffected) => DatabaseConnection.logger.debug(s"$rowAffected row(s) updated errorMsgKey on PresPresentation table")
|
||||||
case Failure(e) => DatabaseConnection.logger.error(s"Error updating errorMsgKey on PresPresentation: $e")
|
case Failure(e) => DatabaseConnection.logger.error(s"Error updating errorMsgKey on PresPresentation: $e")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def updateExportToChat(presentationId: String, exportToChatStatus: String, exportToChatCurrentPage: Int, exportToChatHasError: Boolean) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[PresPresentationDbTableDef]
|
||||||
|
.filter(_.presentationId === presentationId)
|
||||||
|
.map(p => (p.exportToChatStatus, p.exportToChatCurrentPage, p.exportToChatHasError))
|
||||||
|
.update(Some(exportToChatStatus), Some(exportToChatCurrentPage), Some(exportToChatHasError))
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowAffected) => DatabaseConnection.logger.debug(s"$rowAffected row(s) updated exportToChat on PresPresentation table")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.error(s"Error updating exportToChat on PresPresentation: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateExportToChatStatus(presentationId: String, exportToChatStatus: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[PresPresentationDbTableDef]
|
||||||
|
.filter(_.presentationId === presentationId)
|
||||||
|
.map(p => p.exportToChatStatus)
|
||||||
|
.update(Some(exportToChatStatus))
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowAffected) => DatabaseConnection.logger.debug(s"$rowAffected row(s) updated exportToChatStatus on PresPresentation table")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.error(s"Error updating exportToChatStatus on PresPresentation: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def delete(presentationId: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[PresPresentationDbTableDef]
|
||||||
|
.filter(_.presentationId === presentationId)
|
||||||
|
.delete
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowAffected) => DatabaseConnection.logger.debug(s"$rowAffected row(s) deleted presentation on PresPresentation table")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.error(s"Error deleting presentation on PresPresentation: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package org.bigbluebutton.core.db
|
package org.bigbluebutton.core.db
|
||||||
|
|
||||||
import org.bigbluebutton.core.apps.TimerModel
|
import org.bigbluebutton.core.apps.TimerModel
|
||||||
import org.bigbluebutton.core.apps.TimerModel.{getAccumulated, getEndedAt, getIsACtive, getRunning, getStartedAt, getStopwatch, getTime, getTrack}
|
import org.bigbluebutton.core.apps.TimerModel.{getAccumulated, getEndedAt, getIsActive, getRunning, getStartedAt, getStopwatch, getTime, getTrack}
|
||||||
import slick.jdbc.PostgresProfile.api._
|
import slick.jdbc.PostgresProfile.api._
|
||||||
|
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
@ -59,7 +59,7 @@ object TimerDAO {
|
|||||||
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.startedAt, t.endedAt, 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 {
|
||||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on Timer table!")
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on Timer table!")
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package org.bigbluebutton.core.db
|
||||||
|
|
||||||
|
import PostgresProfile.api._
|
||||||
|
import slick.lifted.ProvenShape
|
||||||
|
import spray.json.JsValue
|
||||||
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
import scala.util.{ Failure, Success }
|
||||||
|
|
||||||
|
case class UserClientSettingsDbModel(
|
||||||
|
userId: String,
|
||||||
|
meetingId: String,
|
||||||
|
userClientSettingsJson: JsValue
|
||||||
|
)
|
||||||
|
|
||||||
|
class UserClientSettingsDbTableDef(tag: Tag) extends Table[UserClientSettingsDbModel](tag, "user_clientSettings") {
|
||||||
|
val userId = column[String]("userId", O.PrimaryKey)
|
||||||
|
val meetingId = column[String]("meetingId")
|
||||||
|
val userClientSettingsJson = column[JsValue]("userClientSettingsJson")
|
||||||
|
|
||||||
|
override def * : ProvenShape[UserClientSettingsDbModel] = (userId, meetingId, userClientSettingsJson) <> (UserClientSettingsDbModel.tupled, UserClientSettingsDbModel.unapply)
|
||||||
|
}
|
||||||
|
|
||||||
|
object UserClientSettingsDAO {
|
||||||
|
def insert(userId: String, meetingId: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[UserClientSettingsDbTableDef].insertOrUpdate(
|
||||||
|
UserClientSettingsDbModel(
|
||||||
|
userId = userId,
|
||||||
|
meetingId = meetingId,
|
||||||
|
userClientSettingsJson = JsonUtils.stringToJson("{}")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on UserClientSettings table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting UserClientSettings: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,7 @@ class UserConnectionStatusDbTableDef(tag: Tag) extends Table[UserConnectionStatu
|
|||||||
val connectionAliveAt = column[Option[java.sql.Timestamp]]("connectionAliveAt")
|
val connectionAliveAt = column[Option[java.sql.Timestamp]]("connectionAliveAt")
|
||||||
}
|
}
|
||||||
|
|
||||||
object UserConnectionStatusdDAO {
|
object UserConnectionStatusDAO {
|
||||||
|
|
||||||
def insert(meetingId: String, userId: String) = {
|
def insert(meetingId: String, userId: String) = {
|
||||||
DatabaseConnection.db.run(
|
DatabaseConnection.db.run(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
package org.bigbluebutton.core.db
|
package org.bigbluebutton.core.db
|
||||||
import org.bigbluebutton.core.models.{RegisteredUser}
|
import org.bigbluebutton.core.models.{RegisteredUser, VoiceUserState}
|
||||||
import slick.jdbc.PostgresProfile.api._
|
import slick.jdbc.PostgresProfile.api._
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
@ -13,21 +13,25 @@ case class UserDbModel(
|
|||||||
role: String,
|
role: String,
|
||||||
avatar: String = "",
|
avatar: String = "",
|
||||||
color: String = "",
|
color: String = "",
|
||||||
|
sessionToken: String = "",
|
||||||
authed: Boolean = false,
|
authed: Boolean = false,
|
||||||
joined: Boolean = false,
|
joined: Boolean = false,
|
||||||
|
joinErrorMessage: Option[String],
|
||||||
|
joinErrorCode: Option[String],
|
||||||
banned: Boolean = false,
|
banned: Boolean = false,
|
||||||
loggedOut: Boolean = false,
|
loggedOut: Boolean = false,
|
||||||
guest: Boolean,
|
guest: Boolean,
|
||||||
guestStatus: String,
|
guestStatus: String,
|
||||||
registeredOn: Long,
|
registeredOn: Long,
|
||||||
excludeFromDashboard: Boolean,
|
excludeFromDashboard: Boolean,
|
||||||
|
enforceLayout: Option[String],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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,authed,joined,banned,loggedOut,guest,guestStatus,registeredOn,excludeFromDashboard) <> (UserDbModel.tupled, UserDbModel.unapply)
|
userId,extId,meetingId,name,role,avatar,color, sessionToken, 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")
|
||||||
@ -35,14 +39,18 @@ class UserDbTableDef(tag: Tag) extends Table[UserDbModel](tag, None, "user") {
|
|||||||
val role = column[String]("role")
|
val role = column[String]("role")
|
||||||
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 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 joinErrorMessage = column[Option[String]]("joinErrorMessage")
|
||||||
val banned = column[Boolean]("banned")
|
val banned = column[Boolean]("banned")
|
||||||
val loggedOut = column[Boolean]("loggedOut")
|
val loggedOut = column[Boolean]("loggedOut")
|
||||||
val guest = column[Boolean]("guest")
|
val guest = column[Boolean]("guest")
|
||||||
val guestStatus = column[String]("guestStatus")
|
val guestStatus = column[String]("guestStatus")
|
||||||
val registeredOn = column[Long]("registeredOn")
|
val registeredOn = column[Long]("registeredOn")
|
||||||
val excludeFromDashboard = column[Boolean]("excludeFromDashboard")
|
val excludeFromDashboard = column[Boolean]("excludeFromDashboard")
|
||||||
|
val enforceLayout = column[Option[String]]("enforceLayout")
|
||||||
}
|
}
|
||||||
|
|
||||||
object UserDAO {
|
object UserDAO {
|
||||||
@ -57,23 +65,30 @@ object UserDAO {
|
|||||||
role = regUser.role,
|
role = regUser.role,
|
||||||
avatar = regUser.avatarURL,
|
avatar = regUser.avatarURL,
|
||||||
color = regUser.color,
|
color = regUser.color,
|
||||||
|
sessionToken = regUser.sessionToken,
|
||||||
authed = regUser.authed,
|
authed = regUser.authed,
|
||||||
joined = regUser.joined,
|
joined = regUser.joined,
|
||||||
|
joinErrorCode = None,
|
||||||
|
joinErrorMessage = None,
|
||||||
banned = regUser.banned,
|
banned = regUser.banned,
|
||||||
loggedOut = regUser.loggedOut,
|
loggedOut = regUser.loggedOut,
|
||||||
guest = regUser.guest,
|
guest = regUser.guest,
|
||||||
guestStatus = regUser.guestStatus,
|
guestStatus = regUser.guestStatus,
|
||||||
registeredOn = regUser.registeredOn,
|
registeredOn = regUser.registeredOn,
|
||||||
excludeFromDashboard = regUser.excludeFromDashboard
|
excludeFromDashboard = regUser.excludeFromDashboard,
|
||||||
|
enforceLayout = regUser.enforceLayout match {
|
||||||
|
case "" => None
|
||||||
|
case enforceLayout: String => Some(enforceLayout)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).onComplete {
|
).onComplete {
|
||||||
case Success(rowsAffected) => {
|
case Success(rowsAffected) => {
|
||||||
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted in User table!")
|
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted in User table!")
|
||||||
ChatUserDAO.insertUserPublicChat(meetingId, regUser.id)
|
UserConnectionStatusDAO.insert(meetingId, regUser.id)
|
||||||
UserConnectionStatusdDAO.insert(meetingId, regUser.id)
|
|
||||||
UserCustomParameterDAO.insert(regUser.id, regUser.customParameters)
|
UserCustomParameterDAO.insert(regUser.id, regUser.customParameters)
|
||||||
UserLocalSettingsDAO.insert(regUser.id, meetingId)
|
UserClientSettingsDAO.insert(regUser.id, meetingId)
|
||||||
|
ChatUserDAO.insertUserPublicChat(meetingId, regUser.id)
|
||||||
}
|
}
|
||||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting user: $e")
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting user: $e")
|
||||||
}
|
}
|
||||||
@ -91,18 +106,33 @@ object UserDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def updateVoiceUserJoined(voiceUserState: VoiceUserState) = {
|
||||||
|
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[UserDbTableDef]
|
||||||
|
.filter(_.userId === voiceUserState.intId)
|
||||||
|
.map(u => (u.guest, u.guestStatus, u.authed, u.joined))
|
||||||
|
.update((false, "ALLOW", true, true))
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on user voice table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating user voice: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateJoinError(userId: String, joinErrorCode: String, joinErrorMessage: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[UserDbTableDef]
|
||||||
|
.filter(_.userId === userId)
|
||||||
|
.map(u => (u.joined, u.joinErrorCode, u.joinErrorMessage))
|
||||||
|
.update((false, Some(joinErrorCode), Some(joinErrorMessage)))
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on user (Joined) table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating user (Joined): $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def delete(intId: String) = {
|
def delete(intId: String) = {
|
||||||
// DatabaseConnection.db.run(
|
|
||||||
// TableQuery[UserDbTableDef]
|
|
||||||
// .filter(_.userId === intId)
|
|
||||||
// .delete
|
|
||||||
// ).onComplete {
|
|
||||||
// case Success(rowsAffected) => DatabaseConnection.logger.debug(s"User ${intId} deleted")
|
|
||||||
// case Failure(e) => DatabaseConnection.logger.error(s"Error deleting user ${intId}: $e")
|
|
||||||
// }
|
|
||||||
|
|
||||||
DatabaseConnection.db.run(
|
DatabaseConnection.db.run(
|
||||||
TableQuery[UserDbTableDef]
|
TableQuery[UserDbTableDef]
|
||||||
.filter(_.userId === intId)
|
.filter(_.userId === intId)
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
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 UserGraphqlConnectionDbModel (
|
||||||
|
graphqlConnectionId: Option[Int],
|
||||||
|
sessionToken: String,
|
||||||
|
middlewareConnectionId: String,
|
||||||
|
stablishedAt: java.sql.Timestamp,
|
||||||
|
closedAt: Option[java.sql.Timestamp],
|
||||||
|
)
|
||||||
|
|
||||||
|
class UserGraphqlConnectionDbTableDef(tag: Tag) extends Table[UserGraphqlConnectionDbModel](tag, None, "user_graphqlConnection") {
|
||||||
|
override def * = (
|
||||||
|
graphqlConnectionId, sessionToken, middlewareConnectionId, stablishedAt, closedAt
|
||||||
|
) <> (UserGraphqlConnectionDbModel.tupled, UserGraphqlConnectionDbModel.unapply)
|
||||||
|
val graphqlConnectionId = column[Option[Int]]("graphqlConnectionId", O.PrimaryKey, O.AutoInc)
|
||||||
|
val sessionToken = column[String]("sessionToken")
|
||||||
|
val middlewareConnectionId = column[String]("middlewareConnectionId")
|
||||||
|
val stablishedAt = column[java.sql.Timestamp]("stablishedAt")
|
||||||
|
val closedAt = column[Option[java.sql.Timestamp]]("closedAt")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
object UserGraphqlConnectionDAO {
|
||||||
|
def insert(sessionToken: String, middlewareConnectionId: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[UserGraphqlConnectionDbTableDef].insertOrUpdate(
|
||||||
|
UserGraphqlConnectionDbModel(
|
||||||
|
graphqlConnectionId = None,
|
||||||
|
sessionToken = sessionToken,
|
||||||
|
middlewareConnectionId = middlewareConnectionId,
|
||||||
|
stablishedAt = new java.sql.Timestamp(System.currentTimeMillis()),
|
||||||
|
closedAt = None
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => {
|
||||||
|
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on user_graphqlConnection table!")
|
||||||
|
}
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting user_graphqlConnection: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateClosed(sessionToken: String, middlewareConnectionId: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[UserGraphqlConnectionDbTableDef]
|
||||||
|
.filter(_.sessionToken === sessionToken)
|
||||||
|
.filter(_.middlewareConnectionId === middlewareConnectionId)
|
||||||
|
.filter(_.closedAt.isEmpty)
|
||||||
|
.map(u => u.closedAt)
|
||||||
|
.update(Some(new java.sql.Timestamp(System.currentTimeMillis())))
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on user_graphqlConnection table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.error(s"Error updating user_graphqlConnection: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,38 +0,0 @@
|
|||||||
package org.bigbluebutton.core.db
|
|
||||||
|
|
||||||
import slick.jdbc.PostgresProfile.api._
|
|
||||||
import slick.lifted.{ ProvenShape }
|
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
|
||||||
import scala.util.{ Failure, Success }
|
|
||||||
|
|
||||||
case class UserLocalSettingsDbModel(
|
|
||||||
userId: String,
|
|
||||||
meetingId: String,
|
|
||||||
// settingsJson: String
|
|
||||||
)
|
|
||||||
|
|
||||||
class UserLocalSettingsDbTableDef(tag: Tag) extends Table[UserLocalSettingsDbModel](tag, "user_localSettings") {
|
|
||||||
val userId = column[String]("userId", O.PrimaryKey)
|
|
||||||
val meetingId = column[String]("meetingId")
|
|
||||||
// val settingsJson = column[String]("settingsJson")
|
|
||||||
|
|
||||||
override def * : ProvenShape[UserLocalSettingsDbModel] = (userId, meetingId) <> (UserLocalSettingsDbModel.tupled, UserLocalSettingsDbModel.unapply)
|
|
||||||
}
|
|
||||||
|
|
||||||
object UserLocalSettingsDAO {
|
|
||||||
def insert(userId: String, meetingId: String) = {
|
|
||||||
DatabaseConnection.db.run(
|
|
||||||
TableQuery[UserLocalSettingsDbTableDef].insertOrUpdate(
|
|
||||||
UserLocalSettingsDbModel(
|
|
||||||
userId = userId,
|
|
||||||
meetingId = meetingId
|
|
||||||
// settingsJson = parameter._2
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).onComplete {
|
|
||||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on UserLocalSettings table!")
|
|
||||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting UserLocalSettings: $e")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,17 +9,17 @@ import scala.util.{ Failure, Success }
|
|||||||
case class UserReactionDbModel(
|
case class UserReactionDbModel(
|
||||||
userId: String,
|
userId: String,
|
||||||
reactionEmoji: String,
|
reactionEmoji: String,
|
||||||
duration: Int,
|
durationInSeconds: Int,
|
||||||
createdAt: java.sql.Timestamp
|
createdAt: java.sql.Timestamp
|
||||||
)
|
)
|
||||||
|
|
||||||
class UserReactionDbTableDef(tag: Tag) extends Table[UserReactionDbModel](tag, "user_reaction") {
|
class UserReactionDbTableDef(tag: Tag) extends Table[UserReactionDbModel](tag, "user_reaction") {
|
||||||
val userId = column[String]("userId")
|
val userId = column[String]("userId")
|
||||||
val reactionEmoji = column[String]("reactionEmoji")
|
val reactionEmoji = column[String]("reactionEmoji")
|
||||||
val duration = column[Int]("duration")
|
val durationInSeconds = column[Int]("durationInSeconds")
|
||||||
val createdAt = column[java.sql.Timestamp]("createdAt")
|
val createdAt = column[java.sql.Timestamp]("createdAt")
|
||||||
|
|
||||||
override def * : ProvenShape[UserReactionDbModel] = (userId, reactionEmoji, duration, createdAt) <> (UserReactionDbModel.tupled, UserReactionDbModel.unapply)
|
override def * : ProvenShape[UserReactionDbModel] = (userId, reactionEmoji, durationInSeconds, createdAt) <> (UserReactionDbModel.tupled, UserReactionDbModel.unapply)
|
||||||
}
|
}
|
||||||
|
|
||||||
object UserReactionDAO {
|
object UserReactionDAO {
|
||||||
@ -29,7 +29,7 @@ object UserReactionDAO {
|
|||||||
UserReactionDbModel(
|
UserReactionDbModel(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
reactionEmoji = reactionEmoji,
|
reactionEmoji = reactionEmoji,
|
||||||
duration = 60,
|
durationInSeconds = 60,
|
||||||
createdAt = new java.sql.Timestamp(System.currentTimeMillis())
|
createdAt = new java.sql.Timestamp(System.currentTimeMillis())
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
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 UserVoiceConfStateDbModel(
|
||||||
|
userId: String,
|
||||||
|
voiceConf: String,
|
||||||
|
voiceConfCallSession: String,
|
||||||
|
voiceConfClientSession: String,
|
||||||
|
voiceConfCallState: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
class UserVoiceConfStateDbTableDef(tag: Tag) extends Table[UserVoiceConfStateDbModel](tag, None, "user_voice") {
|
||||||
|
override def * = (
|
||||||
|
userId, voiceConf, voiceConfCallSession, voiceConfClientSession, voiceConfCallState
|
||||||
|
) <> (UserVoiceConfStateDbModel.tupled, UserVoiceConfStateDbModel.unapply)
|
||||||
|
val userId = column[String]("userId", O.PrimaryKey)
|
||||||
|
val voiceConf = column[String]("voiceConf")
|
||||||
|
val voiceConfCallSession = column[String]("voiceConfCallSession")
|
||||||
|
val voiceConfClientSession = column[String]("voiceConfClientSession")
|
||||||
|
val voiceConfCallState = column[String]("voiceConfCallState")
|
||||||
|
}
|
||||||
|
|
||||||
|
object UserVoiceConfStateDAO {
|
||||||
|
def insertOrUpdate(userId: String, voiceConf: String, voiceConfCallSession: String, clientSession: String, callState: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[UserVoiceConfStateDbTableDef].insertOrUpdate(
|
||||||
|
UserVoiceConfStateDbModel(
|
||||||
|
userId = userId,
|
||||||
|
voiceConf = voiceConf,
|
||||||
|
voiceConfCallSession = voiceConfCallSession,
|
||||||
|
voiceConfClientSession = clientSession,
|
||||||
|
voiceConfCallState = callState,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => {
|
||||||
|
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on user_voice table!")
|
||||||
|
}
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting voice: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -19,7 +19,6 @@ case class UserVoiceDbModel(
|
|||||||
talking: Boolean,
|
talking: Boolean,
|
||||||
floor: Boolean,
|
floor: Boolean,
|
||||||
lastFloorTime: String,
|
lastFloorTime: String,
|
||||||
voiceConf: String,
|
|
||||||
startTime: Option[Long],
|
startTime: Option[Long],
|
||||||
endTime: Option[Long],
|
endTime: Option[Long],
|
||||||
)
|
)
|
||||||
@ -27,7 +26,7 @@ case class UserVoiceDbModel(
|
|||||||
class UserVoiceDbTableDef(tag: Tag) extends Table[UserVoiceDbModel](tag, None, "user_voice") {
|
class UserVoiceDbTableDef(tag: Tag) extends Table[UserVoiceDbModel](tag, None, "user_voice") {
|
||||||
override def * = (
|
override def * = (
|
||||||
userId, voiceUserId, callerName, callerNum, callingWith, joined, listenOnly,
|
userId, voiceUserId, callerName, callerNum, callingWith, joined, listenOnly,
|
||||||
muted, spoke, talking, floor, lastFloorTime, voiceConf, startTime, endTime
|
muted, spoke, talking, floor, lastFloorTime, startTime, endTime
|
||||||
) <> (UserVoiceDbModel.tupled, UserVoiceDbModel.unapply)
|
) <> (UserVoiceDbModel.tupled, UserVoiceDbModel.unapply)
|
||||||
val userId = column[String]("userId", O.PrimaryKey)
|
val userId = column[String]("userId", O.PrimaryKey)
|
||||||
val voiceUserId = column[String]("voiceUserId")
|
val voiceUserId = column[String]("voiceUserId")
|
||||||
@ -42,6 +41,9 @@ class UserVoiceDbTableDef(tag: Tag) extends Table[UserVoiceDbModel](tag, None, "
|
|||||||
val floor = column[Boolean]("floor")
|
val floor = column[Boolean]("floor")
|
||||||
val lastFloorTime = column[String]("lastFloorTime")
|
val lastFloorTime = column[String]("lastFloorTime")
|
||||||
val voiceConf = column[String]("voiceConf")
|
val voiceConf = column[String]("voiceConf")
|
||||||
|
val voiceConfCallSession = column[String]("voiceConfCallSession")
|
||||||
|
val voiceConfClientSession = column[String]("voiceConfClientSession")
|
||||||
|
val voiceConfCallState = column[String]("voiceConfCallState")
|
||||||
val startTime = column[Option[Long]]("startTime")
|
val startTime = column[Option[Long]]("startTime")
|
||||||
val endTime = column[Option[Long]]("endTime")
|
val endTime = column[Option[Long]]("endTime")
|
||||||
}
|
}
|
||||||
@ -64,7 +66,6 @@ object UserVoiceDAO {
|
|||||||
talking = voiceUserState.talking,
|
talking = voiceUserState.talking,
|
||||||
floor = voiceUserState.floor,
|
floor = voiceUserState.floor,
|
||||||
lastFloorTime = voiceUserState.lastFloorTime,
|
lastFloorTime = voiceUserState.lastFloorTime,
|
||||||
voiceConf = "",
|
|
||||||
startTime = None,
|
startTime = None,
|
||||||
endTime = None
|
endTime = None
|
||||||
)
|
)
|
||||||
@ -85,7 +86,7 @@ object UserVoiceDAO {
|
|||||||
.update((voiceUserState.listenOnly, voiceUserState.muted, voiceUserState.floor, voiceUserState.lastFloorTime))
|
.update((voiceUserState.listenOnly, voiceUserState.muted, voiceUserState.floor, voiceUserState.lastFloorTime))
|
||||||
).onComplete {
|
).onComplete {
|
||||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on user_voice table!")
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on user_voice table!")
|
||||||
case Failure(e) => DatabaseConnection.logger.error(s"Error updating user: $e")
|
case Failure(e) => DatabaseConnection.logger.error(s"Error updating user_voice: $e")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +114,19 @@ object UserVoiceDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def deleteUser(userId: String) = {
|
def delete(intId: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[UserDbTableDef]
|
||||||
|
.filter(_.userId === intId)
|
||||||
|
.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 deleteUserVoice(userId: String) = {
|
||||||
//Meteor sets this props instead of removing
|
//Meteor sets this props instead of removing
|
||||||
// muted: false
|
// muted: false
|
||||||
// talking: false
|
// talking: false
|
||||||
@ -124,10 +137,11 @@ object UserVoiceDAO {
|
|||||||
DatabaseConnection.db.run(
|
DatabaseConnection.db.run(
|
||||||
TableQuery[UserVoiceDbTableDef]
|
TableQuery[UserVoiceDbTableDef]
|
||||||
.filter(_.userId === userId)
|
.filter(_.userId === userId)
|
||||||
.delete
|
.map(u => (u.muted, u.talking, u.listenOnly, u.joined, u.spoke, u.startTime, u.endTime))
|
||||||
|
.update((false, false, false, false, false, None, None))
|
||||||
).onComplete {
|
).onComplete {
|
||||||
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"Voice of user ${userId} deleted")
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"Voice of user ${userId} deleted (joined=false)")
|
||||||
case Failure(e) => DatabaseConnection.logger.error(s"Error deleting voice: $e")
|
case Failure(e) => DatabaseConnection.logger.error(s"Error deleting voice user: $e")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package org.bigbluebutton.core.models
|
package org.bigbluebutton.core.models
|
||||||
|
|
||||||
import org.bigbluebutton.common2.domain.PageVO
|
|
||||||
import org.bigbluebutton.core.models.PresentationInPod
|
|
||||||
import org.bigbluebutton.core.util.RandomStringGenerator
|
import org.bigbluebutton.core.util.RandomStringGenerator
|
||||||
import org.bigbluebutton.common2.msgs.AnnotationVO
|
|
||||||
import org.bigbluebutton.core.db.{ PresPageDAO, PresPresentationDAO }
|
import org.bigbluebutton.core.db.{ PresPageDAO, PresPresentationDAO }
|
||||||
|
|
||||||
object PresentationPodFactory {
|
object PresentationPodFactory {
|
||||||
@ -25,6 +22,7 @@ case class PresentationPage(
|
|||||||
id: String,
|
id: String,
|
||||||
num: Int,
|
num: Int,
|
||||||
urls: Map[String, String],
|
urls: Map[String, String],
|
||||||
|
content: String,
|
||||||
current: Boolean = false,
|
current: Boolean = false,
|
||||||
xOffset: Double = 0,
|
xOffset: Double = 0,
|
||||||
yOffset: Double = 0,
|
yOffset: Double = 0,
|
||||||
@ -64,9 +62,11 @@ object PresentationInPod {
|
|||||||
case class PresentationInPod(
|
case class PresentationInPod(
|
||||||
id: String,
|
id: String,
|
||||||
name: String,
|
name: String,
|
||||||
|
default: Boolean = false,
|
||||||
current: Boolean = false,
|
current: Boolean = false,
|
||||||
pages: scala.collection.immutable.Map[String, PresentationPage],
|
pages: scala.collection.immutable.Map[String, PresentationPage],
|
||||||
downloadable: Boolean,
|
downloadable: Boolean,
|
||||||
|
downloadFileExtension: String = "",
|
||||||
removable: Boolean,
|
removable: Boolean,
|
||||||
filenameConverted: String = "",
|
filenameConverted: String = "",
|
||||||
uploadCompleted: Boolean,
|
uploadCompleted: Boolean,
|
||||||
@ -118,18 +118,18 @@ case class PresentationPod(id: String, currentPresenter: String,
|
|||||||
Some(tempPod)
|
Some(tempPod)
|
||||||
}
|
}
|
||||||
|
|
||||||
def setPresentationDownloadable(presentationId: String, downloadable: Boolean): Option[PresentationPod] = {
|
def setPresentationDownloadable(presentationId: String, downloadable: Boolean, downloadFileExtension: String): Option[PresentationPod] = {
|
||||||
var tempPod: PresentationPod = this
|
var tempPod: PresentationPod = this
|
||||||
presentations.values foreach (curPres => { // unset previous current presentation
|
presentations.values foreach (curPres => { // unset previous current presentation
|
||||||
if (curPres.id != presentationId) {
|
if (curPres.id != presentationId) {
|
||||||
val newPres = curPres.copy(downloadable = downloadable)
|
val newPres = curPres.copy(downloadable = downloadable, downloadFileExtension = downloadFileExtension)
|
||||||
tempPod = tempPod.addPresentation(newPres)
|
tempPod = tempPod.addPresentation(newPres)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
presentations.get(presentationId) match { // set new current presentation
|
presentations.get(presentationId) match { // set new current presentation
|
||||||
case Some(pres) =>
|
case Some(pres) =>
|
||||||
val cp = pres.copy(downloadable = downloadable)
|
val cp = pres.copy(downloadable = downloadable, downloadFileExtension = downloadFileExtension)
|
||||||
tempPod = tempPod.addPresentation(cp)
|
tempPod = tempPod.addPresentation(cp)
|
||||||
case None => None
|
case None => None
|
||||||
}
|
}
|
||||||
@ -235,10 +235,10 @@ case class PresentationPodManager(presentationPods: collection.immutable.Map[Str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def setPresentationDownloadableInPod(podId: String, presentationId: String, downloadable: Boolean): PresentationPodManager = {
|
def setPresentationDownloadableInPod(podId: String, presentationId: String, downloadable: Boolean, downloadFileExtension: String): PresentationPodManager = {
|
||||||
val updatedManager = for {
|
val updatedManager = for {
|
||||||
pod <- getPod(podId)
|
pod <- getPod(podId)
|
||||||
podWithAdjustedDownloadablePresentation <- pod.setPresentationDownloadable(presentationId, downloadable)
|
podWithAdjustedDownloadablePresentation <- pod.setPresentationDownloadable(presentationId, downloadable, downloadFileExtension)
|
||||||
|
|
||||||
} yield {
|
} yield {
|
||||||
updatePresentationPod(podWithAdjustedDownloadablePresentation)
|
updatePresentationPod(podWithAdjustedDownloadablePresentation)
|
||||||
|
@ -7,7 +7,8 @@ import org.bigbluebutton.core.domain.BreakoutRoom2x
|
|||||||
object RegisteredUsers {
|
object RegisteredUsers {
|
||||||
def create(userId: String, extId: String, name: String, roles: String,
|
def create(userId: String, extId: String, name: String, roles: String,
|
||||||
authToken: String, sessionToken: String, avatar: String, color: String, guest: Boolean, authenticated: Boolean,
|
authToken: String, sessionToken: String, avatar: String, color: String, guest: Boolean, authenticated: Boolean,
|
||||||
guestStatus: String, excludeFromDashboard: Boolean, customParameters: Map[String, String], loggedOut: Boolean): RegisteredUser = {
|
guestStatus: String, excludeFromDashboard: Boolean, enforceLayout: String,
|
||||||
|
customParameters: Map[String, String], loggedOut: Boolean): RegisteredUser = {
|
||||||
new RegisteredUser(
|
new RegisteredUser(
|
||||||
userId,
|
userId,
|
||||||
extId,
|
extId,
|
||||||
@ -25,6 +26,7 @@ object RegisteredUsers {
|
|||||||
0,
|
0,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
enforceLayout,
|
||||||
customParameters,
|
customParameters,
|
||||||
loggedOut,
|
loggedOut,
|
||||||
)
|
)
|
||||||
@ -214,6 +216,7 @@ case class RegisteredUser(
|
|||||||
lastAuthTokenValidatedOn: Long,
|
lastAuthTokenValidatedOn: Long,
|
||||||
joined: Boolean,
|
joined: Boolean,
|
||||||
banned: Boolean,
|
banned: Boolean,
|
||||||
|
enforceLayout: String,
|
||||||
customParameters: Map[String,String],
|
customParameters: Map[String,String],
|
||||||
loggedOut: Boolean,
|
loggedOut: Boolean,
|
||||||
lastBreakoutRoom: BreakoutRoom2x = null,
|
lastBreakoutRoom: BreakoutRoom2x = null,
|
||||||
|
@ -39,7 +39,7 @@ object VoiceUsers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def removeWithIntId(users: VoiceUsers, intId: String): Option[VoiceUserState] = {
|
def removeWithIntId(users: VoiceUsers, intId: String): Option[VoiceUserState] = {
|
||||||
UserVoiceDAO.deleteUser(intId)
|
UserVoiceDAO.deleteUserVoice(intId)
|
||||||
users.remove(intId)
|
users.remove(intId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,8 +346,6 @@ class ReceivedJsonMsgHandlerActor(
|
|||||||
routeGenericMsg[CreateNewPresentationPodPubMsg](envelope, jsonNode)
|
routeGenericMsg[CreateNewPresentationPodPubMsg](envelope, jsonNode)
|
||||||
case RemovePresentationPodPubMsg.NAME =>
|
case RemovePresentationPodPubMsg.NAME =>
|
||||||
routeGenericMsg[RemovePresentationPodPubMsg](envelope, jsonNode)
|
routeGenericMsg[RemovePresentationPodPubMsg](envelope, jsonNode)
|
||||||
case SetPresenterInPodReqMsg.NAME =>
|
|
||||||
routeGenericMsg[SetPresenterInPodReqMsg](envelope, jsonNode)
|
|
||||||
|
|
||||||
// Caption
|
// Caption
|
||||||
case EditCaptionHistoryPubMsg.NAME =>
|
case EditCaptionHistoryPubMsg.NAME =>
|
||||||
@ -417,6 +415,10 @@ class ReceivedJsonMsgHandlerActor(
|
|||||||
case CreateGroupChatReqMsg.NAME =>
|
case CreateGroupChatReqMsg.NAME =>
|
||||||
routeGenericMsg[CreateGroupChatReqMsg](envelope, jsonNode)
|
routeGenericMsg[CreateGroupChatReqMsg](envelope, jsonNode)
|
||||||
|
|
||||||
|
//Plugin
|
||||||
|
case DispatchPluginDataChannelMessageMsg.NAME =>
|
||||||
|
routeGenericMsg[DispatchPluginDataChannelMessageMsg](envelope, jsonNode)
|
||||||
|
|
||||||
// ExternalVideo
|
// ExternalVideo
|
||||||
case StartExternalVideoPubMsg.NAME =>
|
case StartExternalVideoPubMsg.NAME =>
|
||||||
routeGenericMsg[StartExternalVideoPubMsg](envelope, jsonNode)
|
routeGenericMsg[StartExternalVideoPubMsg](envelope, jsonNode)
|
||||||
@ -447,6 +449,13 @@ class ReceivedJsonMsgHandlerActor(
|
|||||||
case TimerEndedPubMsg.NAME =>
|
case TimerEndedPubMsg.NAME =>
|
||||||
routeGenericMsg[TimerEndedPubMsg](envelope, jsonNode)
|
routeGenericMsg[TimerEndedPubMsg](envelope, jsonNode)
|
||||||
|
|
||||||
|
// Messages from Graphql Middleware
|
||||||
|
case UserGraphqlConnectionStablishedSysMsg.NAME =>
|
||||||
|
route[UserGraphqlConnectionStablishedSysMsg](meetingManagerChannel, envelope, jsonNode)
|
||||||
|
|
||||||
|
case UserGraphqlConnectionClosedSysMsg.NAME =>
|
||||||
|
route[UserGraphqlConnectionClosedSysMsg](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
|
||||||
|
@ -5,6 +5,8 @@ import org.bigbluebutton.core.apps._
|
|||||||
import org.bigbluebutton.core.models._
|
import org.bigbluebutton.core.models._
|
||||||
import org.bigbluebutton.core2.MeetingStatus2x
|
import org.bigbluebutton.core2.MeetingStatus2x
|
||||||
|
|
||||||
|
import java.util
|
||||||
|
|
||||||
class LiveMeeting(
|
class LiveMeeting(
|
||||||
val props: DefaultProps,
|
val props: DefaultProps,
|
||||||
val status: MeetingStatus2x,
|
val status: MeetingStatus2x,
|
||||||
@ -23,5 +25,6 @@ class LiveMeeting(
|
|||||||
val webcams: Webcams,
|
val webcams: Webcams,
|
||||||
val voiceUsers: VoiceUsers,
|
val voiceUsers: VoiceUsers,
|
||||||
val users2x: Users2x,
|
val users2x: Users2x,
|
||||||
val guestsWaiting: GuestsWaiting
|
val guestsWaiting: GuestsWaiting,
|
||||||
|
val clientSettings: Map[String, Object],
|
||||||
)
|
)
|
||||||
|
@ -39,6 +39,7 @@ import org.bigbluebutton.common2.msgs
|
|||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import org.bigbluebutton.core.apps.layout.LayoutApp2x
|
import org.bigbluebutton.core.apps.layout.LayoutApp2x
|
||||||
import org.bigbluebutton.core.apps.meeting.{ SyncGetMeetingInfoRespMsgHdlr, ValidateConnAuthTokenSysMsgHdlr }
|
import org.bigbluebutton.core.apps.meeting.{ SyncGetMeetingInfoRespMsgHdlr, ValidateConnAuthTokenSysMsgHdlr }
|
||||||
|
import org.bigbluebutton.core.apps.plugin.PluginHdlrs
|
||||||
import org.bigbluebutton.core.apps.users.ChangeLockSettingsInMeetingCmdMsgHdlr
|
import org.bigbluebutton.core.apps.users.ChangeLockSettingsInMeetingCmdMsgHdlr
|
||||||
import org.bigbluebutton.core.db.UserStateDAO
|
import org.bigbluebutton.core.db.UserStateDAO
|
||||||
import org.bigbluebutton.core.models.VoiceUsers.{ findAllFreeswitchCallers, findAllListenOnlyVoiceUsers }
|
import org.bigbluebutton.core.models.VoiceUsers.{ findAllFreeswitchCallers, findAllListenOnlyVoiceUsers }
|
||||||
@ -137,6 +138,7 @@ class MeetingActor(
|
|||||||
val webcamApp2x = new WebcamApp2x
|
val webcamApp2x = new WebcamApp2x
|
||||||
val wbApp = new WhiteboardApp2x
|
val wbApp = new WhiteboardApp2x
|
||||||
val timerApp2x = new TimerApp2x
|
val timerApp2x = new TimerApp2x
|
||||||
|
val pluginHdlrs = new PluginHdlrs
|
||||||
|
|
||||||
object ExpiryTrackerHelper extends MeetingExpiryTrackerHelper
|
object ExpiryTrackerHelper extends MeetingExpiryTrackerHelper
|
||||||
|
|
||||||
@ -263,6 +265,7 @@ 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: ExtendMeetingDuration => handleExtendMeetingDuration(msg)
|
case msg: ExtendMeetingDuration => handleExtendMeetingDuration(msg)
|
||||||
case msg: SendTimeRemainingAuditInternalMsg =>
|
case msg: SendTimeRemainingAuditInternalMsg =>
|
||||||
@ -529,7 +532,6 @@ class MeetingActor(
|
|||||||
case m: PresentationConversionCompletedSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
case m: PresentationConversionCompletedSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||||
case m: PdfConversionInvalidErrorSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
case m: PdfConversionInvalidErrorSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||||
case m: SetCurrentPagePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
case m: SetCurrentPagePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||||
case m: SetPresenterInPodReqMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
|
||||||
case m: RemovePresentationPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
case m: RemovePresentationPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||||
case m: SetPresentationDownloadablePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
case m: SetPresentationDownloadablePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||||
case m: PresentationConversionUpdateSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
case m: PresentationConversionUpdateSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||||
@ -591,6 +593,9 @@ class MeetingActor(
|
|||||||
state = groupChatApp.handle(m, state, liveMeeting, msgBus)
|
state = groupChatApp.handle(m, state, liveMeeting, msgBus)
|
||||||
updateUserLastActivity(m.body.msg.sender.id)
|
updateUserLastActivity(m.body.msg.sender.id)
|
||||||
|
|
||||||
|
// Plugin
|
||||||
|
case m: DispatchPluginDataChannelMessageMsg => pluginHdlrs.handle(m, state, liveMeeting)
|
||||||
|
|
||||||
// Webcams
|
// Webcams
|
||||||
case m: UserBroadcastCamStartMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
|
case m: UserBroadcastCamStartMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
|
||||||
case m: UserBroadcastCamStopMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
|
case m: UserBroadcastCamStopMsg => webcamApp2x.handle(m, liveMeeting, msgBus)
|
||||||
@ -867,6 +872,7 @@ class MeetingActor(
|
|||||||
val hasModeratorLeftRecently = (TimeUtil.timeNowInMs() - state.expiryTracker.endWhenNoModeratorDelayInMs) < state.expiryTracker.lastModeratorLeftOnInMs
|
val hasModeratorLeftRecently = (TimeUtil.timeNowInMs() - state.expiryTracker.endWhenNoModeratorDelayInMs) < state.expiryTracker.lastModeratorLeftOnInMs
|
||||||
if (!hasModeratorLeftRecently) {
|
if (!hasModeratorLeftRecently) {
|
||||||
log.info("Meeting will end due option endWhenNoModerator is enabled and all moderators have left the meeting. meetingId=" + props.meetingProp.intId)
|
log.info("Meeting will end due option endWhenNoModerator is enabled and all moderators have left the meeting. meetingId=" + props.meetingProp.intId)
|
||||||
|
endAllBreakoutRooms(eventBus, liveMeeting, state, MeetingEndReason.ENDED_DUE_TO_NO_MODERATOR)
|
||||||
sendEndMeetingDueToExpiry(
|
sendEndMeetingDueToExpiry(
|
||||||
MeetingEndReason.ENDED_DUE_TO_NO_MODERATOR,
|
MeetingEndReason.ENDED_DUE_TO_NO_MODERATOR,
|
||||||
eventBus, outGW, liveMeeting,
|
eventBus, outGW, liveMeeting,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.bigbluebutton.core.running
|
package org.bigbluebutton.core.running
|
||||||
|
|
||||||
import org.apache.pekko.actor.ActorContext
|
import org.apache.pekko.actor.ActorContext
|
||||||
|
import org.bigbluebutton.ClientSettings
|
||||||
import org.bigbluebutton.common2.domain.DefaultProps
|
import org.bigbluebutton.common2.domain.DefaultProps
|
||||||
import org.bigbluebutton.core.apps._
|
import org.bigbluebutton.core.apps._
|
||||||
import org.bigbluebutton.core.bus._
|
import org.bigbluebutton.core.bus._
|
||||||
@ -34,6 +35,7 @@ class RunningMeeting(val props: DefaultProps, outGW: OutMessageGateway,
|
|||||||
private val deskshareModel = new ScreenshareModel
|
private val deskshareModel = new ScreenshareModel
|
||||||
private val audioCaptions = new AudioCaptions
|
private val audioCaptions = new AudioCaptions
|
||||||
private val timerModel = new TimerModel
|
private val timerModel = new TimerModel
|
||||||
|
val clientSettings: Map[String, Object] = ClientSettings.getClientSettingsWithOverride(props.overrideClientSettings)
|
||||||
|
|
||||||
// meetingModel.setGuestPolicy(props.usersProp.guestPolicy)
|
// meetingModel.setGuestPolicy(props.usersProp.guestPolicy)
|
||||||
|
|
||||||
@ -41,7 +43,7 @@ class RunningMeeting(val props: DefaultProps, outGW: OutMessageGateway,
|
|||||||
// easy to test.
|
// easy to test.
|
||||||
private val liveMeeting = new LiveMeeting(props, meetingStatux2x, deskshareModel, audioCaptions, timerModel,
|
private val liveMeeting = new LiveMeeting(props, meetingStatux2x, deskshareModel, audioCaptions, timerModel,
|
||||||
chatModel, externalVideoModel, layouts, pads, registeredUsers, polls2x, wbModel, presModel, captionModel,
|
chatModel, externalVideoModel, layouts, pads, registeredUsers, polls2x, wbModel, presModel, captionModel,
|
||||||
webcams, voiceUsers, users2x, guestsWaiting)
|
webcams, voiceUsers, users2x, guestsWaiting, clientSettings)
|
||||||
|
|
||||||
GuestsWaiting.setGuestPolicy(
|
GuestsWaiting.setGuestPolicy(
|
||||||
liveMeeting.props.meetingProp.intId,
|
liveMeeting.props.meetingProp.intId,
|
||||||
|
@ -10,7 +10,7 @@ object RunningMeetings {
|
|||||||
|
|
||||||
def add(meetings: RunningMeetings, meeting: RunningMeeting): RunningMeeting = {
|
def add(meetings: RunningMeetings, meeting: RunningMeeting): RunningMeeting = {
|
||||||
meetings.save(meeting)
|
meetings.save(meeting)
|
||||||
MeetingDAO.insert(meeting.props)
|
MeetingDAO.insert(meeting.props, meeting.clientSettings)
|
||||||
meeting
|
meeting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package org.bigbluebutton.core2.message.senders
|
package org.bigbluebutton.core2.message.senders
|
||||||
|
|
||||||
import org.bigbluebutton.common2.domain.DefaultProps
|
import org.bigbluebutton.common2.domain.DefaultProps
|
||||||
import org.bigbluebutton.common2.msgs.{ BbbCommonEnvCoreMsg, BbbCoreEnvelope, BbbCoreHeaderWithMeetingId, MessageTypes, Routing, ValidateConnAuthTokenSysRespMsg, ValidateConnAuthTokenSysRespMsgBody, NotifyAllInMeetingEvtMsg, NotifyAllInMeetingEvtMsgBody, NotifyRoleInMeetingEvtMsg, NotifyRoleInMeetingEvtMsgBody, NotifyUserInMeetingEvtMsg, NotifyUserInMeetingEvtMsgBody, _ }
|
import org.bigbluebutton.common2.msgs.{ BbbCommonEnvCoreMsg, BbbCoreEnvelope, BbbCoreHeaderWithMeetingId, MessageTypes, NotifyAllInMeetingEvtMsg, NotifyAllInMeetingEvtMsgBody, NotifyRoleInMeetingEvtMsg, NotifyRoleInMeetingEvtMsgBody, NotifyUserInMeetingEvtMsg, NotifyUserInMeetingEvtMsgBody, Routing, ValidateConnAuthTokenSysRespMsg, ValidateConnAuthTokenSysRespMsgBody, _ }
|
||||||
import org.bigbluebutton.core.models.GuestWaiting
|
import org.bigbluebutton.core.models.{ GuestWaiting, PresentationPod }
|
||||||
|
|
||||||
object MsgBuilder {
|
object MsgBuilder {
|
||||||
def buildGuestPolicyChangedEvtMsg(meetingId: String, userId: String, policy: String, setBy: String): BbbCommonEnvCoreMsg = {
|
def buildGuestPolicyChangedEvtMsg(meetingId: String, userId: String, policy: String, setBy: String): BbbCommonEnvCoreMsg = {
|
||||||
|
@ -56,7 +56,7 @@ object FakeUserGenerator {
|
|||||||
val color = "#ff6242"
|
val color = "#ff6242"
|
||||||
|
|
||||||
val ru = RegisteredUsers.create(userId = id, extId, name, role,
|
val ru = RegisteredUsers.create(userId = id, extId, name, role,
|
||||||
authToken, sessionToken, avatarURL, color, guest, authed, guestStatus = GuestStatus.ALLOW, false, Map(), false)
|
authToken, sessionToken, avatarURL, color, guest, authed, guestStatus = GuestStatus.ALLOW, false, "", Map(), false)
|
||||||
RegisteredUsers.add(users, ru, meetingId)
|
RegisteredUsers.add(users, ru, meetingId)
|
||||||
ru
|
ru
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
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: UserGraphqlConnectionStablishedSysMsg => handleUserGraphqlConnectionStablishedSysMsg(m)
|
||||||
|
case m: UserGraphqlConnectionClosedSysMsg => handleUserGraphqlConnectionClosedSysMsg(m)
|
||||||
|
case _ => // message not to be handled.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleUserGraphqlConnectionStablishedSysMsg(msg: UserGraphqlConnectionStablishedSysMsg) {
|
||||||
|
UserGraphqlConnectionDAO.insert(msg.body.sessionToken, msg.body.browserConnectionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleUserGraphqlConnectionClosedSysMsg(msg: UserGraphqlConnectionClosedSysMsg) {
|
||||||
|
UserGraphqlConnectionDAO.updateClosed(msg.body.sessionToken, msg.body.browserConnectionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -397,10 +397,33 @@ class LearningDashboardActor(
|
|||||||
user <- findUserByIntId(meeting, msg.body.userId)
|
user <- findUserByIntId(meeting, msg.body.userId)
|
||||||
} yield {
|
} yield {
|
||||||
if (msg.body.reactionEmoji != "none") {
|
if (msg.body.reactionEmoji != "none") {
|
||||||
|
//Ignore multiple Reactions to prevent flooding
|
||||||
|
val hasSameReactionInLast30Seconds = user.reactions.filter(r => {
|
||||||
|
System.currentTimeMillis() - r.sentOn < (30 * 1000) && r.name == msg.body.reactionEmoji
|
||||||
|
}).length > 0
|
||||||
|
|
||||||
|
if(!hasSameReactionInLast30Seconds) {
|
||||||
val updatedUser = user.copy(reactions = user.reactions :+ Emoji(msg.body.reactionEmoji))
|
val updatedUser = user.copy(reactions = user.reactions :+ Emoji(msg.body.reactionEmoji))
|
||||||
val updatedMeeting = meeting.copy(users = meeting.users + (updatedUser.userKey -> updatedUser))
|
val updatedMeeting = meeting.copy(users = meeting.users + (updatedUser.userKey -> updatedUser))
|
||||||
|
|
||||||
meetings += (updatedMeeting.intId -> updatedMeeting)
|
meetings += (updatedMeeting.intId -> updatedMeeting)
|
||||||
|
|
||||||
|
//Convert Reactions to legacy Emoji (while LearningDashboard doesn't support Reactions)
|
||||||
|
val emoji = msg.body.reactionEmoji.codePointAt(0) match {
|
||||||
|
case 128515 => "happy"
|
||||||
|
case 128528 => "neutral"
|
||||||
|
case 128577 => "sad"
|
||||||
|
case 128077 => "thumbsUp"
|
||||||
|
case 128078 => "thumbsDown"
|
||||||
|
case 128079 => "applause"
|
||||||
|
case _ => "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emoji != "none") {
|
||||||
|
val updatedUserWithEmoji = updatedUser.copy(emojis = user.emojis :+ Emoji(emoji))
|
||||||
|
val updatedMeetingWithEmoji = meeting.copy(users = meeting.users + (updatedUserWithEmoji.userKey -> updatedUserWithEmoji))
|
||||||
|
meetings += (updatedMeeting.intId -> updatedMeetingWithEmoji)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,10 @@ object TestDataGen {
|
|||||||
val sessionToken = RandomStringGenerator.randomAlphanumericString(16)
|
val sessionToken = RandomStringGenerator.randomAlphanumericString(16)
|
||||||
val avatarURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" +
|
val avatarURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" +
|
||||||
RandomStringGenerator.randomAlphanumericString(10) + ".png"
|
RandomStringGenerator.randomAlphanumericString(10) + ".png"
|
||||||
|
val color = "#ff6242"
|
||||||
|
|
||||||
val ru = RegisteredUsers.create(userId = id, extId, name, role,
|
val ru = RegisteredUsers.create(userId = id, extId, name, role,
|
||||||
authToken, sessionToken, avatarURL, guest, authed, GuestStatus.ALLOW, false)
|
authToken, sessionToken, avatarURL, color, guest, authed, GuestStatus.ALLOW, false, "", Map(), false)
|
||||||
|
|
||||||
RegisteredUsers.add(users, ru, meetingId = "test")
|
RegisteredUsers.add(users, ru, meetingId = "test")
|
||||||
ru
|
ru
|
||||||
|
@ -27,6 +27,11 @@ pekko {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client {
|
||||||
|
clientSettingsFilePath = "/usr/share/meteor/bundle/programs/server/assets/app/config/settings.yml"
|
||||||
|
clientSettingsOverrideFilePath = "/etc/bigbluebutton/bbb-html5.yml"
|
||||||
|
}
|
||||||
|
|
||||||
redis {
|
redis {
|
||||||
host="127.0.0.1"
|
host="127.0.0.1"
|
||||||
port=6379
|
port=6379
|
||||||
|
@ -19,6 +19,7 @@ object Dependencies {
|
|||||||
val sl4j = "1.7.32"
|
val sl4j = "1.7.32"
|
||||||
val pool = "2.11.1"
|
val pool = "2.11.1"
|
||||||
val codec = "1.15"
|
val codec = "1.15"
|
||||||
|
val jacksonDataFormat = "2.13.5"
|
||||||
|
|
||||||
// Redis
|
// Redis
|
||||||
val lettuce = "6.1.5.RELEASE"
|
val lettuce = "6.1.5.RELEASE"
|
||||||
@ -40,6 +41,7 @@ object Dependencies {
|
|||||||
val commonsCodec = "commons-codec" % "commons-codec" % Versions.codec
|
val commonsCodec = "commons-codec" % "commons-codec" % Versions.codec
|
||||||
|
|
||||||
val lettuceCore = "io.lettuce" % "lettuce-core" % Versions.lettuce
|
val lettuceCore = "io.lettuce" % "lettuce-core" % Versions.lettuce
|
||||||
|
val jacksonDataFormat = "com.fasterxml.jackson.dataformat" % "jackson-dataformat-yaml" % Versions.jacksonDataFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
object Test {
|
object Test {
|
||||||
@ -64,5 +66,6 @@ object Dependencies {
|
|||||||
Compile.sl4jApi,
|
Compile.sl4jApi,
|
||||||
Compile.commonsCodec,
|
Compile.commonsCodec,
|
||||||
Compile.apachePool2,
|
Compile.apachePool2,
|
||||||
Compile.lettuceCore) ++ testing
|
Compile.lettuceCore,
|
||||||
|
Compile.jacksonDataFormat) ++ testing
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,8 @@ case class DefaultProps(
|
|||||||
metadataProp: MetadataProp,
|
metadataProp: MetadataProp,
|
||||||
lockSettingsProps: LockSettingsProps,
|
lockSettingsProps: LockSettingsProps,
|
||||||
systemProps: SystemProps,
|
systemProps: SystemProps,
|
||||||
groups: Vector[GroupProps]
|
groups: Vector[GroupProps],
|
||||||
|
overrideClientSettings: String
|
||||||
)
|
)
|
||||||
|
|
||||||
case class StartEndTimeStatus(startTime: Long, endTime: Long)
|
case class StartEndTimeStatus(startTime: Long, endTime: Long)
|
||||||
|
@ -2,7 +2,7 @@ package org.bigbluebutton.common2.domain
|
|||||||
|
|
||||||
case class PresentationVO(id: String, temporaryPresentationId: String, name: String, current: Boolean = false,
|
case class PresentationVO(id: String, temporaryPresentationId: String, name: String, current: Boolean = false,
|
||||||
pages: Vector[PageVO], downloadable: Boolean, removable: Boolean,
|
pages: Vector[PageVO], downloadable: Boolean, removable: Boolean,
|
||||||
isInitialPresentation: Boolean, filenameConverted: String)
|
defaultPresentation: Boolean, filenameConverted: String)
|
||||||
|
|
||||||
case class PageVO(id: String, num: Int, thumbUri: String = "",
|
case class PageVO(id: String, num: Int, thumbUri: String = "",
|
||||||
txtUri: String, svgUri: String, current: Boolean = false, xOffset: Double = 0,
|
txtUri: String, svgUri: String, current: Boolean = false, xOffset: Double = 0,
|
||||||
@ -15,6 +15,7 @@ case class PresentationPageConvertedVO(
|
|||||||
id: String,
|
id: String,
|
||||||
num: Int,
|
num: Int,
|
||||||
urls: Map[String, String],
|
urls: Map[String, String],
|
||||||
|
content: String,
|
||||||
current: Boolean = false,
|
current: Boolean = false,
|
||||||
width: Double = 1440D,
|
width: Double = 1440D,
|
||||||
height: Double = 1080D
|
height: Double = 1080D
|
||||||
|
@ -11,6 +11,7 @@ object GroupChatMessageType {
|
|||||||
val POLL = "poll"
|
val POLL = "poll"
|
||||||
val BREAKOUTROOM_MOD_MSG = "breakoutRoomModeratorMsg"
|
val BREAKOUTROOM_MOD_MSG = "breakoutRoomModeratorMsg"
|
||||||
val PUBLIC_CHAT_HIST_CLEARED = "publicChatHistoryCleared"
|
val PUBLIC_CHAT_HIST_CLEARED = "publicChatHistoryCleared"
|
||||||
|
val USER_AWAY_STATUS_MSG = "userAwayStatusMsg"
|
||||||
}
|
}
|
||||||
|
|
||||||
case class GroupChatUser(id: String, name: String = "", role: String = "VIEWER")
|
case class GroupChatUser(id: String, name: String = "", role: String = "VIEWER")
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package org.bigbluebutton.common2.msgs
|
||||||
|
|
||||||
|
// In messages
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent from graphql-actions to bbb-akka
|
||||||
|
*/
|
||||||
|
object DispatchPluginDataChannelMessageMsg { val NAME = "DispatchPluginDataChannelMessageMsg" }
|
||||||
|
case class DispatchPluginDataChannelMessageMsg(header: BbbClientMsgHeader, body: DispatchPluginDataChannelMessageMsgBody) extends StandardMsg
|
||||||
|
case class DispatchPluginDataChannelMessageMsgBody(
|
||||||
|
pluginName: String,
|
||||||
|
dataChannel: String,
|
||||||
|
payloadJson: String,
|
||||||
|
toRoles: List[String],
|
||||||
|
toUserIds: List[String],
|
||||||
|
)
|
@ -13,7 +13,7 @@ case class RemovePresentationPodPubMsgBody(podId: String)
|
|||||||
|
|
||||||
object PresentationUploadTokenReqMsg { val NAME = "PresentationUploadTokenReqMsg" }
|
object PresentationUploadTokenReqMsg { val NAME = "PresentationUploadTokenReqMsg" }
|
||||||
case class PresentationUploadTokenReqMsg(header: BbbClientMsgHeader, body: PresentationUploadTokenReqMsgBody) extends StandardMsg
|
case class PresentationUploadTokenReqMsg(header: BbbClientMsgHeader, body: PresentationUploadTokenReqMsgBody) extends StandardMsg
|
||||||
case class PresentationUploadTokenReqMsgBody(podId: String, filename: String, temporaryPresentationId: String)
|
case class PresentationUploadTokenReqMsgBody(podId: String, filename: String, uploadTemporaryId: String)
|
||||||
|
|
||||||
object GetAllPresentationPodsReqMsg { val NAME = "GetAllPresentationPodsReqMsg" }
|
object GetAllPresentationPodsReqMsg { val NAME = "GetAllPresentationPodsReqMsg" }
|
||||||
case class GetAllPresentationPodsReqMsg(header: BbbClientMsgHeader, body: GetAllPresentationPodsReqMsgBody) extends StandardMsg
|
case class GetAllPresentationPodsReqMsg(header: BbbClientMsgHeader, body: GetAllPresentationPodsReqMsgBody) extends StandardMsg
|
||||||
@ -23,10 +23,6 @@ object SetCurrentPagePubMsg { val NAME = "SetCurrentPagePubMsg" }
|
|||||||
case class SetCurrentPagePubMsg(header: BbbClientMsgHeader, body: SetCurrentPagePubMsgBody) extends StandardMsg
|
case class SetCurrentPagePubMsg(header: BbbClientMsgHeader, body: SetCurrentPagePubMsgBody) extends StandardMsg
|
||||||
case class SetCurrentPagePubMsgBody(podId: String, presentationId: String, pageId: String)
|
case class SetCurrentPagePubMsgBody(podId: String, presentationId: String, pageId: String)
|
||||||
|
|
||||||
object SetPresenterInPodReqMsg { val NAME = "SetPresenterInPodReqMsg" }
|
|
||||||
case class SetPresenterInPodReqMsg(header: BbbClientMsgHeader, body: SetPresenterInPodReqMsgBody) extends StandardMsg
|
|
||||||
case class SetPresenterInPodReqMsgBody(podId: String, nextPresenterId: String)
|
|
||||||
|
|
||||||
object RemovePresentationPubMsg { val NAME = "RemovePresentationPubMsg" }
|
object RemovePresentationPubMsg { val NAME = "RemovePresentationPubMsg" }
|
||||||
case class RemovePresentationPubMsg(header: BbbClientMsgHeader, body: RemovePresentationPubMsgBody) extends StandardMsg
|
case class RemovePresentationPubMsg(header: BbbClientMsgHeader, body: RemovePresentationPubMsgBody) extends StandardMsg
|
||||||
case class RemovePresentationPubMsgBody(podId: String, presentationId: String)
|
case class RemovePresentationPubMsgBody(podId: String, presentationId: String)
|
||||||
@ -139,7 +135,9 @@ case class PresentationPageConversionStartedSysMsgBody(
|
|||||||
podId: String,
|
podId: String,
|
||||||
presentationId: String,
|
presentationId: String,
|
||||||
current: Boolean,
|
current: Boolean,
|
||||||
|
default: Boolean,
|
||||||
presName: String,
|
presName: String,
|
||||||
|
presFilenameConverted: String,
|
||||||
downloadable: Boolean,
|
downloadable: Boolean,
|
||||||
removable: Boolean,
|
removable: Boolean,
|
||||||
authzToken: String,
|
authzToken: String,
|
||||||
@ -221,7 +219,7 @@ case class PdfConversionInvalidErrorEvtMsgBody(podId: String, messageKey: String
|
|||||||
|
|
||||||
object PresentationUploadTokenPassRespMsg { val NAME = "PresentationUploadTokenPassRespMsg" }
|
object PresentationUploadTokenPassRespMsg { val NAME = "PresentationUploadTokenPassRespMsg" }
|
||||||
case class PresentationUploadTokenPassRespMsg(header: BbbClientMsgHeader, body: PresentationUploadTokenPassRespMsgBody) extends StandardMsg
|
case class PresentationUploadTokenPassRespMsg(header: BbbClientMsgHeader, body: PresentationUploadTokenPassRespMsgBody) extends StandardMsg
|
||||||
case class PresentationUploadTokenPassRespMsgBody(podId: String, authzToken: String, filename: String, temporaryPresentationId: String)
|
case class PresentationUploadTokenPassRespMsgBody(podId: String, authzToken: String, filename: String, temporaryPresentationId: String, presentationId: String)
|
||||||
|
|
||||||
object PresentationUploadTokenFailRespMsg { val NAME = "PresentationUploadTokenFailRespMsg" }
|
object PresentationUploadTokenFailRespMsg { val NAME = "PresentationUploadTokenFailRespMsg" }
|
||||||
case class PresentationUploadTokenFailRespMsg(header: BbbClientMsgHeader, body: PresentationUploadTokenFailRespMsgBody) extends StandardMsg
|
case class PresentationUploadTokenFailRespMsg(header: BbbClientMsgHeader, body: PresentationUploadTokenFailRespMsgBody) extends StandardMsg
|
||||||
@ -359,5 +357,5 @@ case class SyncGetPresentationPodsRespMsgBody(pods: Vector[PresentationPodVO])
|
|||||||
// ------------ akka-apps to bbb-common-web ------------
|
// ------------ akka-apps to bbb-common-web ------------
|
||||||
object PresentationUploadTokenSysPubMsg { val NAME = "PresentationUploadTokenSysPubMsg" }
|
object PresentationUploadTokenSysPubMsg { val NAME = "PresentationUploadTokenSysPubMsg" }
|
||||||
case class PresentationUploadTokenSysPubMsg(header: BbbClientMsgHeader, body: PresentationUploadTokenSysPubMsgBody) extends BbbCoreMsg
|
case class PresentationUploadTokenSysPubMsg(header: BbbClientMsgHeader, body: PresentationUploadTokenSysPubMsgBody) extends BbbCoreMsg
|
||||||
case class PresentationUploadTokenSysPubMsgBody(podId: String, authzToken: String, filename: String, meetingId: String)
|
case class PresentationUploadTokenSysPubMsgBody(podId: String, authzToken: String, filename: String, meetingId: String, presentationId: String)
|
||||||
// ------------ akka-apps to bbb-common-web ------------
|
// ------------ akka-apps to bbb-common-web ------------
|
||||||
|
@ -242,6 +242,31 @@ case class InvalidateUserGraphqlConnectionSysMsg(
|
|||||||
) extends BbbCoreMsg
|
) extends BbbCoreMsg
|
||||||
case class InvalidateUserGraphqlConnectionSysMsgBody(meetingId: String, userId: String, sessionToken: String, reason: String)
|
case class InvalidateUserGraphqlConnectionSysMsgBody(meetingId: String, userId: String, sessionToken: String, reason: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent from graphql-middleware to akka-apps
|
||||||
|
*/
|
||||||
|
|
||||||
|
object UserGraphqlConnectionInvalidatedEvtMsg { val NAME = "UserGraphqlConnectionInvalidatedEvtMsg" }
|
||||||
|
case class UserGraphqlConnectionInvalidatedEvtMsg(
|
||||||
|
header: BbbCoreBaseHeader,
|
||||||
|
body: UserGraphqlConnectionInvalidatedEvtMsgBody
|
||||||
|
) extends BbbCoreMsg
|
||||||
|
case class UserGraphqlConnectionInvalidatedEvtMsgBody(sessionToken: String, browserConnectionId: String)
|
||||||
|
|
||||||
|
object UserGraphqlConnectionStablishedSysMsg { val NAME = "UserGraphqlConnectionStablishedSysMsg" }
|
||||||
|
case class UserGraphqlConnectionStablishedSysMsg(
|
||||||
|
header: BbbCoreBaseHeader,
|
||||||
|
body: UserGraphqlConnectionStablishedSysMsgBody
|
||||||
|
) extends BbbCoreMsg
|
||||||
|
case class UserGraphqlConnectionStablishedSysMsgBody(sessionToken: String, browserConnectionId: String)
|
||||||
|
|
||||||
|
object UserGraphqlConnectionClosedSysMsg { val NAME = "UserGraphqlConnectionClosedSysMsg" }
|
||||||
|
case class UserGraphqlConnectionClosedSysMsg(
|
||||||
|
header: BbbCoreBaseHeader,
|
||||||
|
body: UserGraphqlConnectionClosedSysMsgBody
|
||||||
|
) extends BbbCoreMsg
|
||||||
|
case class UserGraphqlConnectionClosedSysMsgBody(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
|
||||||
*/
|
*/
|
||||||
|
@ -8,7 +8,7 @@ case class RegisterUserReqMsg(
|
|||||||
case class RegisterUserReqMsgBody(meetingId: String, intUserId: String, name: String, role: String,
|
case class RegisterUserReqMsgBody(meetingId: String, intUserId: String, name: String, role: String,
|
||||||
extUserId: String, authToken: String, sessionToken: String, avatarURL: String,
|
extUserId: String, authToken: String, sessionToken: String, avatarURL: String,
|
||||||
guest: Boolean, authed: Boolean, guestStatus: String, excludeFromDashboard: Boolean,
|
guest: Boolean, authed: Boolean, guestStatus: String, excludeFromDashboard: Boolean,
|
||||||
customParameters: Map[String, String])
|
enforceLayout: String, customParameters: Map[String, String])
|
||||||
|
|
||||||
object UserRegisteredRespMsg { val NAME = "UserRegisteredRespMsg" }
|
object UserRegisteredRespMsg { val NAME = "UserRegisteredRespMsg" }
|
||||||
case class UserRegisteredRespMsg(
|
case class UserRegisteredRespMsg(
|
||||||
@ -302,7 +302,7 @@ case class UserMobileFlagChangedEvtMsgBody(userId: String, mobile: Boolean)
|
|||||||
|
|
||||||
object AssignPresenterReqMsg { val NAME = "AssignPresenterReqMsg" }
|
object AssignPresenterReqMsg { val NAME = "AssignPresenterReqMsg" }
|
||||||
case class AssignPresenterReqMsg(header: BbbClientMsgHeader, body: AssignPresenterReqMsgBody) extends StandardMsg
|
case class AssignPresenterReqMsg(header: BbbClientMsgHeader, body: AssignPresenterReqMsgBody) extends StandardMsg
|
||||||
case class AssignPresenterReqMsgBody(requesterId: String, newPresenterId: String, newPresenterName: String, assignedBy: String)
|
case class AssignPresenterReqMsgBody(assignedBy: String, newPresenterId: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent from client to change the video pin of the user in the meeting.
|
* Sent from client to change the video pin of the user in the meeting.
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package org.bigbluebutton.common2.util
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
||||||
|
import com.fasterxml.jackson.module.scala.{ DefaultScalaModule, ScalaObjectMapper }
|
||||||
|
|
||||||
|
import scala.util.Try
|
||||||
|
|
||||||
|
object YamlUtil {
|
||||||
|
val mapper = new ObjectMapper(new YAMLFactory()) with ScalaObjectMapper
|
||||||
|
mapper.registerModule(DefaultScalaModule)
|
||||||
|
|
||||||
|
def mergeImmutableMaps(target: Map[String, Object], source: Map[String, Object]): Map[String, Object] = {
|
||||||
|
source.foldLeft(target) {
|
||||||
|
case (acc, (key, sourceValue)) if acc.contains(key) =>
|
||||||
|
val targetValue = acc(key)
|
||||||
|
val mergedValue = (targetValue, sourceValue) match {
|
||||||
|
case (targetMap: Map[String, Object], sourceMap: Map[String, Object]) =>
|
||||||
|
// If both source and target values are maps, recursively merge them.
|
||||||
|
mergeImmutableMaps(targetMap, sourceMap)
|
||||||
|
case _ =>
|
||||||
|
// If not, replace the target value with the source value.
|
||||||
|
sourceValue
|
||||||
|
}
|
||||||
|
acc.updated(key, mergedValue)
|
||||||
|
case (acc, (key, sourceValue)) =>
|
||||||
|
// If the key is not in the target map, add it.
|
||||||
|
acc + (key -> sourceValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def toMap[V](yaml: String)(implicit m: Manifest[V]): Try[Map[String, V]] = fromYaml[Map[String, V]](yaml)
|
||||||
|
|
||||||
|
def fromYaml[T](yaml: String)(implicit m: Manifest[T]): Try[T] = {
|
||||||
|
for {
|
||||||
|
result <- Try(mapper.readValue[T](yaml))
|
||||||
|
} yield result
|
||||||
|
}
|
||||||
|
}
|
@ -122,13 +122,19 @@ public class MeetingService implements MessageListener {
|
|||||||
public void registerUser(String meetingID, String internalUserId,
|
public void registerUser(String meetingID, String internalUserId,
|
||||||
String fullname, String role, String externUserID,
|
String fullname, String role, String externUserID,
|
||||||
String authToken, String sessionToken, String avatarURL, Boolean guest,
|
String authToken, String sessionToken, String avatarURL, Boolean guest,
|
||||||
Boolean authed, String guestStatus, Boolean excludeFromDashboard, Boolean leftGuestLobby, Map<String, String> customParameters) {
|
Boolean authed, String guestStatus, Boolean excludeFromDashboard, Boolean leftGuestLobby,
|
||||||
handle(new RegisterUser(meetingID, internalUserId, fullname, role,
|
String enforceLayout, Map<String, String> customParameters) {
|
||||||
externUserID, authToken, sessionToken, avatarURL, guest, authed, guestStatus, excludeFromDashboard, leftGuestLobby, customParameters));
|
handle(
|
||||||
|
new RegisterUser(meetingID, internalUserId, fullname, role,
|
||||||
|
externUserID, authToken, sessionToken, avatarURL, guest, authed, guestStatus,
|
||||||
|
excludeFromDashboard, leftGuestLobby, enforceLayout, customParameters
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
Meeting m = getMeeting(meetingID);
|
Meeting m = getMeeting(meetingID);
|
||||||
if (m != null) {
|
if (m != null) {
|
||||||
RegisteredUser ruser = new RegisteredUser(authToken, internalUserId, guestStatus, excludeFromDashboard, leftGuestLobby);
|
RegisteredUser ruser = new RegisteredUser(authToken, internalUserId, guestStatus,
|
||||||
|
excludeFromDashboard, leftGuestLobby, enforceLayout);
|
||||||
m.userRegistered(ruser);
|
m.userRegistered(ruser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -407,7 +413,8 @@ public class MeetingService implements MessageListener {
|
|||||||
m.getMuteOnStart(), m.getAllowModsToUnmuteUsers(), m.getAllowModsToEjectCameras(), m.getMeetingKeepEvents(),
|
m.getMuteOnStart(), m.getAllowModsToUnmuteUsers(), m.getAllowModsToEjectCameras(), m.getMeetingKeepEvents(),
|
||||||
m.breakoutRoomsParams, m.lockSettingsParams, m.getHtml5InstanceId(),
|
m.breakoutRoomsParams, m.lockSettingsParams, m.getHtml5InstanceId(),
|
||||||
m.getGroups(), m.getDisabledFeatures(), m.getNotifyRecordingIsOn(),
|
m.getGroups(), m.getDisabledFeatures(), m.getNotifyRecordingIsOn(),
|
||||||
m.getPresentationUploadExternalDescription(), m.getPresentationUploadExternalUrl());
|
m.getPresentationUploadExternalDescription(), m.getPresentationUploadExternalUrl(),
|
||||||
|
m.getOverrideClientSettings());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String formatPrettyDate(Long timestamp) {
|
private String formatPrettyDate(Long timestamp) {
|
||||||
@ -422,7 +429,7 @@ public class MeetingService implements MessageListener {
|
|||||||
gw.registerUser(message.meetingID,
|
gw.registerUser(message.meetingID,
|
||||||
message.internalUserId, message.fullname, message.role,
|
message.internalUserId, message.fullname, message.role,
|
||||||
message.externUserID, message.authToken, message.sessionToken, message.avatarURL, message.guest,
|
message.externUserID, message.authToken, message.sessionToken, message.avatarURL, message.guest,
|
||||||
message.authed, message.guestStatus, message.excludeFromDashboard, message.customParameters);
|
message.authed, message.guestStatus, message.excludeFromDashboard, message.enforceLayout, message.customParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Meeting getMeeting(String meetingId) {
|
public Meeting getMeeting(String meetingId) {
|
||||||
@ -733,7 +740,7 @@ public class MeetingService implements MessageListener {
|
|||||||
uploadAuthzTokens.put(message.authzToken, message);
|
uploadAuthzTokens.put(message.authzToken, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expirePresentationUploadToken(String usedToken) {
|
public void expirePresentationUploadToken(String usedToken) {
|
||||||
uploadAuthzTokens.remove(usedToken);
|
uploadAuthzTokens.remove(usedToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,13 +19,10 @@
|
|||||||
|
|
||||||
package org.bigbluebutton.api;
|
package org.bigbluebutton.api;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.text.DecimalFormat;
|
|
||||||
import java.text.DecimalFormatSymbols;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -37,12 +34,6 @@ import com.google.gson.JsonObject;
|
|||||||
import org.apache.commons.codec.digest.DigestUtils;
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.http.HttpEntity;
|
|
||||||
import org.apache.http.HttpResponse;
|
|
||||||
import org.apache.http.client.ClientProtocolException;
|
|
||||||
import org.apache.http.client.ResponseHandler;
|
|
||||||
import org.apache.http.client.methods.HttpGet;
|
|
||||||
import org.apache.http.util.EntityUtils;
|
|
||||||
import org.bigbluebutton.api.domain.BreakoutRoomsParams;
|
import org.bigbluebutton.api.domain.BreakoutRoomsParams;
|
||||||
import org.bigbluebutton.api.domain.LockSettingsParams;
|
import org.bigbluebutton.api.domain.LockSettingsParams;
|
||||||
import org.bigbluebutton.api.domain.Meeting;
|
import org.bigbluebutton.api.domain.Meeting;
|
||||||
@ -145,6 +136,7 @@ public class ParamsProcessorUtil {
|
|||||||
|
|
||||||
private String bbbVersion = "";
|
private String bbbVersion = "";
|
||||||
private Boolean allowRevealOfBBBVersion = false;
|
private Boolean allowRevealOfBBBVersion = false;
|
||||||
|
private Boolean allowOverrideClientSettingsOnCreateCall = false;
|
||||||
|
|
||||||
private String formatConfNum(String s) {
|
private String formatConfNum(String s) {
|
||||||
if (s.length() > 5) {
|
if (s.length() > 5) {
|
||||||
@ -912,6 +904,10 @@ public class ParamsProcessorUtil {
|
|||||||
return allowRevealOfBBBVersion;
|
return allowRevealOfBBBVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getAllowOverrideClientSettingsOnCreateCall() {
|
||||||
|
return allowOverrideClientSettingsOnCreateCall;
|
||||||
|
}
|
||||||
|
|
||||||
public String processWelcomeMessage(String message, Boolean isBreakout) {
|
public String processWelcomeMessage(String message, Boolean isBreakout) {
|
||||||
String welcomeMessage = message;
|
String welcomeMessage = message;
|
||||||
if (StringUtils.isEmpty(message)) {
|
if (StringUtils.isEmpty(message)) {
|
||||||
@ -1156,6 +1152,11 @@ public class ParamsProcessorUtil {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean parentMeetingExists(String parentMeetingId) {
|
||||||
|
Meeting meeting = ServiceUtils.findMeetingFromMeetingID(parentMeetingId);
|
||||||
|
return meeting != null;
|
||||||
|
}
|
||||||
|
|
||||||
/*************************************************
|
/*************************************************
|
||||||
* Setters
|
* Setters
|
||||||
************************************************/
|
************************************************/
|
||||||
@ -1515,4 +1516,8 @@ public class ParamsProcessorUtil {
|
|||||||
this.allowRevealOfBBBVersion = allowVersion;
|
this.allowRevealOfBBBVersion = allowVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAllowOverrideClientSettingsOnCreateCall(Boolean allowOverrideClientSettingsOnCreateCall) {
|
||||||
|
this.allowOverrideClientSettingsOnCreateCall = allowOverrideClientSettingsOnCreateCall;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,14 @@ package org.bigbluebutton.api;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
public final class Util {
|
public final class Util {
|
||||||
@ -20,6 +23,14 @@ public final class Util {
|
|||||||
throw new IllegalStateException("Utility class");
|
throw new IllegalStateException("Utility class");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String extractFilenameFromUrl(String preUploadedPresentation) throws MalformedURLException {
|
||||||
|
URL url = new URL(preUploadedPresentation);
|
||||||
|
String filename = FilenameUtils.getName(url.getPath());
|
||||||
|
String extension = FilenameUtils.getExtension(url.getPath());
|
||||||
|
if (extension == null || extension.isEmpty()) return null;
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isMeetingIdValidFormat(String id) {
|
public static boolean isMeetingIdValidFormat(String id) {
|
||||||
Matcher matcher = MEETING_ID_PATTERN.matcher(id);
|
Matcher matcher = MEETING_ID_PATTERN.matcher(id);
|
||||||
if (matcher.matches()) {
|
if (matcher.matches()) {
|
||||||
@ -51,7 +62,11 @@ public final class Util {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String createNewFilename(String presId, String fileExt) {
|
public static String createNewFilename(String presId, String fileExt) {
|
||||||
|
if (!fileExt.isEmpty()) {
|
||||||
return presId + "." + fileExt;
|
return presId + "." + fileExt;
|
||||||
|
} else {
|
||||||
|
return presId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File createPresentationDir(String meetingId, String presentationDir, String presentationId) {
|
public static File createPresentationDir(String meetingId, String presentationDir, String presentationId) {
|
||||||
|
@ -118,6 +118,8 @@ public class Meeting {
|
|||||||
|
|
||||||
private Integer html5InstanceId;
|
private Integer html5InstanceId;
|
||||||
|
|
||||||
|
private String overrideClientSettings = "";
|
||||||
|
|
||||||
public Meeting(Meeting.Builder builder) {
|
public Meeting(Meeting.Builder builder) {
|
||||||
name = builder.name;
|
name = builder.name;
|
||||||
extMeetingId = builder.externalId;
|
extMeetingId = builder.externalId;
|
||||||
@ -1131,4 +1133,12 @@ public class Meeting {
|
|||||||
return new Meeting(this);
|
return new Meeting(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getOverrideClientSettings() {
|
||||||
|
return overrideClientSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOverrideClientSettings(String overrideClientConfigs) {
|
||||||
|
this.overrideClientSettings = overrideClientConfigs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,16 @@ public class RegisteredUser {
|
|||||||
private Boolean excludeFromDashboard;
|
private Boolean excludeFromDashboard;
|
||||||
private Long guestWaitedOn;
|
private Long guestWaitedOn;
|
||||||
private Boolean leftGuestLobby;
|
private Boolean leftGuestLobby;
|
||||||
|
private String enforceLayout;
|
||||||
|
|
||||||
public RegisteredUser(String authToken, String userId, String guestStatus, Boolean excludeFromDashboard, Boolean leftGuestLobby) {
|
public RegisteredUser(String authToken, String userId, String guestStatus, Boolean excludeFromDashboard,
|
||||||
|
Boolean leftGuestLobby, String enforceLayout) {
|
||||||
this.authToken = authToken;
|
this.authToken = authToken;
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
this.guestStatus = guestStatus;
|
this.guestStatus = guestStatus;
|
||||||
this.excludeFromDashboard = excludeFromDashboard;
|
this.excludeFromDashboard = excludeFromDashboard;
|
||||||
this.leftGuestLobby = leftGuestLobby;
|
this.leftGuestLobby = leftGuestLobby;
|
||||||
|
this.enforceLayout = enforceLayout;
|
||||||
|
|
||||||
Long currentTimeMillis = System.currentTimeMillis();
|
Long currentTimeMillis = System.currentTimeMillis();
|
||||||
this.registeredOn = currentTimeMillis;
|
this.registeredOn = currentTimeMillis;
|
||||||
@ -30,6 +33,10 @@ public class RegisteredUser {
|
|||||||
return guestStatus;
|
return guestStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLeftGuestLobby(boolean bool) {
|
||||||
|
this.leftGuestLobby = bool;
|
||||||
|
}
|
||||||
|
|
||||||
public Boolean getLeftGuestLobby() {
|
public Boolean getLeftGuestLobby() {
|
||||||
return leftGuestLobby;
|
return leftGuestLobby;
|
||||||
}
|
}
|
||||||
@ -38,6 +45,14 @@ public class RegisteredUser {
|
|||||||
this.excludeFromDashboard = excludeFromDashboard;
|
this.excludeFromDashboard = excludeFromDashboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getEnforceLayout() {
|
||||||
|
return enforceLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnforceLayout(String enforceLayout) {
|
||||||
|
this.enforceLayout = enforceLayout;
|
||||||
|
}
|
||||||
|
|
||||||
public Boolean getExcludeFromDashboard() {
|
public Boolean getExcludeFromDashboard() {
|
||||||
return excludeFromDashboard;
|
return excludeFromDashboard;
|
||||||
}
|
}
|
||||||
@ -46,9 +61,6 @@ public class RegisteredUser {
|
|||||||
this.guestWaitedOn = System.currentTimeMillis();
|
this.guestWaitedOn = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLeftGuestLobby(boolean bool) {
|
|
||||||
this.leftGuestLobby = bool;
|
|
||||||
}
|
|
||||||
public Long getGuestWaitedOn() {
|
public Long getGuestWaitedOn() {
|
||||||
return this.guestWaitedOn;
|
return this.guestWaitedOn;
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ public class UserSession {
|
|||||||
public String welcome = null;
|
public String welcome = null;
|
||||||
public String logoutUrl = null;
|
public String logoutUrl = null;
|
||||||
public String defaultLayout = "NOLAYOUT";
|
public String defaultLayout = "NOLAYOUT";
|
||||||
|
public String enforceLayout = "";
|
||||||
public String avatarURL;
|
public String avatarURL;
|
||||||
public String guestStatus = GuestPolicy.ALLOW;
|
public String guestStatus = GuestPolicy.ALLOW;
|
||||||
public String clientUrl = null;
|
public String clientUrl = null;
|
||||||
@ -130,6 +131,10 @@ public class UserSession {
|
|||||||
return defaultLayout;
|
return defaultLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getEnforceLayout() {
|
||||||
|
return enforceLayout;
|
||||||
|
}
|
||||||
|
|
||||||
public String getAvatarURL() {
|
public String getAvatarURL() {
|
||||||
return avatarURL;
|
return avatarURL;
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,15 @@ package org.bigbluebutton.api.messaging.messages;
|
|||||||
|
|
||||||
public class PresentationUploadToken implements IMessage {
|
public class PresentationUploadToken implements IMessage {
|
||||||
public final String podId;
|
public final String podId;
|
||||||
|
public final String presentationId;
|
||||||
public final String authzToken;
|
public final String authzToken;
|
||||||
public final String filename;
|
public final String filename;
|
||||||
public final String meetingId;
|
public final String meetingId;
|
||||||
|
|
||||||
public PresentationUploadToken(String podId, String authzToken, String filename, String meetingId) {
|
public PresentationUploadToken(String podId, String authzToken, String filename, String meetingId, String presentationId) {
|
||||||
this.podId = podId;
|
this.podId = podId;
|
||||||
this.authzToken = authzToken;
|
this.authzToken = authzToken;
|
||||||
|
this.presentationId = presentationId;
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
this.meetingId = meetingId;
|
this.meetingId = meetingId;
|
||||||
}
|
}
|
||||||
|
@ -18,12 +18,13 @@ public class RegisterUser implements IMessage {
|
|||||||
public final String guestStatus;
|
public final String guestStatus;
|
||||||
public final Boolean excludeFromDashboard;
|
public final Boolean excludeFromDashboard;
|
||||||
public final Boolean leftGuestLobby;
|
public final Boolean leftGuestLobby;
|
||||||
|
public final String enforceLayout;
|
||||||
public final Map<String, String> customParameters;
|
public final Map<String, String> customParameters;
|
||||||
|
|
||||||
public RegisterUser(String meetingID, String internalUserId, String fullname, String role, String externUserID,
|
public RegisterUser(String meetingID, String internalUserId, String fullname, String role, String externUserID,
|
||||||
String authToken, String sessionToken, String avatarURL, Boolean guest,
|
String authToken, String sessionToken, String avatarURL, Boolean guest,
|
||||||
Boolean authed, String guestStatus, Boolean excludeFromDashboard, Boolean leftGuestLobby,
|
Boolean authed, String guestStatus, Boolean excludeFromDashboard, Boolean leftGuestLobby,
|
||||||
Map<String, String> customParameters) {
|
String enforceLayout, Map<String, String> customParameters) {
|
||||||
this.meetingID = meetingID;
|
this.meetingID = meetingID;
|
||||||
this.internalUserId = internalUserId;
|
this.internalUserId = internalUserId;
|
||||||
this.fullname = fullname;
|
this.fullname = fullname;
|
||||||
@ -37,6 +38,7 @@ public class RegisterUser implements IMessage {
|
|||||||
this.guestStatus = guestStatus;
|
this.guestStatus = guestStatus;
|
||||||
this.excludeFromDashboard = excludeFromDashboard;
|
this.excludeFromDashboard = excludeFromDashboard;
|
||||||
this.leftGuestLobby = leftGuestLobby;
|
this.leftGuestLobby = leftGuestLobby;
|
||||||
|
this.enforceLayout = enforceLayout;
|
||||||
this.customParameters = customParameters;
|
this.customParameters = customParameters;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user