Merge branch 'develop' into patch-7

This commit is contained in:
Anton Georgiev 2024-05-10 22:03:20 -04:00 committed by GitHub
commit 81bedbff8f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1916 changed files with 45663 additions and 43317 deletions

View File

@ -6,7 +6,7 @@ runs:
using: "composite"
steps:
- name: Checkout ${{ github.event.pull_request.base.ref || 'master' }}
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.ref || '' }}
fetch-depth: 0 # Fetch all history

View File

@ -48,7 +48,7 @@ updates:
# upcoming release branch
## excluding bigbluebutton-tests/playwright, bigbluebutton-tests/puppeteer, docs, bbb-graphql-client-test
- package-ecosystem: npm
directory: "/bbb-graphql-actions-adapter-server"
directory: "/bbb-graphql-actions"
target-branch: "v3.0.x-release"
schedule:
interval: daily

View File

@ -54,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
- package: bbb-graphql-server
build-name: bbb-graphql-server
build-list: bbb-graphql-server bbb-graphql-middleware bbb-graphql-actions-adapter-server
build-list: bbb-graphql-server bbb-graphql-middleware bbb-graphql-actions
- package: bbb-etherpad
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
@ -75,7 +75,7 @@ jobs:
- package: others
build-list: bbb-mkclean bbb-pads bbb-libreoffice-docker bbb-transcription-controller bigbluebutton bbb-livekit
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Merge branches
uses: ./.github/actions/merge-branches
- name: Set cache-key vars
@ -90,7 +90,7 @@ jobs:
- name: Handle cache
if: matrix.cache-files-list != ''
id: cache-action
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: artifacts.tar
key: ${{ runner.os }}-${{ matrix.package }}-${{ env.BIGBLUEBUTTON_RELEASE }}-commits-${{ env.CACHE_KEY_FILES }}-urls-${{ env.CACHE_KEY_URLS }}
@ -102,7 +102,7 @@ jobs:
echo "${{ matrix.build-list || matrix.package }}" | xargs -n 1 ./build/setup.sh
tar cvf artifacts.tar artifacts/
- name: Archive packages
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: artifacts_${{ matrix.package }}.tar
path: artifacts.tar
@ -112,74 +112,76 @@ jobs:
strategy:
fail-fast: false
matrix:
shard: [1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8, 8/8]
shard: [1, 2, 3, 4, 5, 6, 7, 8]
env:
shard: ${{ matrix.shard }}/8
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Merge branches
uses: ./.github/actions/merge-branches
- run: ./build/get_external_dependencies.sh
- name: Download artifacts_bbb-apps-akka
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: artifacts_bbb-apps-akka.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-config
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: artifacts_bbb-config.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-export-annotations
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: artifacts_bbb-export-annotations.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-learning-dashboard
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: artifacts_bbb-learning-dashboard.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-playback-record
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: artifacts_bbb-playback-record.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-graphql-server
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: artifacts_bbb-graphql-server.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-etherpad
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: artifacts_bbb-etherpad.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-freeswitch
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: artifacts_bbb-freeswitch.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-webrtc
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: artifacts_bbb-webrtc.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-web
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: artifacts_bbb-web.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-fsesl-akka
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: artifacts_bbb-fsesl-akka.tar
- run: tar xf artifacts.tar
- name: Download artifacts_bbb-html5
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: artifacts_bbb-html5.tar
- run: tar xf artifacts.tar
- name: Download artifacts
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: artifacts_others.tar
- run: tar xf artifacts.tar
@ -253,17 +255,22 @@ jobs:
apt --purge -y remove apache2-bin
'
- name: Install BBB
timeout-minutes: 25
run: |
sudo -i <<EOF
set -e
cd /root/ && wget -nv https://raw.githubusercontent.com/bigbluebutton/bbb-install/v3.0.x-release/bbb-install.sh -O bbb-install.sh
cat bbb-install.sh | sed "s|> /etc/apt/sources.list.d/bigbluebutton.list||g" | bash -s -- -v jammy-30-dev -s bbb-ci.test -j -d /certs/
bbb-conf --salt bbbci
echo "NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt" >> /usr/share/meteor/bundle/bbb-html5-with-roles.conf
sed -i "s/\"minify\": true,/\"minify\": false,/" /usr/share/etherpad-lite/settings.json
bbb-conf --restart
EOF
uses: nick-fields/retry@v3
env:
NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt
ACTIONS_RUNNER_DEBUG: true
with:
timeout_minutes: 25
max_attempts: 2
command: |
sudo -i <<EOF
set -e
cd /root/ && wget -nv https://raw.githubusercontent.com/bigbluebutton/bbb-install/v3.0.x-release/bbb-install.sh -O bbb-install.sh
cat bbb-install.sh | sed "s|> /etc/apt/sources.list.d/bigbluebutton.list||g" | bash -s -- -v jammy-30-dev -s bbb-ci.test -j -d /certs/
bbb-conf --salt bbbci
sed -i "s/\"minify\": true,/\"minify\": false,/" /usr/share/etherpad-lite/settings.json
bbb-conf --restart
EOF
- name: List systemctl services
timeout-minutes: 1
run: |
@ -279,13 +286,13 @@ jobs:
npx playwright install
'
- name: Run tests
uses: nick-fields/retry@v2
uses: nick-fields/retry@v3
with:
timeout_minutes: 25
max_attempts: 3
max_attempts: 2
command: |
cd ./bigbluebutton-tests/playwright
npm run test-chromium-ci -- --shard ${{ matrix.shard }}
npm run test-chromium-ci -- --shard ${{ env.shard }}
env:
NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt
ACTIONS_RUNNER_DEBUG: true
@ -305,13 +312,13 @@ jobs:
run: |
sh -c '
find $HOME/.cache/ms-playwright -name libnssckbi.so -exec rm {} \; -exec ln -s /usr/lib/x86_64-linux-gnu/pkcs11/p11-kit-trust.so {} \;
npm run test-firefox-ci -- --shard ${{ matrix.shard }}
npm run test-firefox-ci -- --shard ${{ env.shard }}
'
- if: always() && github.event_name == 'pull_request'
name: Upload blob report to GitHub Actions Artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: all-blob-reports
name: blob-report-${{ matrix.shard }}
path: bigbluebutton-tests/playwright/blob-report
- if: failure()
name: Prepare artifacts (configs and logs)
@ -337,42 +344,56 @@ jobs:
chmod a+r -R /home/runner/work/bigbluebutton/bigbluebutton/configs
bbb-conf --zip
ls -t /root/*.tar.gz | head -1 | xargs -I '{}' cp '{}' /home/runner/work/bigbluebutton/bigbluebutton/bbb-logs.tar.gz
echo "MATRIX_SHARD=$(echo ${{ matrix.shard }} | tr '/' '_')" >> $GITHUB_ENV
echo "MATRIX_SHARD=${{ matrix.shard }}_8" >> $GITHUB_ENV
EOF
- if: failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: bbb-configs-${{ env.MATRIX_SHARD }}
path: configs
- if: failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: bbb-logs-${{ env.MATRIX_SHARD }}
path: ./bbb-logs.tar.gz
upload-report:
if: always()
if: always() && !contains(github.event.head_commit.message, 'Merge pull request')
needs: install-and-run-tests
runs-on: ubuntu-latest
env:
hasReportData: ${{ needs.install-and-run-tests.result == 'success' || needs.install-and-run-tests.result == 'failure' }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- name: Install dependencies
if: ${{ env.hasReportData }}
working-directory: ./bigbluebutton-tests/playwright
run: npm ci
- name: Download all blob reports from GitHub Actions Artifacts
uses: actions/download-artifact@v3
- name: Merge artifacts
uses: actions/upload-artifact/merge@v4
with:
name: all-blob-reports
pattern: blob-report-*
delete-merged: true
- name: Download all blob reports from GitHub Actions Artifacts
if: ${{ env.hasReportData }}
uses: actions/download-artifact@v4
with:
pattern: all-blob-reports-*
path: bigbluebutton-tests/playwright/all-blob-reports
merge-multiple: true
- name: Merge into HTML Report
if: ${{ env.hasReportData }}
working-directory: ./bigbluebutton-tests/playwright
run: npx playwright merge-reports --reporter html ./all-blob-reports
- name: Upload HTML tests report
uses: actions/upload-artifact@v3
if: ${{ env.hasReportData }}
uses: actions/upload-artifact@v4
with:
name: tests-report
overwrite: true
path: |
bigbluebutton-tests/playwright/playwright-report
bigbluebutton-tests/playwright/test-results
@ -385,7 +406,7 @@ jobs:
echo ${{ github.run_id }} > ./pr-comment-data/workflow_id
- name: Upload PR data for auto-comment
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: pr-comment-data
path: pr-comment-data

View File

@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check for dirty pull requests
uses: eps1lon/actions-label-merge-conflict@releases/2.x
uses: eps1lon/actions-label-merge-conflict@v3
with:
dirtyLabel: "status: conflict"
repoToken: "${{ secrets.GITHUB_TOKEN }}"

View File

@ -22,8 +22,8 @@ jobs:
working-directory: ./docs
steps:
# Setup
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

View File

@ -23,7 +23,7 @@ jobs:
ts-code-compilation:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Merge branches

View File

@ -23,7 +23,7 @@ jobs:
ts-code-validation:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Merge branches

2
.gitignore vendored
View File

@ -23,3 +23,5 @@ cache/*
artifacts/*
bbb-presentation-video.zip
bbb-presentation-video
bbb-graphql-actions-adapter-server/
bigbluebutton-html5/public/locales/index.json

View File

@ -17,7 +17,7 @@ As such, we recommend that all administrators deploy 2.7 going forward. You'll
## Reporting a Vulnerability
If you believe you have found a security vunerability in BigBlueButton please let us know directly by
If you believe you have found a security vulnerability in BigBlueButton please let us know directly by
- using GitHub's "Report a vulnerability" functionality on https://github.com/bigbluebutton/bigbluebutton/security/advisories
- or e-mailing security@bigbluebutton.org with as much detail as possible.

View File

@ -75,5 +75,6 @@ daemonUser in Linux := user
daemonGroup in Linux := group
javaOptions in Universal ++= Seq("-J-Xms130m", "-J-Xmx256m", "-Dconfig.file=/etc/bigbluebutton/bbb-apps-akka.conf", "-Dlogback.configurationFile=conf/logback.xml")
javaOptions in reStart ++= Seq("-Dconfig.file=/etc/bigbluebutton/bbb-apps-akka.conf", "-Dlogback.configurationFile=conf/logback.xml")
debianPackageDependencies in Debian ++= Seq("java17-runtime-headless", "bash")

View File

@ -16,7 +16,7 @@ object Dependencies {
val pekkoHttpVersion = "1.0.0"
val gson = "2.8.9"
val jackson = "2.13.5"
val logback = "1.2.11"
val logback = "1.2.13"
val quicklens = "1.7.5"
val spray = "1.3.6"
@ -68,7 +68,6 @@ object Dependencies {
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 {

View File

@ -10,7 +10,7 @@ import org.bigbluebutton.core.bus._
import org.bigbluebutton.core.pubsub.senders.ReceivedJsonMsgHandlerActor
import org.bigbluebutton.core2.AnalyticsActor
import org.bigbluebutton.core2.FromAkkaAppsMsgSenderActor
import org.bigbluebutton.endpoint.redis.{AppsRedisSubscriberActor, ExportAnnotationsActor, GraphqlActionsActor, LearningDashboardActor, RedisRecorderActor}
import org.bigbluebutton.endpoint.redis.{AppsRedisSubscriberActor, ExportAnnotationsActor, GraphqlConnectionsActor, LearningDashboardActor, RedisRecorderActor}
import org.bigbluebutton.common2.bus.IncomingJsonMessageBus
import org.bigbluebutton.service.{HealthzService, MeetingInfoActor, MeetingInfoService}
@ -67,9 +67,9 @@ object Boot extends App with SystemConfiguration {
"LearningDashboardActor"
)
val graphqlActionsActor = system.actorOf(
GraphqlActionsActor.props(system),
"GraphqlActionsActor"
val graphqlConnectionsActor = system.actorOf(
GraphqlConnectionsActor.props(system, eventBus, outGW),
"GraphqlConnectionsActor"
)
ClientSettings.loadClientSettingsFromFile()
@ -89,8 +89,8 @@ object Boot extends App with SystemConfiguration {
outBus2.subscribe(learningDashboardActor, outBbbMsgMsgChannel)
bbbMsgBus.subscribe(learningDashboardActor, analyticsChannel)
eventBus.subscribe(graphqlActionsActor, meetingManagerChannel)
bbbMsgBus.subscribe(graphqlActionsActor, analyticsChannel)
eventBus.subscribe(graphqlConnectionsActor, meetingManagerChannel)
bbbMsgBus.subscribe(graphqlConnectionsActor, analyticsChannel)
val bbbActor = system.actorOf(BigBlueButtonActor.props(system, eventBus, bbbMsgBus, outGW, healthzService), "bigbluebutton-actor")
eventBus.subscribe(bbbActor, meetingManagerChannel)

View File

@ -5,7 +5,7 @@ import org.slf4j.LoggerFactory
import java.io.{ ByteArrayInputStream, File }
import scala.io.BufferedSource
import scala.util.{ Failure, Success, Try }
import scala.util.{ Failure, Success }
object ClientSettings extends SystemConfiguration {
var clientSettingsFromFile: Map[String, Object] = Map("" -> "")
@ -38,6 +38,9 @@ object ClientSettings extends SystemConfiguration {
Map[String, Object]()
}
)
//Remove `:private` once it's used only by Meteor internal configs
clientSettingsFromFile -= "private"
}
def getClientSettingsWithOverride(clientSettingsOverrideJson: String): Map[String, Object] = {
@ -52,6 +55,51 @@ object ClientSettings extends SystemConfiguration {
} else clientSettingsFromFile
}
def getConfigPropertyValueByPathAsIntOrElse(map: Map[String, Any], path: String, alternativeValue: Int): Int = {
getConfigPropertyValueByPath(map, path) match {
case Some(configValue: Int) => configValue
case _ =>
logger.debug(s"Config `$path` with type Integer not found in clientSettings.")
alternativeValue
}
}
def getConfigPropertyValueByPathAsStringOrElse(map: Map[String, Any], path: String, alternativeValue: String): String = {
getConfigPropertyValueByPath(map, path) match {
case Some(configValue: String) => configValue
case _ =>
logger.debug(s"Config `$path` with type String not found in clientSettings.")
alternativeValue
}
}
def getConfigPropertyValueByPathAsBooleanOrElse(map: Map[String, Any], path: String, alternativeValue: Boolean): Boolean = {
getConfigPropertyValueByPath(map, path) match {
case Some(configValue: Boolean) => configValue
case _ =>
logger.debug(s"Config `$path` with type Boolean found in clientSettings.")
alternativeValue
}
}
def getConfigPropertyValueByPathAsListOfStringOrElse(map: Map[String, Any], path: String, alternativeValue: List[String]): List[String] = {
getConfigPropertyValueByPath(map, path) match {
case Some(configValue: List[String]) => configValue
case _ =>
logger.debug(s"Config `$path` with type List[String] not found in clientSettings.")
alternativeValue
}
}
def getConfigPropertyValueByPathAsListOfIntOrElse(map: Map[String, Any], path: String, alternativeValue: List[Int]): List[Int] = {
getConfigPropertyValueByPath(map, path) match {
case Some(configValue: List[Int]) => configValue
case _ =>
logger.debug(s"Config `$path` with type List[Int] not found in clientSettings.")
alternativeValue
}
}
def getConfigPropertyValueByPath(map: Map[String, Any], path: String): Option[Any] = {
val keys = path.split("\\.")
@ -90,13 +138,37 @@ object ClientSettings extends SystemConfiguration {
for {
dataChannel <- dataChannels
} yield {
if (dataChannel.contains("name") && dataChannel.contains("writePermission")) {
if (dataChannel.contains("name")) {
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")
val writePermission = {
if (dataChannel.contains("writePermission")) {
dataChannel("writePermission") match {
case wPerm: List[String] => wPerm
case _ => {
logger.warn(s"Invalid writePermission for channel $channelName in plugin $pluginName")
List()
}
}
} else {
logger.warn(s"Missing config writePermission for channel $channelName in plugin $pluginName")
List()
}
}
val deletePermission = {
if (dataChannel.contains("deletePermission")) {
dataChannel("deletePermission") match {
case dPerm: List[String] => dPerm
case _ => {
logger.warn(s"Invalid deletePermission for channel $channelName in plugin $pluginName")
List()
}
}
} else {
List()
}
}
pluginDataChannels += (channelName -> DataChannel(channelName, writePermission, deletePermission))
}
}
case _ => logger.warn(s"Plugin $pluginName has an invalid dataChannels format")
@ -112,7 +184,7 @@ object ClientSettings extends SystemConfiguration {
pluginsFromConfig
}
case class DataChannel(name: String, writePermission: List[String])
case class DataChannel(name: String, writePermission: List[String], deletePermission: List[String])
case class Plugin(name: String, url: String, dataChannels: Map[String, DataChannel])
}

View File

@ -13,6 +13,7 @@ trait SystemConfiguration {
lazy val bbbWebPort = Try(config.getInt("services.bbbWebPort")).getOrElse(8888)
lazy val bbbWebAPI = Try(config.getString("services.bbbWebAPI")).getOrElse("localhost")
lazy val bbbWebSharedSecret = Try(config.getString("services.sharedSecret")).getOrElse("changeme")
lazy val checkSumAlgorithmForBreakouts = Try(config.getString("services.checkSumAlgorithmForBreakouts")).getOrElse("sha256")
lazy val bbbWebModeratorPassword = Try(config.getString("services.moderatorPassword")).getOrElse("changeme")
lazy val bbbWebViewerPassword = Try(config.getString("services.viewerPassword")).getOrElse("changeme")
lazy val keysExpiresInSec = Try(config.getInt("redis.keyExpiry")).getOrElse(14 * 86400) // 14 days

View File

@ -14,6 +14,7 @@ import org.bigbluebutton.SystemConfiguration
import java.util.concurrent.TimeUnit
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.db.{ DatabaseConnection, MeetingDAO }
import org.bigbluebutton.core.domain.MeetingEndReason
import org.bigbluebutton.core.running.RunningMeeting
import org.bigbluebutton.core.util.ColorPicker
import org.bigbluebutton.core2.RunningMeetings
@ -57,6 +58,9 @@ class BigBlueButtonActor(
override def preStart() {
bbbMsgBus.subscribe(self, meetingManagerChannel)
DatabaseConnection.initialize()
//Terminate all previous meetings, as they will not function following the akka-apps restart
MeetingDAO.setAllMeetingsEnded(MeetingEndReason.ENDED_DUE_TO_SERVICE_INTERRUPTION, "system")
}
override def postStop() {
@ -75,15 +79,16 @@ class BigBlueButtonActor(
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
msg.core match {
case m: CreateMeetingReqMsg => handleCreateMeetingReqMsg(m)
case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m)
case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m)
case m: GetRunningMeetingsReqMsg => handleGetRunningMeetingsReqMsg(m)
case m: CheckAlivePingSysMsg => handleCheckAlivePingSysMsg(m)
case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m)
case _: UserGraphqlConnectionStablishedSysMsg => //Ignore
case _: UserGraphqlConnectionClosedSysMsg => //Ignore
case _ => log.warning("Cannot handle " + msg.envelope.name)
case m: CreateMeetingReqMsg => handleCreateMeetingReqMsg(m)
case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m)
case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m)
case m: GetRunningMeetingsReqMsg => handleGetRunningMeetingsReqMsg(m)
case m: CheckAlivePingSysMsg => handleCheckAlivePingSysMsg(m)
case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m)
case _: UserGraphqlConnectionEstablishedSysMsg => //Ignore
case _: UserGraphqlConnectionClosedSysMsg => //Ignore
case _: CheckGraphqlMiddlewareAlivePongSysMsg => //Ignore
case _ => log.warning("Cannot handle " + msg.envelope.name)
}
}
@ -149,7 +154,7 @@ class BigBlueButtonActor(
}
private def handleGetAllMeetingsReqMsg(msg: GetAllMeetingsReqMsg): Unit = {
RunningMeetings.meetings(meetings).filter(_.props.systemProps.html5InstanceId == msg.body.html5InstanceId).foreach(m => {
RunningMeetings.meetings(meetings).foreach(m => {
m.actorRef ! msg
})
}
@ -189,9 +194,10 @@ class BigBlueButtonActor(
context.stop(m.actorRef)
}
MeetingDAO.delete(msg.meetingId)
// MeetingDAO.delete(msg.meetingId)
// MeetingDAO.setMeetingEnded(msg.meetingId)
// Removing the meeting is enough, all other tables has "ON DELETE CASCADE"
// UserDAO.deleteAllFromMeeting(msg.meetingId)
// UserDAO.softDeleteAllFromMeeting(msg.meetingId)
// MeetingRecordingDAO.updateStopped(msg.meetingId, "")
//Remove ColorPicker idx of the meeting

View File

@ -1,5 +1,6 @@
package org.bigbluebutton.core.api
import org.bigbluebutton.core.apps.users.UserEstablishedGraphqlConnectionInternalMsgHdlr
import org.bigbluebutton.core.domain.{ BreakoutUser, BreakoutVoiceUser }
import spray.json.JsObject
case class InMessageHeader(name: String)
@ -120,15 +121,13 @@ case class CapturePresentationReqInternalMsg(userId: String, parentMeetingId: St
case class SetPresenterInDefaultPodInternalMsg(presenterId: String) extends InMessage
/**
* Sent by breakout room to parent meeting to obtain padId
* @param breakoutId
* @param filename
* Sent by GraphqlActionsActor to inform MeetingActor that user disconnected
* @param userId
*/
case class CaptureSharedNotesReqInternalMsg(breakoutId: String, filename: String) extends InMessage
case class UserClosedAllGraphqlConnectionsInternalMsg(userId: String) extends InMessage
// DeskShare
case class DeskShareStartedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
case class DeskShareStoppedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
case class DeskShareRTMPBroadcastStartedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) extends InMessage
case class DeskShareRTMPBroadcastStoppedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) extends InMessage
case class DeskShareGetDeskShareInfoRequest(conferenceName: String, requesterID: String, replyTo: String) extends InMessage
/**
* Sent by GraphqlActionsActor to inform MeetingActor that user came back from disconnection
* @param userId
*/
case class UserEstablishedGraphqlConnectionInternalMsg(userId: String) extends InMessage

View File

@ -95,7 +95,7 @@ object PermissionCheck extends SystemConfiguration {
for {
regUser <- RegisteredUsers.findWithUserId(userId, liveMeeting.registeredUsers)
} yield {
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, reason, outGW)
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, reason, outGW)
}
} else {
// TODO: get this object a context so it can use the akka logging system

View File

@ -45,6 +45,14 @@ object TimerModel {
}
def setRunning(model: TimerModel, running: Boolean): Unit = {
//If it is running and will stop, calculate new Accumulated
if(getRunning(model) && !running) {
val now = System.currentTimeMillis()
val accumulated = getAccumulated(model) + Math.abs(now - getStartedAt(model)).toInt
this.setAccumulated(model, accumulated)
}
model.running = running
}

View File

@ -46,73 +46,112 @@ class WhiteboardModel extends SystemConfiguration {
k -> newValue
}).toMap
def addAnnotations(wbId: String, userId: String, annotations: Array[AnnotationVO], isPresenter: Boolean, isModerator: Boolean): Array[AnnotationVO] = {
var annotationsAdded = Array[AnnotationVO]()
def addAnnotations(wbId: String, meetingId: String, userId: String, annotations: Array[AnnotationVO], isPresenter: Boolean, isModerator: Boolean): Array[AnnotationVO] = {
val wb = getWhiteboard(wbId)
var annotationsAdded = Array[AnnotationVO]()
var newAnnotationsMap = wb.annotationsMap
for (annotation <- annotations) {
val oldAnnotation = wb.annotationsMap.get(annotation.id)
if (!oldAnnotation.isEmpty) {
val hasPermission = isPresenter || isModerator || oldAnnotation.get.userId == userId
if (hasPermission) {
val newAnnotation = oldAnnotation.get.copy(annotationInfo = deepMerge(oldAnnotation.get.annotationInfo, annotation.annotationInfo))
// Merge old and new annotation properties
val mergedAnnotationInfo = deepMerge(oldAnnotation.get.annotationInfo, annotation.annotationInfo)
// Apply cleaning if it's an arrow annotation
val finalAnnotationInfo = if (annotation.annotationInfo.get("type").contains("arrow")) {
cleanArrowAnnotationProps(mergedAnnotationInfo)
} else {
mergedAnnotationInfo
}
val newAnnotation = oldAnnotation.get.copy(annotationInfo = finalAnnotationInfo)
newAnnotationsMap += (annotation.id -> newAnnotation)
annotationsAdded :+= annotation
PresAnnotationDAO.insertOrUpdate(newAnnotation, annotation)
println(s"Updated annotation onpage [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
annotationsAdded :+= newAnnotation
println(s"Updated annotation on page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
} else {
println(s"User $userId doesn't have permission to edit annotation ${annotation.id}, ignoring...")
}
} else if (annotation.annotationInfo.contains("type")) {
newAnnotationsMap += (annotation.id -> annotation)
annotationsAdded :+= annotation
PresAnnotationDAO.insertOrUpdate(annotation, annotation)
println(s"Adding annotation to page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
} else {
println(s"New annotation [${annotation.id}] with no type, ignoring (probably received a remove message before and now the shape is incomplete, ignoring...")
println(s"New annotation [${annotation.id}] with no type, ignoring...")
}
}
PresAnnotationDAO.insertOrUpdateMap(meetingId, annotationsAdded)
val newWb = wb.copy(annotationsMap = newAnnotationsMap)
saveWhiteboard(newWb)
annotationsAdded
}
private def cleanArrowAnnotationProps(annotationInfo: Map[String, _]): Map[String, _] = {
annotationInfo.get("props") match {
case Some(props: Map[String, _]) =>
val cleanedProps = props.map {
case ("end", endProps: Map[String, _]) => "end" -> cleanEndOrStartProps(endProps)
case ("start", startProps: Map[String, _]) => "start" -> cleanEndOrStartProps(startProps)
case other => other
}
annotationInfo + ("props" -> cleanedProps)
case _ => annotationInfo
}
}
private def cleanEndOrStartProps(props: Map[String, _]): Map[String, _] = {
props.get("type") match {
case Some("binding") => props - ("x", "y") // Remove 'x' and 'y' for 'binding' type
case Some("point") => props - ("boundShapeId", "normalizedAnchor", "isExact", "isPrecise") // Remove unwanted properties for 'point' type
case _ => props
}
}
def getHistory(wbId: String): Array[AnnotationVO] = {
val wb = getWhiteboard(wbId)
wb.annotationsMap.values.toArray
}
def deleteAnnotations(wbId: String, userId: String, annotationsIds: Array[String], isPresenter: Boolean, isModerator: Boolean): Array[String] = {
var annotationsIdsRemoved = Array[String]()
def deleteAnnotations(wbId: String, meetingId: String, userId: String, annotationsIds: Array[String], isPresenter: Boolean, isModerator: Boolean): Array[String] = {
val wb = getWhiteboard(wbId)
var annotationsIdsRemoved = Array[String]()
var newAnnotationsMap = wb.annotationsMap
for (annotationId <- annotationsIds) {
val annotation = wb.annotationsMap.get(annotationId)
if (!annotation.isEmpty) {
if (annotation.isDefined) {
val hasPermission = isPresenter || isModerator || annotation.get.userId == userId
if (hasPermission) {
newAnnotationsMap -= annotationId
println("Removing annotation on page [" + wb.id + "]. After numAnnotations=[" + newAnnotationsMap.size + "].")
println(s"Removed annotation $annotationId on page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
annotationsIdsRemoved :+= annotationId
} else {
println("User doesn't have permission to remove this annotation, ignoring...")
println(s"User $userId doesn't have permission to remove annotation $annotationId, ignoring...")
}
} else {
println(s"Annotation $annotationId not found while trying to delete it.")
}
}
val newWb = wb.copy(annotationsMap = newAnnotationsMap)
saveWhiteboard(newWb)
annotationsIdsRemoved.map(PresAnnotationDAO.delete(wbId, userId, _))
// Update whiteboard and save
val updatedWb = wb.copy(annotationsMap = newAnnotationsMap)
saveWhiteboard(updatedWb)
PresAnnotationDAO.delete(meetingId, userId, annotationsIdsRemoved)
annotationsIdsRemoved
}
def modifyWhiteboardAccess(wbId: String, multiUser: Array[String]) {
def modifyWhiteboardAccess(meetingId: String, wbId: String, multiUser: Array[String]) {
val wb = getWhiteboard(wbId)
val newWb = wb.copy(multiUser = multiUser, oldMultiUser = wb.multiUser, changedModeOn = System.currentTimeMillis())
PresPageWritersDAO.updateMultiuser(newWb)
PresPageWritersDAO.updateMultiuser(meetingId, newWb)
saveWhiteboard(newWb)
}

View File

@ -4,6 +4,7 @@ import org.apache.pekko.actor.ActorContext
class AudioCaptionsApp2x(implicit val context: ActorContext)
extends UpdateTranscriptPubMsgHdlr
with TranscriptionProviderErrorMsgHdlr
with AudioFloorChangedVoiceConfEvtMsgHdlr {
}

View File

@ -0,0 +1,31 @@
package org.bigbluebutton.core.apps.audiocaptions
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.db.UserTranscriptionErrorDAO
import org.bigbluebutton.core.models.AudioCaptions
import org.bigbluebutton.core.running.LiveMeeting
trait TranscriptionProviderErrorMsgHdlr {
this: AudioCaptionsApp2x =>
def handleTranscriptionProviderErrorMsg(msg: TranscriptionProviderErrorMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
val meetingId = liveMeeting.props.meetingProp.intId
def broadcastEvent(userId: String, errorCode: String, errorMessage: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, "nodeJSapp")
val envelope = BbbCoreEnvelope(TranscriptionProviderErrorEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(TranscriptionProviderErrorEvtMsg.NAME, meetingId, userId)
val body = TranscriptionProviderErrorEvtMsgBody(errorCode, errorMessage)
val event = TranscriptionProviderErrorEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
broadcastEvent(msg.header.userId, msg.body.errorCode, msg.body.errorMessage)
UserTranscriptionErrorDAO.insert(msg.header.userId, msg.header.meetingId, msg.body.errorCode, msg.body.errorMessage)
}
}

View File

@ -1,12 +1,13 @@
package org.bigbluebutton.core.apps.audiocaptions
import org.bigbluebutton.ClientSettings.getConfigPropertyValueByPathAsStringOrElse
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.db.CaptionDAO
import org.bigbluebutton.core.models.{AudioCaptions, Users2x}
import org.bigbluebutton.core.models.{AudioCaptions, UserState, Users2x}
import org.bigbluebutton.core.running.LiveMeeting
import java.sql.Timestamp
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
trait UpdateTranscriptPubMsgHdlr {
this: AudioCaptionsApp2x =>
@ -25,6 +26,17 @@ trait UpdateTranscriptPubMsgHdlr {
bus.outGW.send(msgEvent)
}
def sendPadUpdatePubMsg(userId: String, defaultPad: String, text: String, transcript: Boolean): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, "nodeJSapp")
val envelope = BbbCoreEnvelope(PadUpdatePubMsg.NAME, routing)
val header = BbbClientMsgHeader(PadUpdatePubMsg.NAME, meetingId, userId)
val body = PadUpdatePubMsgBody(defaultPad, text, transcript)
val event = PadUpdatePubMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
// Adapt to the current captions' recording process
def editTranscript(
userId: String,
@ -80,6 +92,28 @@ trait UpdateTranscriptPubMsgHdlr {
msg.body.locale,
msg.body.result,
)
if(msg.body.result) {
val userName = Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId).get match {
case u: UserState => u.name
case _ => "???"
}
val now = LocalDateTime.now()
val formatter = DateTimeFormatter.ofPattern("HH:mm:ss")
val formattedTime = now.format(formatter)
val userSpoke = s"\n $userName ($formattedTime): $transcript"
val defaultPad = getConfigPropertyValueByPathAsStringOrElse(
liveMeeting.clientSettings,
"public.captions.defaultPad",
alternativeValue = ""
)
sendPadUpdatePubMsg(msg.header.userId, defaultPad, userSpoke, transcript = true)
}
}
}
}

View File

@ -4,6 +4,7 @@ import org.bigbluebutton.core.running.MeetingActor
import java.net.URLEncoder
import scala.collection.SortedSet
import org.apache.commons.codec.digest.DigestUtils
import org.bigbluebutton.SystemConfiguration
trait BreakoutApp2x extends BreakoutRoomCreatedMsgHdlr
with BreakoutRoomsListMsgHdlr
@ -26,7 +27,7 @@ trait BreakoutApp2x extends BreakoutRoomCreatedMsgHdlr
}
object BreakoutRoomsUtil {
object BreakoutRoomsUtil extends SystemConfiguration {
def createMeetingIds(id: String, index: Int): (String, String) = {
val timeStamp = System.currentTimeMillis()
val externalHash = DigestUtils.sha1Hex(id.concat("-").concat(timeStamp.toString()).concat("-").concat(index.toString()))
@ -48,7 +49,13 @@ object BreakoutRoomsUtil {
//checksum() -- Return a checksum based on SHA-1 digest
//
def checksum(s: String): String = {
DigestUtils.sha256Hex(s);
checkSumAlgorithmForBreakouts match {
case "sha1" => DigestUtils.sha1Hex(s);
case "sha256" => DigestUtils.sha256Hex(s);
case "sha384" => DigestUtils.sha384Hex(s);
case "sha512" => DigestUtils.sha512Hex(s);
case _ => DigestUtils.sha256Hex(s); // default
}
}
def calculateChecksum(apiCall: String, baseString: String, sharedSecret: String): String = {

View File

@ -1,7 +1,7 @@
package org.bigbluebutton.core.apps.breakout
import org.bigbluebutton.core.api.BreakoutRoomEndedInternalMsg
import org.bigbluebutton.core.db.BreakoutRoomDAO
import org.bigbluebutton.core.db.{ BreakoutRoomDAO, NotificationDAO }
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core2.message.senders.MsgBuilder
@ -37,6 +37,7 @@ trait BreakoutRoomEndedInternalMsgHdlr {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
BreakoutRoomDAO.updateRoomsEnded(liveMeeting.props.meetingProp.intId)
state.update(None)

View File

@ -53,8 +53,8 @@ trait BreakoutRoomUsersUpdateMsgHdlr {
breakoutRoomUser <- updatedRoom.users
u <- RegisteredUsers.findWithBreakoutRoomId(breakoutRoomUser.id, liveMeeting.registeredUsers)
} yield u.id
UserBreakoutRoomDAO.updateLastBreakoutRoom(usersInRoom, updatedRoom)
BreakoutRoomUserDAO.updateUserJoined(usersInRoom, updatedRoom)
UserBreakoutRoomDAO.updateLastBreakoutRoom(props.meetingProp.intId, usersInRoom, updatedRoom)
BreakoutRoomUserDAO.updateUserJoined(props.meetingProp.intId, usersInRoom, updatedRoom)
model.update(updatedRoom)
}

View File

@ -5,7 +5,7 @@ import org.bigbluebutton.core.api.EjectUserFromBreakoutInternalMsg
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers.getRedirectUrls
import org.bigbluebutton.core.apps.{PermissionCheck, RightsManagementTrait}
import org.bigbluebutton.core.bus.BigBlueButtonEvent
import org.bigbluebutton.core.db.BreakoutRoomUserDAO
import org.bigbluebutton.core.db.{BreakoutRoomUserDAO, NotificationDAO}
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.EjectReasonCode
import org.bigbluebutton.core.running.{MeetingActor, OutMsgRouter}
@ -57,7 +57,7 @@ trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
)
//Update database
BreakoutRoomUserDAO.updateRoomChanged(msg.body.userId, msg.body.fromBreakoutId, msg.body.toBreakoutId, redirectToHtml5JoinURL)
BreakoutRoomUserDAO.updateRoomChanged(meetingId, msg.body.userId, msg.body.fromBreakoutId, msg.body.toBreakoutId, redirectToHtml5JoinURL)
//Send notification to moved User
@ -75,6 +75,7 @@ trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
Vector(roomTo.shortName)
)
outGW.send(notifyUserEvent)
NotificationDAO.insert(notifyUserEvent)
}
}

View File

@ -1,5 +1,6 @@
package org.bigbluebutton.core.apps.breakout
import org.bigbluebutton.ClientSettings.{getConfigPropertyValueByPath, getConfigPropertyValueByPathAsIntOrElse}
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.{BreakoutModel, PermissionCheck, RightsManagementTrait}
import org.bigbluebutton.core.db.BreakoutRoomDAO
@ -16,6 +17,10 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
def handleCreateBreakoutRoomsCmdMsg(msg: CreateBreakoutRoomsCmdMsg, state: MeetingState2x): MeetingState2x = {
val minOfRooms = 2
val maxOfRooms = getConfigPropertyValueByPathAsIntOrElse(liveMeeting.clientSettings, "public.app.breakouts.breakoutRoomLimit", 16)
if (liveMeeting.props.meetingProp.disabledFeatures.contains("breakoutRooms")) {
val meetingId = liveMeeting.props.meetingProp.intId
val reason = "Breakout rooms is disabled for this meeting."
@ -27,6 +32,15 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId,
reason, outGW, liveMeeting)
state
} else if(msg.body.rooms.length > maxOfRooms || msg.body.rooms.length < minOfRooms) {
log.warning(
"Attempt to create breakout rooms with invalid number of rooms (rooms: {}, max: {}, min: {}) in meeting {}",
msg.body.rooms.size,
maxOfRooms,
minOfRooms,
liveMeeting.props.meetingProp.intId
)
state
} else {
state.breakout match {
case Some(breakout) =>
@ -54,8 +68,8 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
val voiceConf = BreakoutRoomsUtil.createVoiceConfId(liveMeeting.props.voiceProp.voiceConf, i)
val breakout = BreakoutModel.create(parentId, internalId, externalId, room.name, room.sequence, room.shortName,
room.isDefaultName, room.freeJoin, voiceConf, room.users, msg.body.captureNotes,
msg.body.captureSlides, room.captureNotesFilename, room.captureSlidesFilename)
room.isDefaultName, room.freeJoin, voiceConf, room.users, msg.body.captureNotes,
msg.body.captureSlides, room.captureNotesFilename, room.captureSlidesFilename)
rooms = rooms + (breakout.id -> breakout)
}

View File

@ -30,13 +30,13 @@ trait EjectUserFromBreakoutInternalMsgHdlr {
)
//TODO inform reason
UserDAO.delete(registeredUser.id)
UserDAO.softDelete(registeredUser.meetingId, registeredUser.id)
// send a system message to force disconnection
Sender.sendDisconnectClientSysMsg(msg.breakoutId, registeredUser.id, msg.ejectedBy, msg.reasonCode, outGW)
// Force reconnection with graphql to refresh permissions
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, registeredUser.id, registeredUser.sessionToken, msg.reasonCode, outGW)
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, registeredUser.id, registeredUser.sessionToken, msg.reasonCode, outGW)
//send users update to parent meeting
BreakoutHdlrHelpers.updateParentMeetingWithUsers(liveMeeting, eventBus)

View File

@ -6,9 +6,8 @@ import org.bigbluebutton.core.bus.BigBlueButtonEvent
import org.bigbluebutton.core.domain.{ MeetingEndReason, MeetingState2x }
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.db.UserBreakoutRoomDAO
import org.bigbluebutton.core.db.{ BreakoutRoomDAO, NotificationDAO, UserBreakoutRoomDAO }
import org.bigbluebutton.core2.message.senders.MsgBuilder
import org.bigbluebutton.core.db.BreakoutRoomDAO
trait EndAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
this: MeetingActor =>
@ -22,24 +21,19 @@ trait EndAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
state
} else {
for {
model <- state.breakout
} yield {
model.rooms.values.foreach { room =>
eventBus.publish(BigBlueButtonEvent(room.id, EndBreakoutRoomInternalMsg(meetingId, room.id, MeetingEndReason.BREAKOUT_ENDED_BY_MOD)))
UserBreakoutRoomDAO.updateLastBreakoutRoom(Vector(), room)
}
endAllBreakoutRooms(eventBus, liveMeeting, state, MeetingEndReason.BREAKOUT_ENDED_BY_MOD)
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
meetingId,
"info",
"rooms",
"app.toast.breakoutRoomEnded",
"Message when the breakout room is ended",
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
meetingId,
"info",
"rooms",
"app.toast.breakoutRoomEnded",
"Message when the breakout room is ended",
Vector()
)
outGW.send(notifyEvent)
}
BreakoutRoomDAO.updateRoomsEnded(meetingId)
state.update(None)
}

View File

@ -1,7 +1,10 @@
package org.bigbluebutton.core.apps.breakout
import org.bigbluebutton.core.api.{ CaptureSharedNotesReqInternalMsg, CapturePresentationReqInternalMsg, EndBreakoutRoomInternalMsg }
import org.bigbluebutton.common2.msgs.{ BbbClientMsgHeader, BbbCommonEnvCoreMsg, BbbCoreEnvelope, BbbCoreHeaderWithMeetingId, ExportJob, MessageTypes, PresentationConversionUpdateEvtMsg, PresentationConversionUpdateEvtMsgBody, PresentationConversionUpdateSysPubMsg, PresentationUploadTokenSysPubMsg, PresentationUploadTokenSysPubMsgBody, Routing, StoreExportJobInRedisSysMsg, StoreExportJobInRedisSysMsgBody }
import org.bigbluebutton.core.api.{ CapturePresentationReqInternalMsg, EndBreakoutRoomInternalMsg }
import org.bigbluebutton.core.apps.presentationpod.PresentationPodsApp
import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, InternalEventBus }
import org.bigbluebutton.core.models.Pads
import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting, OutMsgRouter }
trait EndBreakoutRoomInternalMsgHdlr extends HandlerHelpers {
@ -19,12 +22,68 @@ trait EndBreakoutRoomInternalMsgHdlr extends HandlerHelpers {
}
if (liveMeeting.props.breakoutProps.captureNotes) {
val filename = liveMeeting.props.breakoutProps.captureNotesFilename
val captureNotesEvent = BigBlueButtonEvent(msg.parentId, CaptureSharedNotesReqInternalMsg(msg.breakoutId, filename))
eventBus.publish(captureNotesEvent)
for {
group <- Pads.getGroup(liveMeeting.pads, "notes")
} yield {
val filename = liveMeeting.props.breakoutProps.captureNotesFilename
val userId: String = "system"
val jobId: String = s"${msg.breakoutId}-notes" // Used as the temporaryPresentationId upon upload
val presentationId = PresentationPodsApp.generatePresentationId(filename)
if (group.rev > 0) {
//Request upload of the sharedNotes of breakoutRoom
val presentationUploadToken: String = PresentationPodsApp.generateToken("DEFAULT_PRESENTATION_POD", userId)
outGW.send(buildPresentationUploadTokenSysPubMsg(msg.parentId, userId, presentationUploadToken, filename, presentationId))
val exportJob = ExportJob(jobId, "PadCaptureJob", filename, filename, group.padId, "", allPages = true, List(), msg.parentId, presentationUploadToken)
val job = buildStoreExportJobInRedisSysMsg(exportJob, liveMeeting)
outGW.send(job)
} else {
// Notify that no content is available
val event = buildPresentationConversionUpdateEvtMsg(msg.parentId, presentationId, filename, jobId)
outGW.send(event)
}
}
}
log.info("Breakout room {} ended by parent meeting {}.", msg.breakoutId, msg.parentId)
sendEndMeetingDueToExpiry(msg.reason, eventBus, outGW, liveMeeting, "system")
}
def buildStoreExportJobInRedisSysMsg(exportJob: ExportJob, liveMeeting: LiveMeeting): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(StoreExportJobInRedisSysMsg.NAME, routing)
val body = StoreExportJobInRedisSysMsgBody(exportJob)
val header = BbbCoreHeaderWithMeetingId(StoreExportJobInRedisSysMsg.NAME, liveMeeting.props.meetingProp.intId)
val event = StoreExportJobInRedisSysMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildPresentationUploadTokenSysPubMsg(parentMeetingId: String, userId: String, presentationUploadToken: String, filename: String, presId: String): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(PresentationUploadTokenSysPubMsg.NAME, routing)
val header = BbbClientMsgHeader(PresentationUploadTokenSysPubMsg.NAME, parentMeetingId, userId)
val body = PresentationUploadTokenSysPubMsgBody("DEFAULT_PRESENTATION_POD", presentationUploadToken, filename, parentMeetingId, presId)
val event = PresentationUploadTokenSysPubMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildPresentationConversionUpdateEvtMsg(meetingId: String, presentationId: String, presName: String, temporaryPresentationId: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "system")
val envelope = BbbCoreEnvelope(PresentationConversionUpdateEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(PresentationConversionUpdateEvtMsg.NAME, meetingId, "system")
val body = PresentationConversionUpdateEvtMsgBody(
"DEFAULT_PRESENTATION_POD",
"204",
"not-used",
presentationId,
presName,
temporaryPresentationId
)
val event = PresentationConversionUpdateEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
}

View File

@ -1,13 +1,14 @@
package org.bigbluebutton.core.apps.breakout
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.api.{ SendMessageToBreakoutRoomInternalMsg }
import org.bigbluebutton.core.api.SendMessageToBreakoutRoomInternalMsg
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.bus.BigBlueButtonEvent
import org.bigbluebutton.core.db.NotificationDAO
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.{ RegisteredUsers }
import org.bigbluebutton.core.models.RegisteredUsers
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
this: MeetingActor =>
@ -32,7 +33,7 @@ trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
val event = buildSendMessageToAllBreakoutRoomsEvtMsg(msg.header.userId, msg.body.msg, breakoutModel.rooms.size)
outGW.send(event)
val notifyModeratorEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
msg.header.userId,
liveMeeting.props.meetingProp.intId,
"info",
@ -41,7 +42,8 @@ trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
"Message for chat sent successfully",
Vector(s"${breakoutModel.rooms.size}")
)
outGW.send(notifyModeratorEvent)
outGW.send(notifyUserEvent)
NotificationDAO.insert(notifyUserEvent)
log.debug("Sending message '{}' to all breakout rooms in meeting {}", msg.body.msg, props.meetingProp.intId)
}

View File

@ -18,8 +18,8 @@ trait SendMessageToBreakoutRoomInternalMsgHdlr {
sender <- GroupChatApp.findGroupChatUser(SystemUser.ID, liveMeeting.users2x)
chat <- state.groupChats.find(GroupChatApp.MAIN_PUBLIC_CHAT)
} yield {
val groupChatMsgFromUser = GroupChatMsgFromUser(sender.id, sender.copy(name = msg.senderName), true, msg.msg)
val gcm = GroupChatApp.toGroupChatMessage(sender.copy(name = msg.senderName), groupChatMsgFromUser)
val groupChatMsgFromUser = GroupChatMsgFromUser(sender.id, sender.copy(name = msg.senderName), msg.msg)
val gcm = GroupChatApp.toGroupChatMessage(sender.copy(name = msg.senderName), groupChatMsgFromUser, emphasizedText = true)
val gcs = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm, GroupChatMessageType.BREAKOUTROOM_MOD_MSG)
val event = buildGroupChatMessageBroadcastEvtMsg(

View File

@ -2,6 +2,7 @@ package org.bigbluebutton.core.apps.breakout
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.{ BreakoutModel, PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.db.UserDAO
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.VoiceUsers
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
@ -17,13 +18,13 @@ trait TransferUserToMeetingRequestHdlr extends RightsManagementTrait {
val reason = "No permission to transfer user to voice breakout."
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
} else {
processRequest(msg)
processTransferUserToMeetingRequest(msg)
}
state
}
def processRequest(msg: TransferUserToMeetingRequestMsg) {
def processTransferUserToMeetingRequest(msg: TransferUserToMeetingRequestMsg) {
if (msg.body.fromMeetingId == liveMeeting.props.meetingProp.intId) {
// want to transfer from parent meeting to breakout
for {
@ -32,6 +33,7 @@ trait TransferUserToMeetingRequestHdlr extends RightsManagementTrait {
from <- getVoiceConf(msg.body.fromMeetingId, model)
voiceUser <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId)
} yield {
UserDAO.transferUserToBreakoutRoomAsAudioOnly(msg.body.userId, msg.body.fromMeetingId, msg.body.toMeetingId)
val event = buildTransferUserToVoiceConfSysMsg(from, to, voiceUser.voiceUserId)
outGW.send(event)
}
@ -53,6 +55,7 @@ trait TransferUserToMeetingRequestHdlr extends RightsManagementTrait {
room <- model.find(msg.body.fromMeetingId)
voiceUser <- room.voiceUsers.find(p => p.id == msg.body.userId)
} yield {
UserDAO.transferUserToBreakoutRoomAsAudioOnly(msg.body.userId, msg.body.fromMeetingId, msg.body.toMeetingId)
val event = buildTransferUserToVoiceConfSysMsg(from, to, voiceUser.voiceUserId)
outGW.send(event)
}

View File

@ -4,7 +4,7 @@ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.api.{ SendTimeRemainingAuditInternalMsg, UpdateBreakoutRoomTimeInternalMsg }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.bus.BigBlueButtonEvent
import org.bigbluebutton.core.db.{ BreakoutRoomDAO, MeetingDAO }
import org.bigbluebutton.core.db.{ BreakoutRoomDAO, MeetingDAO, NotificationDAO }
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
@ -63,9 +63,10 @@ trait UpdateBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
Vector(s"${msg.body.timeInMinutes}")
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
val notifyModeratorEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
msg.header.userId,
liveMeeting.props.meetingProp.intId,
"info",
@ -74,7 +75,8 @@ trait UpdateBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
"Sent to the moderator that requested breakout duration change",
Vector(s"${msg.body.timeInMinutes}")
)
outGW.send(notifyModeratorEvent)
outGW.send(notifyUserEvent)
NotificationDAO.insert(notifyUserEvent)
log.debug("Updating {} minutes for breakout rooms time in meeting {}", msg.body.timeInMinutes, props.meetingProp.intId)
BreakoutRoomDAO.updateRoomsDuration(props.meetingProp.intId, newDurationInSeconds)

View File

@ -4,8 +4,9 @@ import org.apache.pekko.actor.ActorContext
import org.apache.pekko.event.Logging
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.running.{ LiveMeeting }
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.db.{ CaptionLocaleDAO, CaptionTypes }
class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementTrait {
val log = Logging(context.system, getClass)
@ -84,6 +85,7 @@ class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementT
val event = UpdateCaptionOwnerEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
CaptionLocaleDAO.insertOrUpdateCaptionLocale(liveMeeting.props.meetingProp.intId, locale, CaptionTypes.TYPED, newOwnerId)
}
if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) {

View File

@ -59,7 +59,7 @@ trait CreateGroupChatReqMsgHdlr extends SystemConfiguration {
val newState = for {
createdBy <- GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x)
} yield {
val msgs = msg.body.msg.map(m => GroupChatApp.toGroupChatMessage(createdBy, m))
val msgs = msg.body.msg.map(m => GroupChatApp.toGroupChatMessage(createdBy, m, emphasizedText = false))
val users = {
if (msg.body.access == GroupChatAccess.PRIVATE) {
val cu = msg.body.users.toSet + msg.header.userId

View File

@ -20,10 +20,10 @@ object GroupChatApp {
GroupChatFactory.create(gcId, access, createBy, users, msgs)
}
def toGroupChatMessage(sender: GroupChatUser, msg: GroupChatMsgFromUser): GroupChatMessage = {
def toGroupChatMessage(sender: GroupChatUser, msg: GroupChatMsgFromUser, emphasizedText: Boolean): GroupChatMessage = {
val now = System.currentTimeMillis()
val id = GroupChatFactory.genId()
GroupChatMessage(id, now, msg.correlationId, now, now, sender, msg.chatEmphasizedText, msg.message)
GroupChatMessage(id, now, msg.correlationId, now, now, sender, emphasizedText, msg.message)
}
def toMessageToUser(msg: GroupChatMessage): GroupChatMsgToUser = {
@ -80,8 +80,8 @@ object GroupChatApp {
sender <- GroupChatApp.findGroupChatUser(userId, liveMeeting.users2x)
chat <- state.groupChats.find(chatId)
} yield {
val gcm1 = GroupChatApp.toGroupChatMessage(sender, msg)
val emphasizedText = sender.role == Roles.MODERATOR_ROLE
val gcm1 = GroupChatApp.toGroupChatMessage(sender, msg, emphasizedText)
val gcs1 = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm1)
state.update(gcs1)
}

View File

@ -1,5 +1,6 @@
package org.bigbluebutton.core.apps.groupchats
import org.bigbluebutton.ClientSettings.{ getConfigPropertyValueByPath, getConfigPropertyValueByPathAsBooleanOrElse }
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.PermissionCheck
import org.bigbluebutton.core.bus.MessageBus
@ -48,7 +49,17 @@ trait SendGroupChatMessageMsgHdlr extends HandlerHelpers {
val userIsAParticipant = chat.users.filter(u => u.id == sender.id).length > 0;
if ((chatIsPrivate && userIsAParticipant) || !chatIsPrivate) {
val gcm = GroupChatApp.toGroupChatMessage(sender, msg.body.msg)
val moderatorChatEmphasizedEnabled = getConfigPropertyValueByPathAsBooleanOrElse(
liveMeeting.clientSettings,
"public.chat.moderatorChatEmphasized",
alternativeValue = true
)
val emphasizedText = moderatorChatEmphasizedEnabled &&
!chatIsPrivate &&
sender.role == Roles.MODERATOR_ROLE
val gcm = GroupChatApp.toGroupChatMessage(sender, msg.body.msg, emphasizedText)
val gcs = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm)
val event = buildGroupChatMessageBroadcastEvtMsg(

View File

@ -11,7 +11,7 @@ trait SyncGetGroupChatsInfoMsgHdlr {
def handleSyncGetGroupChatsInfo(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
def buildSyncGetGroupChatsRespMsg(allChats: Vector[GroupChatInfo]): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetGroupChatsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetGroupChatsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val body = SyncGetGroupChatsRespMsgBody(allChats)
@ -21,7 +21,7 @@ trait SyncGetGroupChatsInfoMsgHdlr {
}
def buildSyncGetGroupChatMsgsRespMsg(msgs: Vector[GroupChatMsgToUser], chatId: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetGroupChatMsgsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetGroupChatMsgsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val body = SyncGetGroupChatMsgsRespMsgBody(chatId, msgs)

View File

@ -5,7 +5,8 @@ import org.bigbluebutton.core.models.{ Layouts, LayoutsType }
import org.bigbluebutton.core.running.OutMsgRouter
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.db.LayoutDAO
import org.bigbluebutton.core.db.{ LayoutDAO, NotificationDAO }
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
this: LayoutApp2x =>
@ -60,5 +61,19 @@ trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
if (body.pushLayout) {
val notifyEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
fromUserId,
liveMeeting.props.meetingProp.intId,
"info",
"user",
"app.layoutUpdate.label",
"Notification to when the presenter changes size of cams",
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
}
}

View File

@ -9,7 +9,7 @@ trait SyncGetMeetingInfoRespMsgHdlr {
val outGW: OutMsgRouter
def handleSyncGetMeetingInfoRespMsg(props: DefaultProps): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(props.meetingProp.intId, props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetMeetingInfoRespMsg.NAME, routing)
val header = BbbCoreBaseHeader(SyncGetMeetingInfoRespMsg.NAME)

View File

@ -10,17 +10,6 @@ trait PadCreateGroupReqMsgHdlr {
def handle(msg: PadCreateGroupReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(externalId: String, model: String): Unit = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(PadCreateGroupCmdMsg.NAME, routing)
val header = BbbCoreHeaderWithMeetingId(PadCreateGroupCmdMsg.NAME, liveMeeting.props.meetingProp.intId)
val body = PadCreateGroupCmdMsgBody(externalId, model)
val event = PadCreateGroupCmdMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
val padEnabled = msg.body.model match {
case "notes" => !liveMeeting.props.meetingProp.disabledFeatures.contains("sharedNotes")
case "captions" => !liveMeeting.props.meetingProp.disabledFeatures.contains("captions")
@ -29,7 +18,7 @@ trait PadCreateGroupReqMsgHdlr {
if (padEnabled && !Pads.hasGroup(liveMeeting.pads, msg.body.externalId)) {
Pads.addGroup(liveMeeting.pads, msg.body.externalId, msg.body.model, msg.body.name, msg.header.userId)
broadcastEvent(msg.body.externalId, msg.body.model)
PadslHdlrHelpers.broadcastPadCreateGroupCmdMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.body.externalId, msg.body.model)
}
}
}

View File

@ -24,6 +24,7 @@ trait PadCreatedEvtMsgHdlr {
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
case Some(group) => {
Pads.setPadId(liveMeeting.pads, group.externalId, msg.body.padId)
SharedNotesDAO.insert(liveMeeting.props.meetingProp.intId, group, msg.body.padId, msg.body.name)
broadcastEvent(group.externalId, group.userId, msg.body.padId, msg.body.name)
}

View File

@ -9,22 +9,12 @@ trait PadGroupCreatedEvtMsgHdlr {
this: PadsApp2x =>
def handle(msg: PadGroupCreatedEvtMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(externalId: String, model: String, name: String, userId: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, userId)
val envelope = BbbCoreEnvelope(PadGroupCreatedRespMsg.NAME, routing)
val header = BbbClientMsgHeader(PadGroupCreatedRespMsg.NAME, liveMeeting.props.meetingProp.intId, userId)
val body = PadGroupCreatedRespMsgBody(externalId, model, name)
val event = PadGroupCreatedRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
Pads.getGroup(liveMeeting.pads, msg.body.externalId) match {
case Some(group) => {
Pads.setGroupId(liveMeeting.pads, msg.body.externalId, msg.body.groupId)
broadcastEvent(msg.body.externalId, group.model, group.name, group.userId)
//Group was created, now request to create the pad
PadslHdlrHelpers.broadcastPadCreateCmdMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.body.groupId, msg.body.externalId)
}
case _ =>
}

View File

@ -24,7 +24,7 @@ trait PadSessionDeletedSysMsgHdlr {
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
case Some(group) => {
SharedNotesSessionDAO.delete(msg.body.userId, msg.body.sessionId)
SharedNotesSessionDAO.delete(liveMeeting.props.meetingProp.intId, msg.body.userId, msg.body.sessionId)
broadcastEvent(group.externalId, msg.body.userId, msg.body.sessionId)
}
case _ =>

View File

@ -21,7 +21,7 @@ trait PadUpdatePubMsgHdlr {
bus.outGW.send(msgEvent)
}
if (Pads.hasAccess(liveMeeting, msg.body.externalId, msg.header.userId)) {
if (Pads.hasAccess(liveMeeting, msg.body.externalId, msg.header.userId) || msg.body.transcript == true) {
Pads.getGroup(liveMeeting.pads, msg.body.externalId) match {
case Some(group) => broadcastEvent(group.groupId, msg.body.externalId, msg.body.text)
case _ =>

View File

@ -24,6 +24,7 @@ trait PadUpdatedSysMsgHdlr {
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
case Some(group) => {
Pads.setRev(liveMeeting.pads, group.externalId, msg.body.rev)
SharedNotesRevDAO.insert(liveMeeting.props.meetingProp.intId, group.externalId, msg.body.rev, msg.body.userId, msg.body.changeset)
broadcastEvent(group.externalId, msg.body.padId, msg.body.userId, msg.body.rev, msg.body.changeset)
}

View File

@ -0,0 +1,30 @@
package org.bigbluebutton.core.apps.pads
import org.bigbluebutton.common2.msgs.{ BbbCommonEnvCoreMsg, BbbCoreEnvelope, BbbCoreHeaderWithMeetingId, PadCreateCmdMsg, PadCreateCmdMsgBody, PadCreateGroupCmdMsg, PadCreateGroupCmdMsgBody }
import org.bigbluebutton.core.running.OutMsgRouter
object PadslHdlrHelpers {
def broadcastPadCreateGroupCmdMsg(outGW: OutMsgRouter, meetingId: String, externalId: String, model: String): Unit = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(PadCreateGroupCmdMsg.NAME, routing)
val header = BbbCoreHeaderWithMeetingId(PadCreateGroupCmdMsg.NAME, meetingId)
val body = PadCreateGroupCmdMsgBody(externalId, model)
val event = PadCreateGroupCmdMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
def broadcastPadCreateCmdMsg(outGW: OutMsgRouter, meetingId: String, groupId: String, name: String): Unit = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(PadCreateCmdMsg.NAME, routing)
val header = BbbCoreHeaderWithMeetingId(PadCreateCmdMsg.NAME, meetingId)
val body = PadCreateCmdMsgBody(groupId, name)
val event = PadCreateCmdMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
}

View File

@ -0,0 +1,62 @@
package org.bigbluebutton.core.apps.plugin
import org.bigbluebutton.ClientSettings
import org.bigbluebutton.common2.msgs.PluginDataChannelDeleteEntryMsg
import org.bigbluebutton.core.db.PluginDataChannelEntryDAO
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.{ Roles, Users2x }
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
trait PluginDataChannelDeleteEntryMsgHdlr extends HandlerHelpers {
def handle(msg: PluginDataChannelDeleteEntryMsg, 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.channelName)) {
println(s"Data channel '${msg.body.channelName}' not found in plugin '${msg.body.pluginName}'.")
} else {
val hasPermission = for {
deletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).deletePermission
} yield {
deletePermission.toLowerCase match {
case "all" => true
case "moderator" => user.role == Roles.MODERATOR_ROLE
case "presenter" => user.presenter
case "sender" => {
val senderUserId = PluginDataChannelEntryDAO.getMessageSender(
meetingId,
msg.body.pluginName,
msg.body.channelName,
msg.body.subChannelName,
msg.body.entryId
)
senderUserId == msg.header.userId
}
case _ => false
}
}
if (!hasPermission.contains(true)) {
println(s"No permission to delete in plugin: '${msg.body.pluginName}', data channel: '${msg.body.channelName}'.")
} else {
PluginDataChannelEntryDAO.delete(
meetingId,
msg.body.pluginName,
msg.body.channelName,
msg.body.subChannelName,
msg.body.entryId
)
}
}
}
}
}

View File

@ -1,15 +1,15 @@
package org.bigbluebutton.core.apps.plugin
import org.bigbluebutton.common2.msgs.PluginDataChannelPushEntryMsg
import org.bigbluebutton.ClientSettings
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.db.{ PluginDataChannelMessageDAO }
import org.bigbluebutton.core.db.PluginDataChannelEntryDAO
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.{ Roles, Users2x }
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
trait DispatchPluginDataChannelMessageMsgHdlr extends HandlerHelpers {
trait PluginDataChannelPushEntryMsgHdlr extends HandlerHelpers {
def handle(msg: DispatchPluginDataChannelMessageMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = {
def handle(msg: PluginDataChannelPushEntryMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = {
val pluginsDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("plugins")
val meetingId = liveMeeting.props.meetingProp.intId
@ -21,11 +21,11 @@ trait DispatchPluginDataChannelMessageMsgHdlr extends HandlerHelpers {
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 if (!pluginsConfig(msg.body.pluginName).dataChannels.contains(msg.body.channelName)) {
println(s"Data channel '${msg.body.channelName}' not found in plugin '${msg.body.pluginName}'.")
} else {
val hasPermission = for {
writePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.dataChannel).writePermission
writePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).writePermission
} yield {
writePermission.toLowerCase match {
case "all" => true
@ -36,12 +36,13 @@ trait DispatchPluginDataChannelMessageMsgHdlr extends HandlerHelpers {
}
if (!hasPermission.contains(true)) {
println(s"No permission to write in plugin: '${msg.body.pluginName}', data channel: '${msg.body.dataChannel}'.")
println(s"No permission to write in plugin: '${msg.body.pluginName}', data channel: '${msg.body.channelName}'.")
} else {
PluginDataChannelMessageDAO.insert(
PluginDataChannelEntryDAO.insert(
meetingId,
msg.body.pluginName,
msg.body.dataChannel,
msg.body.channelName,
msg.body.subChannelName,
msg.header.userId,
msg.body.payloadJson,
msg.body.toRoles,

View File

@ -0,0 +1,51 @@
package org.bigbluebutton.core.apps.plugin
import org.bigbluebutton.ClientSettings
import org.bigbluebutton.common2.msgs.PluginDataChannelResetMsg
import org.bigbluebutton.core.db.PluginDataChannelEntryDAO
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.{ Roles, Users2x }
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
trait PluginDataChannelResetMsgHdlr extends HandlerHelpers {
def handle(msg: PluginDataChannelResetMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = {
val pluginsDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("plugins")
val meetingId = liveMeeting.props.meetingProp.intId
for {
_ <- if (!pluginsDisabled) Some(()) else None
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
} yield {
val pluginsConfig = ClientSettings.getPluginsFromConfig(ClientSettings.clientSettingsFromFile)
if (!pluginsConfig.contains(msg.body.pluginName)) {
println(s"Plugin '${msg.body.pluginName}' not found.")
} else if (!pluginsConfig(msg.body.pluginName).dataChannels.contains(msg.body.channelName)) {
println(s"Data channel '${msg.body.channelName}' not found in plugin '${msg.body.pluginName}'.")
} else {
val hasPermission = for {
deletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).deletePermission
} yield {
deletePermission.toLowerCase match {
case "all" => true
case "moderator" => user.role == Roles.MODERATOR_ROLE
case "presenter" => user.presenter
case _ => false
}
}
if (!hasPermission.contains(true)) {
println(s"No permission to delete (reset) in plugin: '${msg.body.pluginName}', data channel: '${msg.body.channelName}'.")
} else {
PluginDataChannelEntryDAO.reset(
meetingId,
msg.body.pluginName,
msg.body.channelName,
msg.body.subChannelName
)
}
}
}
}
}

View File

@ -2,9 +2,12 @@ package org.bigbluebutton.core.apps.plugin
import org.apache.pekko.actor.ActorContext
import org.apache.pekko.event.Logging
import org.bigbluebutton.common2.msgs.PluginDataChannelDeleteEntryMsgBody
class PluginHdlrs(implicit val context: ActorContext)
extends DispatchPluginDataChannelMessageMsgHdlr {
extends PluginDataChannelPushEntryMsgHdlr
with PluginDataChannelDeleteEntryMsgHdlr
with PluginDataChannelResetMsgHdlr {
val log = Logging(context.system, getClass)
}

View File

@ -0,0 +1,56 @@
package org.bigbluebutton.core.apps.polls
import org.bigbluebutton.common2.domain.SimplePollResultOutVO
import org.bigbluebutton.common2.msgs.{ BbbClientMsgHeader, BbbCommonEnvCoreMsg, BbbCoreEnvelope, MessageTypes, PollUpdatedEvtMsg, PollUpdatedEvtMsgBody, Routing, UserRespondedToPollRecordMsg, UserRespondedToPollRecordMsgBody, UserRespondedToPollRespMsg, UserRespondedToPollRespMsgBody, UserRespondedToTypedPollRespMsg, UserRespondedToTypedPollRespMsgBody }
import org.bigbluebutton.core.running.OutMsgRouter
object PollHdlrHelpers {
def broadcastPollUpdatedEvent(outGW: OutMsgRouter, meetingId: String, userId: String, pollId: String, poll: SimplePollResultOutVO): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(PollUpdatedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(PollUpdatedEvtMsg.NAME, meetingId, userId)
val body = PollUpdatedEvtMsgBody(pollId, poll)
val event = PollUpdatedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
def broadcastUserRespondedToTypedPollRespMsg(outGW: OutMsgRouter, meetingId: String, userId: String,
pollId: String, answer: String, sendToId: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, sendToId)
val envelope = BbbCoreEnvelope(UserRespondedToTypedPollRespMsg.NAME, routing)
val header = BbbClientMsgHeader(UserRespondedToTypedPollRespMsg.NAME, meetingId, sendToId)
val body = UserRespondedToTypedPollRespMsgBody(pollId, userId, answer)
val event = UserRespondedToTypedPollRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
def broadcastUserRespondedToPollRecordMsg(outGW: OutMsgRouter, meetingId: String, userId: String,
pollId: String, answerId: Int, answer: String, isSecret: Boolean): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(UserRespondedToPollRecordMsg.NAME, routing)
val header = BbbClientMsgHeader(UserRespondedToPollRecordMsg.NAME, meetingId, userId)
val body = UserRespondedToPollRecordMsgBody(pollId, answerId, answer, isSecret)
val event = UserRespondedToPollRecordMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
def broadcastUserRespondedToPollRespMsg(outGW: OutMsgRouter, meetingId: String, userId: String,
pollId: String, answerIds: Seq[Int], sendToId: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, sendToId)
val envelope = BbbCoreEnvelope(UserRespondedToPollRespMsg.NAME, routing)
val header = BbbClientMsgHeader(UserRespondedToPollRespMsg.NAME, meetingId, sendToId)
val body = UserRespondedToPollRespMsgBody(pollId, userId, answerIds)
val event = UserRespondedToPollRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
}

View File

@ -4,7 +4,7 @@ import org.bigbluebutton.common2.domain.SimplePollResultOutVO
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.models.Polls
import org.bigbluebutton.core.running.{ LiveMeeting }
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core.models.Users2x
trait RespondToPollReqMsgHdlr {
@ -12,45 +12,12 @@ trait RespondToPollReqMsgHdlr {
def handle(msg: RespondToPollReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastPollUpdatedEvent(msg: RespondToPollReqMsg, pollId: String, poll: SimplePollResultOutVO): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(PollUpdatedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(PollUpdatedEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
val body = PollUpdatedEvtMsgBody(pollId, poll)
val event = PollUpdatedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
def broadcastUserRespondedToPollRecordMsg(msg: RespondToPollReqMsg, pollId: String, answerId: Int, answer: String, isSecret: Boolean): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(UserRespondedToPollRecordMsg.NAME, routing)
val header = BbbClientMsgHeader(UserRespondedToPollRecordMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
val body = UserRespondedToPollRecordMsgBody(pollId, answerId, answer, isSecret)
val event = UserRespondedToPollRecordMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
def broadcastUserRespondedToPollRespMsg(msg: RespondToPollReqMsg, pollId: String, answerIds: Seq[Int], sendToId: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, sendToId)
val envelope = BbbCoreEnvelope(UserRespondedToPollRespMsg.NAME, routing)
val header = BbbClientMsgHeader(UserRespondedToPollRespMsg.NAME, liveMeeting.props.meetingProp.intId, sendToId)
val body = UserRespondedToPollRespMsgBody(pollId, msg.header.userId, answerIds)
val event = UserRespondedToPollRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
if (Polls.checkUserResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls) == false) {
if (!Polls.hasUserAlreadyResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls)) {
for {
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToPollReqMsg(msg.header.userId, msg.body.pollId,
msg.body.questionId, msg.body.answerIds, liveMeeting)
} yield {
broadcastPollUpdatedEvent(msg, pollId, updatedPoll)
PollHdlrHelpers.broadcastPollUpdatedEvent(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, updatedPoll)
for {
poll <- Polls.getPoll(pollId, liveMeeting.polls)
} yield {
@ -58,14 +25,14 @@ trait RespondToPollReqMsgHdlr {
answerId <- msg.body.answerIds
} yield {
val answerText = poll.questions(0).answers.get(answerId).key
broadcastUserRespondedToPollRecordMsg(msg, pollId, answerId, answerText, poll.isSecret)
PollHdlrHelpers.broadcastUserRespondedToPollRecordMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, answerId, answerText, poll.isSecret)
}
}
for {
presenter <- Users2x.findPresenter(liveMeeting.users2x)
} yield {
broadcastUserRespondedToPollRespMsg(msg, pollId, msg.body.answerIds, presenter.intId)
PollHdlrHelpers.broadcastUserRespondedToPollRespMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, msg.body.answerIds, presenter.intId)
}
}
} else {

View File

@ -1,10 +1,11 @@
package org.bigbluebutton.core.apps.polls
import org.bigbluebutton.ClientSettings.getConfigPropertyValueByPathAsIntOrElse
import org.bigbluebutton.common2.domain.SimplePollResultOutVO
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.models.Polls
import org.bigbluebutton.core.running.{ LiveMeeting }
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core.models.Users2x
trait RespondToTypedPollReqMsgHdlr {
@ -12,43 +13,60 @@ trait RespondToTypedPollReqMsgHdlr {
def handle(msg: RespondToTypedPollReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastPollUpdatedEvent(msg: RespondToTypedPollReqMsg, pollId: String, poll: SimplePollResultOutVO): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(PollUpdatedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(PollUpdatedEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
val body = PollUpdatedEvtMsgBody(pollId, poll)
val event = PollUpdatedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
def broadcastUserRespondedToTypedPollRespMsg(msg: RespondToTypedPollReqMsg, pollId: String, answer: String, sendToId: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, sendToId)
val envelope = BbbCoreEnvelope(UserRespondedToTypedPollRespMsg.NAME, routing)
val header = BbbClientMsgHeader(UserRespondedToTypedPollRespMsg.NAME, liveMeeting.props.meetingProp.intId, sendToId)
val body = UserRespondedToTypedPollRespMsgBody(pollId, msg.header.userId, answer)
val event = UserRespondedToTypedPollRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
if (Polls.isResponsePollType(msg.body.pollId, liveMeeting.polls) &&
Polls.checkUserResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls) == false &&
Polls.checkUserAddedQuestion(msg.body.pollId, msg.header.userId, liveMeeting.polls) == false) {
for {
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToTypedPollReqMsg(msg.header.userId, msg.body.pollId,
msg.body.questionId, msg.body.answer, liveMeeting)
} yield {
broadcastPollUpdatedEvent(msg, pollId, updatedPoll)
!Polls.hasUserAlreadyResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls) &&
!Polls.hasUserAlreadyAddedTypedAnswer(msg.body.pollId, msg.header.userId, liveMeeting.polls)) {
for {
presenter <- Users2x.findPresenter(liveMeeting.users2x)
} yield {
broadcastUserRespondedToTypedPollRespMsg(msg, pollId, msg.body.answer, presenter.intId)
//Truncate answer case it is longer than `maxTypedAnswerLength`
val maxTypedAnswerLength = getConfigPropertyValueByPathAsIntOrElse(liveMeeting.clientSettings, "public.poll.maxTypedAnswerLength", 45)
val answer = msg.body.answer.substring(0, Math.min(msg.body.answer.length, maxTypedAnswerLength))
val answerExists = Polls.findAnswerWithText(msg.body.pollId, msg.body.questionId, answer, liveMeeting.polls)
//Create answer if it doesn't exist
answerExists match {
case None => {
for {
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToTypedPollReqMsg(msg.header.userId, msg.body.pollId,
msg.body.questionId, answer, liveMeeting)
} yield {
PollHdlrHelpers.broadcastPollUpdatedEvent(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, updatedPoll)
for {
presenter <- Users2x.findPresenter(liveMeeting.users2x)
} yield {
PollHdlrHelpers.broadcastUserRespondedToTypedPollRespMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, answer, presenter.intId)
}
}
}
case _ => //Do nothing, answer with same text exists already
}
//Submit the answer
Polls.findAnswerWithText(msg.body.pollId, msg.body.questionId, answer, liveMeeting.polls) match {
case Some(answerId) => {
for {
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToPollReqMsg(msg.header.userId, msg.body.pollId,
msg.body.questionId, Seq(answerId), liveMeeting)
} yield {
PollHdlrHelpers.broadcastPollUpdatedEvent(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, updatedPoll)
for {
poll <- Polls.getPoll(pollId, liveMeeting.polls)
} yield {
val answerText = poll.questions(0).answers.get(answerId).key
PollHdlrHelpers.broadcastUserRespondedToPollRecordMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, answerId, answerText, poll.isSecret)
}
for {
presenter <- Users2x.findPresenter(liveMeeting.users2x)
} yield {
PollHdlrHelpers.broadcastUserRespondedToPollRespMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, Seq(answerId), presenter.intId)
}
}
}
case None => log.error("Error while trying to answer the poll {} in meeting {}: Answer not found or something went wrong while trying to create the answer.", msg.body.pollId, msg.header.meetingId)
}
} else {
log.info("Ignoring typed answer from user {} once user already added an answer to this poll {} in meeting {}", msg.header.userId, msg.body.pollId, msg.header.meetingId)
}

View File

@ -7,8 +7,8 @@ import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.Polls
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.db.{ ChatMessageDAO, JsonUtils }
import org.bigbluebutton.core.apps.{PermissionCheck, RightsManagementTrait}
import org.bigbluebutton.core.db.{ChatMessageDAO, JsonUtils, NotificationDAO}
import org.bigbluebutton.core2.message.senders.MsgBuilder
import spray.json.DefaultJsonProtocol.jsonFormat2
@ -37,6 +37,7 @@ trait ShowPollResultReqMsgHdlr extends RightsManagementTrait {
Vector()
)
bus.outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
// SendWhiteboardAnnotationPubMsg
val annotationRouting = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)

View File

@ -1,10 +1,9 @@
package org.bigbluebutton.core.apps.presentationpod
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.api.{ CapturePresentationReqInternalMsg, CaptureSharedNotesReqInternalMsg }
import org.bigbluebutton.core.api.{ CapturePresentationReqInternalMsg }
import org.bigbluebutton.core.apps.groupchats.GroupChatApp
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.apps.presentationpod.PresentationSender
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.db.{ ChatMessageDAO, PresPresentationDAO }
import org.bigbluebutton.core.domain.MeetingState2x
@ -46,7 +45,7 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
def buildNewPresFileAvailable(annotatedFileURI: String, originalFileURI: String, convertedFileURI: String,
presId: String, fileStateType: String): NewPresFileAvailableMsg = {
val header = BbbClientMsgHeader(NewPresFileAvailableMsg.NAME, "not-used", "not-used")
val body = NewPresFileAvailableMsgBody(annotatedFileURI, originalFileURI, convertedFileURI, presId, fileStateType)
val body = NewPresFileAvailableMsgBody(annotatedFileURI, originalFileURI, convertedFileURI, presId, fileStateType, "")
NewPresFileAvailableMsg(header, body)
}
@ -160,7 +159,7 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
val pages: List[Int] = m.body.pages // Desired presentation pages for export
val pagesRange: List[Int] = if (allPages) (1 to pageCount).toList else pages
val exportJob: ExportJob = new ExportJob(jobId, JobTypes.DOWNLOAD, "annotated_slides", presId, presLocation, allPages, pagesRange, meetingId, "");
val exportJob: ExportJob = new ExportJob(jobId, JobTypes.DOWNLOAD, currentPres.get.name, "annotated_slides", presId, presLocation, allPages, pagesRange, meetingId, "");
val storeAnnotationPages: List[PresentationPageForExport] = getPresentationPagesForExport(pagesRange, pageCount, presId, currentPres, liveMeeting);
val isPresentationOriginalOrConverted = m.body.fileStateType == "Original" || m.body.fileStateType == "Converted"
@ -226,7 +225,7 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
val currentPage: PresentationPage = PresentationInPod.getCurrentPage(currentPres.get).get
val pagesRange: List[Int] = if (allPages) (1 to pageCount).toList else List(currentPage.num)
val exportJob: ExportJob = ExportJob(jobId, JobTypes.CAPTURE_PRESENTATION, filename, presId, presLocation, allPages, pagesRange, parentMeetingId, presentationUploadToken)
val exportJob: ExportJob = ExportJob(jobId, JobTypes.CAPTURE_PRESENTATION, filename, filename, presId, presLocation, allPages, pagesRange, parentMeetingId, presentationUploadToken)
val storeAnnotationPages: List[PresentationPageForExport] = getPresentationPagesForExport(pagesRange, pageCount, presId, currentPres, liveMeeting);
val annotationCount: Int = storeAnnotationPages.map(_.annotations.size).sum
@ -252,11 +251,10 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
liveMeeting.props.meetingProp.intId, m.body.presId
)
//TODO let frontend choose the name in favor of internationalization
if (m.body.fileStateType == "Annotated") {
val presentationDownloadInfo = Map(
"fileURI" -> m.body.annotatedFileURI,
"filename" -> "annotated_slides.pdf"
"filename" -> m.body.fileName
)
ChatMessageDAO.insertSystemMsg(liveMeeting.props.meetingProp.intId, GroupChatApp.MAIN_PUBLIC_CHAT, "", GroupChatMessageType.PRESENTATION, presentationDownloadInfo, "")
} else if (m.body.fileStateType == "Converted") {
@ -269,35 +267,8 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
bus.outGW.send(buildBroadcastNewPresFileAvailable(m, liveMeeting))
}
def handle(m: CaptureSharedNotesReqInternalMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
val parentMeetingId = liveMeeting.props.meetingProp.intId
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, parentMeetingId, "not-used")
val envelope = BbbCoreEnvelope(PresentationPageConversionStartedEventMsg.NAME, routing)
val header = BbbClientMsgHeader(CaptureSharedNotesReqEvtMsg.NAME, parentMeetingId, "not-used")
val body = CaptureSharedNotesReqEvtMsgBody(m.breakoutId, m.filename)
val event = CaptureSharedNotesReqEvtMsg(header, body)
bus.outGW.send(BbbCommonEnvCoreMsg(envelope, event))
}
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))
}
def handle(m: PadCapturePubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
val userId: String = "system"
val jobId: String = s"${m.body.breakoutId}-notes" // Used as the temporaryPresentationId upon upload
val filename = m.body.filename
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, presentationId))
val exportJob = new ExportJob(jobId, JobTypes.CAPTURE_NOTES, filename, m.body.padId, "", true, List(), m.body.parentMeetingId, presentationUploadToken)
val job = buildStoreExportJobInRedisSysMsg(exportJob, liveMeeting)
bus.outGW.send(job)
}
}

View File

@ -13,31 +13,7 @@ trait PresentationConversionUpdatePubMsgHdlr {
def handle(msg: PresentationConversionUpdateSysPubMsg, state: MeetingState2x,
liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
def broadcastEvent(msg: PresentationConversionUpdateSysPubMsg): Unit = {
val routing = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
liveMeeting.props.meetingProp.intId, msg.header.userId
)
val envelope = BbbCoreEnvelope(PresentationConversionUpdateEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(
PresentationConversionUpdateEvtMsg.NAME,
liveMeeting.props.meetingProp.intId, msg.header.userId
)
val body = PresentationConversionUpdateEvtMsgBody(
msg.body.podId,
msg.body.messageKey,
msg.body.code,
msg.body.presentationId,
msg.body.presName,
msg.body.temporaryPresentationId
)
val event = PresentationConversionUpdateEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
broadcastEvent(msg)
// broadcastEvent(msg)
state
}
}

View File

@ -15,7 +15,7 @@ trait SetPresenterInDefaultPodInternalMsgHdlr {
msg: SetPresenterInDefaultPodInternalMsg, state: MeetingState2x,
liveMeeting: LiveMeeting, bus: MessageBus
): MeetingState2x = {
// Swith presenter as default presenter pod has changed.
// Switch 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)
}

View File

@ -12,7 +12,7 @@ trait SyncGetPresentationPodsMsgHdlr {
def handleSyncGetPresentationPods(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
def buildSyncGetPresentationPodsRespMsg(pods: Vector[PresentationPodVO]): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetPresentationPodsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetPresentationPodsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")

View File

@ -9,9 +9,10 @@ trait SyncGetScreenshareInfoRespMsgHdlr {
this: ScreenshareApp2x =>
def handleSyncGetScreenshareInfoRespMsg(liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(
val routing = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
liveMeeting.props.meetingProp.intId,
liveMeeting.props.systemProps.html5InstanceId.toString
"nodeJSapp"
)
val envelope = BbbCoreEnvelope(SyncGetScreenshareInfoRespMsg.NAME, routing)
val header = BbbClientMsgHeader(

View File

@ -30,7 +30,10 @@ trait DeactivateTimerReqMsgHdlr extends RightsManagementTrait {
val reason = "You need to be the presenter or moderator to deactivate timer"
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
} else {
TimerModel.setIsActive(liveMeeting.timerModel, false)
TimerModel.setRunning(liveMeeting.timerModel, running = false)
TimerModel.setIsActive(liveMeeting.timerModel, active = false)
TimerModel.setStopwatch(liveMeeting.timerModel, stopwatch = true)
TimerModel.reset(liveMeeting.timerModel)
TimerDAO.update(liveMeeting.props.meetingProp.intId, liveMeeting.timerModel)
broadcastEvent()
}

View File

@ -31,7 +31,7 @@ trait StartTimerReqMsgHdlr extends RightsManagementTrait {
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
} else {
TimerModel.setStartedAt(liveMeeting.timerModel, System.currentTimeMillis())
TimerModel.setRunning(liveMeeting.timerModel, true)
TimerModel.setRunning(liveMeeting.timerModel, running = true)
TimerDAO.update(liveMeeting.props.meetingProp.intId, liveMeeting.timerModel)
broadcastEvent()
}

View File

@ -33,10 +33,9 @@ trait StopTimerReqMsgHdlr extends RightsManagementTrait {
val reason = "You need to be the presenter or moderator to stop timer"
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
} else {
TimerModel.setAccumulated(liveMeeting.timerModel, msg.body.accumulated)
TimerModel.setRunning(liveMeeting.timerModel, false)
TimerModel.setRunning(liveMeeting.timerModel, running = false)
TimerDAO.update(liveMeeting.props.meetingProp.intId, liveMeeting.timerModel)
broadcastEvent(msg.body.accumulated)
broadcastEvent(TimerModel.getAccumulated(liveMeeting.timerModel))
}
}
}

View File

@ -34,6 +34,7 @@ trait SwitchTimerReqMsgHdlr extends RightsManagementTrait {
} else {
if (TimerModel.getStopwatch(liveMeeting.timerModel) != msg.body.stopwatch) {
TimerModel.setStopwatch(liveMeeting.timerModel, msg.body.stopwatch)
TimerModel.setRunning(liveMeeting.timerModel, running = false)
TimerModel.reset(liveMeeting.timerModel) //Reset on switch Stopwatch/Timer
if (msg.body.stopwatch) {
TimerModel.setTrack(liveMeeting.timerModel, "noTrack")

View File

@ -85,7 +85,7 @@ object AssignPresenterActionHandler extends RightsManagementTrait {
for {
u <- RegisteredUsers.findWithUserId(oldPres.intId, liveMeeting.registeredUsers)
} yield {
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, oldPres.intId, u.sessionToken, "role_changed", outGW)
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, oldPres.intId, u.sessionToken, "role_changed", outGW)
}
}
}
@ -100,7 +100,7 @@ object AssignPresenterActionHandler extends RightsManagementTrait {
for {
u <- RegisteredUsers.findWithUserId(newPres.intId, liveMeeting.registeredUsers)
} yield {
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, newPres.intId, u.sessionToken, "role_changed", outGW)
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, newPres.intId, u.sessionToken, "role_changed", outGW)
}
}
}

View File

@ -3,13 +3,13 @@ package org.bigbluebutton.core.apps.users
import org.bigbluebutton.LockSettingsUtil
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.db.MeetingLockSettingsDAO
import org.bigbluebutton.core.db.{ MeetingLockSettingsDAO, NotificationDAO }
import org.bigbluebutton.core.models._
import org.bigbluebutton.core.running.OutMsgRouter
import org.bigbluebutton.core.running.MeetingActor
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core2.Permissions
import org.bigbluebutton.core2.message.senders.MsgBuilder
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
this: MeetingActor =>
@ -66,6 +66,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
LockSettingsUtil.enforceCamLockSettingsForAllUsers(liveMeeting, outGW)
} else {
@ -78,6 +79,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
}
@ -92,6 +94,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
VoiceUsers.findAll(liveMeeting.voiceUsers) foreach { vu =>
if (vu.intId.startsWith(IntIdPrefixType.DIAL_IN)) { // only Dial-in users need this
val eventExplicitLock = buildLockMessage(liveMeeting.props.meetingProp.intId, vu.intId, msg.body.setBy, settings.disableMic)
@ -109,6 +112,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
}
@ -123,6 +127,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
} else {
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
liveMeeting.props.meetingProp.intId,
@ -133,6 +138,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
}
@ -147,6 +153,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
} else {
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
liveMeeting.props.meetingProp.intId,
@ -157,6 +164,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
}
@ -171,6 +179,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
} else {
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
liveMeeting.props.meetingProp.intId,
@ -181,6 +190,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
}
@ -195,6 +205,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
} else {
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
liveMeeting.props.meetingProp.intId,
@ -205,6 +216,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
}
@ -237,6 +249,16 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
)
outGW.send(BbbCommonEnvCoreMsg(envelope, LockSettingsInMeetingChangedEvtMsg(header, body)))
//Refresh graphql session for all locked viewers
for {
user <- Users2x.findAll(liveMeeting.users2x)
if user.locked
if user.role == Roles.VIEWER_ROLE
regUser <- RegisteredUsers.findWithUserId(user.intId, liveMeeting.registeredUsers)
} yield {
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, "lockSettings_changed", outGW)
}
}
}
}

View File

@ -1,5 +1,6 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.ClientSettings.{ getConfigPropertyValueByPath, getConfigPropertyValueByPathAsIntOrElse }
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.RightsManagementTrait
import org.bigbluebutton.core.models.{ UserState, Users2x }
@ -29,9 +30,11 @@ trait ChangeUserReactionEmojiReqMsgHdlr extends RightsManagementTrait {
outGW.send(msgEventChange)
}
//Get durationInSeconds from Client config
val userReactionExpire = getConfigPropertyValueByPathAsIntOrElse(liveMeeting.clientSettings, "public.userReaction.expire", 30)
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
newUserState <- Users2x.setReactionEmoji(liveMeeting.users2x, user.intId, msg.body.reactionEmoji)
newUserState <- Users2x.setReactionEmoji(liveMeeting.users2x, user.intId, msg.body.reactionEmoji, userReactionExpire)
} yield {
if (user.reactionEmoji != msg.body.reactionEmoji) {
broadcast(newUserState, msg.body.reactionEmoji)

View File

@ -5,6 +5,7 @@ import org.bigbluebutton.core.models.{ RegisteredUsers, Roles, UserState, Users2
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.LockSettingsUtil
import org.bigbluebutton.core.db.NotificationDAO
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait {
@ -43,6 +44,7 @@ trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
Users2x.changeRole(liveMeeting.users2x, uvo, msg.body.role)
val event = buildUserRoleChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.userId,
@ -59,6 +61,7 @@ trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
val newUvo: UserState = Users2x.changeRole(liveMeeting.users2x, uvo, msg.body.role)
val event = buildUserRoleChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.userId,
@ -73,7 +76,7 @@ trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait {
for {
u <- RegisteredUsers.findWithUserId(uvo.intId, liveMeeting.registeredUsers)
} yield {
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, uvo.intId, u.sessionToken, "role_changed", outGW)
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, uvo.intId, u.sessionToken, "role_changed", outGW)
}
}
}

View File

@ -24,7 +24,7 @@ trait ClearAllUsersReactionCmdMsgHdlr extends RightsManagementTrait {
user <- Users2x.findAll(liveMeeting.users2x)
} yield {
//Don't clear away and RaiseHand
Users2x.setReactionEmoji(liveMeeting.users2x, user.intId, "none")
Users2x.setReactionEmoji(liveMeeting.users2x, user.intId, "none", 0)
}
sendClearedAllUsersReactionEvtMsg(outGW, liveMeeting.props.meetingProp.intId, msg.header.userId)
} else {

View File

@ -74,7 +74,7 @@ trait EjectUserFromMeetingCmdMsgHdlr extends RightsManagementTrait {
Sender.sendDisconnectClientSysMsg(meetingId, ru.id, ejectedBy, EjectReasonCode.EJECT_USER, outGW)
// Force reconnection with graphql to refresh permissions
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, registeredUser.id, registeredUser.sessionToken, EjectReasonCode.EJECT_USER, outGW)
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, registeredUser.id, registeredUser.sessionToken, EjectReasonCode.EJECT_USER, outGW)
}
} else {
// User is ejecting self, so just eject this userid not all sessions if joined using multiple
@ -93,7 +93,7 @@ trait EjectUserFromMeetingCmdMsgHdlr extends RightsManagementTrait {
Sender.sendDisconnectClientSysMsg(meetingId, userId, ejectedBy, EjectReasonCode.EJECT_USER, outGW)
// Force reconnection with graphql to refresh permissions
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, registeredUser.id, registeredUser.sessionToken, EjectReasonCode.EJECT_USER, outGW)
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, registeredUser.id, registeredUser.sessionToken, EjectReasonCode.EJECT_USER, outGW)
}
}
@ -129,7 +129,7 @@ trait EjectUserFromMeetingSysMsgHdlr {
for {
regUser <- RegisteredUsers.findWithUserId(userId, liveMeeting.registeredUsers)
} yield {
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, EjectReasonCode.SYSTEM_EJECT_USER, outGW)
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, EjectReasonCode.SYSTEM_EJECT_USER, outGW)
}
}
}

View File

@ -44,7 +44,7 @@ trait LockUserInMeetingCmdMsgHdlr extends RightsManagementTrait {
for {
u <- RegisteredUsers.findWithUserId(uvo.intId, liveMeeting.registeredUsers)
} yield {
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, uvo.intId, u.sessionToken, "lock_user_changed", outGW)
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, uvo.intId, u.sessionToken, "lock_user_changed", outGW)
}
log.info("Lock user. meetingId=" + props.meetingProp.intId + " userId=" + uvo.intId + " locked=" + uvo.locked)

View File

@ -1,6 +1,7 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.db.NotificationDAO
import org.bigbluebutton.core.models._
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core.util.ColorPicker
@ -49,7 +50,7 @@ trait RegisterUserReqMsgHdlr {
Sender.sendDisconnectClientSysMsg(meetingId, userToRemove.id, SystemUser.ID, EjectReasonCode.DUPLICATE_USER, outGW)
// Force reconnection with graphql to refresh permissions
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, userToRemove.id, userToRemove.sessionToken, EjectReasonCode.DUPLICATE_USER, outGW)
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, userToRemove.id, userToRemove.sessionToken, EjectReasonCode.DUPLICATE_USER, outGW)
}
}
}
@ -58,7 +59,7 @@ trait RegisterUserReqMsgHdlr {
val guestStatus = msg.body.guestStatus
val regUser = RegisteredUsers.create(msg.body.intUserId, msg.body.extUserId,
val regUser = RegisteredUsers.create(liveMeeting.props.meetingProp.intId, msg.body.intUserId, msg.body.extUserId,
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.enforceLayout, msg.body.customParameters, false)
@ -107,6 +108,7 @@ trait RegisterUserReqMsgHdlr {
Vector(s"${regUser.name}")
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
case GuestStatus.DENY =>
val g = GuestApprovedVO(regUser.id, GuestStatus.DENY)
UsersApp.approveOrRejectGuest(liveMeeting, outGW, g, SystemUser.ID)

View File

@ -1,63 +0,0 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core.models.{ UserState, Users2x }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.SystemConfiguration
import scala.util.Random
trait SelectRandomViewerReqMsgHdlr extends RightsManagementTrait {
this: UsersApp =>
val outGW: OutMsgRouter
def handleSelectRandomViewerReqMsg(msg: SelectRandomViewerReqMsg): Unit = {
log.debug("Received SelectRandomViewerReqMsg {}", SelectRandomViewerReqMsg)
def broadcastEvent(msg: SelectRandomViewerReqMsg, users: Vector[String], choice: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(SelectRandomViewerRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SelectRandomViewerRespMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
val body = SelectRandomViewerRespMsgBody(msg.header.userId, users, choice)
val event = SelectRandomViewerRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
if (permissionFailed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to select random user."
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
} else {
val users = Users2x.getRandomlyPickableUsers(liveMeeting.users2x, false)
val usersPicked = Users2x.getRandomlyPickableUsers(liveMeeting.users2x, reduceDuplicatedPick)
val randNum = new scala.util.Random
var pickedUser = if (usersPicked.size == 0) "" else usersPicked(randNum.nextInt(usersPicked.size)).intId
if (reduceDuplicatedPick) {
if (usersPicked.size <= 1) {
// Initialise the exemption
val usersToUnexempt = Users2x.findAll(liveMeeting.users2x)
usersToUnexempt foreach { u =>
Users2x.setUserExempted(liveMeeting.users2x, u.intId, false)
}
if (usersPicked.size == 0) {
// Pick again
val usersRepicked = Users2x.getRandomlyPickableUsers(liveMeeting.users2x, reduceDuplicatedPick)
pickedUser = if (usersRepicked.size == 0) "" else usersRepicked(randNum.nextInt(usersRepicked.size)).intId
Users2x.setUserExempted(liveMeeting.users2x, pickedUser, true)
}
} else if (usersPicked.size > 1) {
Users2x.setUserExempted(liveMeeting.users2x, pickedUser, true)
}
}
val userIds = users.map { case (v) => v.intId }
broadcastEvent(msg, userIds, pickedUser)
}
}
}

View File

@ -10,7 +10,7 @@ import org.bigbluebutton.core.api.SendRecordingTimerInternalMsg
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core2.message.senders.MsgBuilder
import org.bigbluebutton.core.apps.voice.VoiceApp
import org.bigbluebutton.core.db.MeetingRecordingDAO
import org.bigbluebutton.core.db.{ MeetingRecordingDAO, NotificationDAO }
trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
this: UsersApp =>
@ -49,6 +49,7 @@ trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
MeetingStatus2x.recordingStarted(liveMeeting.status)
MeetingRecordingDAO.insertRecording(liveMeeting.props.meetingProp.intId, msg.body.setBy)
@ -75,6 +76,7 @@ trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
MeetingStatus2x.recordingStopped(liveMeeting.status)
MeetingRecordingDAO.updateStopped(liveMeeting.props.meetingProp.intId, msg.body.setBy)

View File

@ -0,0 +1,41 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.models.{ UserState, Users2x }
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.domain.MeetingState2x
trait SetUserSpeechOptionsMsgHdlr extends RightsManagementTrait {
this: UsersApp =>
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
def handleSetUserSpeechOptionsReqMsg(msg: SetUserSpeechOptionsReqMsg): Unit = {
log.info("handleSetUserSpeechOptionsReqMsg: partialUtterances={} minUtteranceLength={} userId={}", msg.body.partialUtterances, msg.body.minUtteranceLength, msg.header.userId)
def broadcastUserSpeechOptionsChanged(user: UserState, partialUtterances: Boolean, minUtteranceLength: Int): Unit = {
val routingChange = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
liveMeeting.props.meetingProp.intId, user.intId
)
val envelopeChange = BbbCoreEnvelope(UserSpeechOptionsChangedEvtMsg.NAME, routingChange)
val headerChange = BbbClientMsgHeader(UserSpeechOptionsChangedEvtMsg.NAME, liveMeeting.props.meetingProp.intId, user.intId)
val bodyChange = UserSpeechOptionsChangedEvtMsgBody(partialUtterances, minUtteranceLength)
val eventChange = UserSpeechOptionsChangedEvtMsg(headerChange, bodyChange)
val msgEventChange = BbbCommonEnvCoreMsg(envelopeChange, eventChange)
outGW.send(msgEventChange)
}
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
} yield {
var changeLocale: Option[UserState] = None;
//changeLocale = Users2x.setUserSpeechLocale(liveMeeting.users2x, msg.header.userId, msg.body.locale)
broadcastUserSpeechOptionsChanged(user, msg.body.partialUtterances, msg.body.minUtteranceLength)
}
}
}

View File

@ -11,7 +11,7 @@ trait SyncGetUsersMeetingRespMsgHdlr {
val outGW: OutMsgRouter
def handleSyncGetUsersMeetingRespMsg(): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetUsersMeetingRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetUsersMeetingRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")

View File

@ -1,6 +1,7 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.db.UserDAO
import org.bigbluebutton.core.models.{ Users2x, VoiceUserState, VoiceUsers }
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
@ -29,10 +30,10 @@ trait UserConnectedToGlobalAudioMsgHdlr {
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
} yield {
val vu = VoiceUserState(
intId = user.intId,
voiceUserId = user.intId,
meetingId = props.meetingProp.intId,
callingWith = "flash",
callerName = user.name,
callerNum = user.name,

View File

@ -0,0 +1,55 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.ClientSettings.{ getConfigPropertyValueByPathAsListOfIntOrElse, getConfigPropertyValueByPathAsListOfStringOrElse }
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.RightsManagementTrait
import org.bigbluebutton.core.db.UserConnectionStatusDAO
import org.bigbluebutton.core.models.Users2x
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
trait UserConnectionAliveReqMsgHdlr extends RightsManagementTrait {
this: UsersApp =>
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
def handleUserConnectionAliveReqMsg(msg: UserConnectionAliveReqMsg): Unit = {
log.info("handleUserConnectionAliveReqMsg: networkRttInMs={} userId={}", msg.body.networkRttInMs, msg.body.userId)
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
} yield {
val rtt: Option[Double] = msg.body.networkRttInMs match {
case 0 => None
case rtt: Double => Some(rtt)
}
val status = getLevelFromRtt(msg.body.networkRttInMs)
UserConnectionStatusDAO.updateUserAlive(user.meetingId, user.intId, rtt, status)
}
}
def getLevelFromRtt(networkRttInMs: Double): String = {
val levelOptions = getConfigPropertyValueByPathAsListOfStringOrElse(
liveMeeting.clientSettings,
"public.stats.level",
List("warning", "danger", "critical")
)
val rttOptions = getConfigPropertyValueByPathAsListOfIntOrElse(
liveMeeting.clientSettings,
"public.stats.rtt",
List(500, 1000, 2000)
)
val statusRttXLevel = levelOptions.zip(rttOptions).reverse
val statusFound = statusRttXLevel.collectFirst {
case (level, rtt) if networkRttInMs > rtt => level
}
statusFound.getOrElse("normal")
}
}

View File

@ -28,7 +28,7 @@ trait UserDisconnectedFromGlobalAudioMsgHdlr {
for {
user <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId)
} yield {
VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, user.intId)
VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, liveMeeting.props.meetingProp.intId, user.intId)
broadcastEvent(user)
}
}

View File

@ -0,0 +1,29 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.core.api.UserEstablishedGraphqlConnectionInternalMsg
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.Users2x
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, MeetingActor, OutMsgRouter }
trait UserEstablishedGraphqlConnectionInternalMsgHdlr extends HandlerHelpers {
this: MeetingActor =>
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
def handleUserEstablishedGraphqlConnectionInternalMsg(msg: UserEstablishedGraphqlConnectionInternalMsg, state: MeetingState2x): MeetingState2x = {
log.info("Received user established a graphql connection. user {} meetingId={}", msg.userId, liveMeeting.props.meetingProp.intId)
Users2x.findWithIntId(liveMeeting.users2x, msg.userId) match {
case Some(reconnectingUser) =>
if (reconnectingUser.userLeftFlag.left) {
log.info("Resetting flag that user left meeting. user {}", msg.userId)
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.userId, leftFlag = false)
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.userId)
}
state
case None =>
state
}
}
}

View File

@ -20,7 +20,7 @@ trait UserJoinMeetingAfterReconnectReqMsgHdlr extends HandlerHelpers with UserJo
if (reconnectingUser.userLeftFlag.left) {
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
// User has reconnected. Just reset it's flag. ralam Oct 23, 2018
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false)
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, leftFlag = false)
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
}
state

View File

@ -2,7 +2,7 @@ package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs.UserJoinMeetingReqMsg
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers
import org.bigbluebutton.core.db.{ UserDAO, UserStateDAO }
import org.bigbluebutton.core.db.{ NotificationDAO, UserDAO, UserStateDAO }
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models._
import org.bigbluebutton.core.running._
@ -51,14 +51,14 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
notifyPreviousUsersWithSameExtId(regUser)
clearCachedVoiceUser(regUser)
clearExpiredUserState(regUser)
invalidateUserGraphqlConnection(regUser)
ForceUserGraphqlReconnection(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)
UserDAO.updateJoinError(msg.header.meetingId, msg.body.userId, failReasonCode, failReason)
state
}
@ -71,7 +71,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
private def resetUserLeftFlag(msg: UserJoinMeetingReqMsg) = {
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false)
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, leftFlag = false)
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
}
@ -137,6 +137,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
Vector(newUser.name)
)
outGW.send(notifyUserEvent)
NotificationDAO.insert(notifyUserEvent)
}
private def clearCachedVoiceUser(regUser: RegisteredUser) =
@ -144,9 +145,9 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
VoiceUsers.recoverVoiceUser(liveMeeting.voiceUsers, regUser.id)
private def clearExpiredUserState(regUser: RegisteredUser) =
UserStateDAO.updateExpired(regUser.id, false)
UserStateDAO.updateExpired(regUser.meetingId, regUser.id, false)
private def invalidateUserGraphqlConnection(regUser: RegisteredUser) =
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, "user_joined", outGW)
private def ForceUserGraphqlReconnection(regUser: RegisteredUser) =
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, "user_joined", outGW)
}

View File

@ -1,6 +1,7 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs.UserLeaveReqMsg
import org.bigbluebutton.core.api.{ UserClosedAllGraphqlConnectionsInternalMsg }
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.{ RegisteredUsers, Users2x }
import org.bigbluebutton.core.running.{ HandlerHelpers, MeetingActor, OutMsgRouter }
@ -12,26 +13,36 @@ trait UserLeaveReqMsgHdlr extends HandlerHelpers {
val outGW: OutMsgRouter
def handleUserLeaveReqMsg(msg: UserLeaveReqMsg, state: MeetingState2x): MeetingState2x = {
Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) match {
handleUserLeaveReq(msg.body.userId, msg.header.meetingId, msg.body.loggedOut, state)
}
def handleUserClosedAllGraphqlConnectionsInternalMsg(msg: UserClosedAllGraphqlConnectionsInternalMsg, state: MeetingState2x): MeetingState2x = {
log.info("Received user closed all graphql connections. user {} meetingId={}", msg.userId, liveMeeting.props.meetingProp.intId)
handleUserLeaveReq(msg.userId, liveMeeting.props.meetingProp.intId, loggedOut = false, state)
}
def handleUserLeaveReq(userId: String, meetingId: String, loggedOut: Boolean, state: MeetingState2x): MeetingState2x = {
Users2x.findWithIntId(liveMeeting.users2x, userId) match {
case Some(reconnectingUser) =>
log.info("Received user left meeting. user {} meetingId={}", msg.body.userId, msg.header.meetingId)
log.info("Received user left meeting. user {} meetingId={}", userId, meetingId)
if (!reconnectingUser.userLeftFlag.left) {
log.info("Setting user left flag. user {} meetingId={}", msg.body.userId, msg.header.meetingId)
log.info("Setting user left flag. user {} meetingId={}", userId, meetingId)
// Just flag that user has left as the user might be reconnecting.
// An audit will remove this user if it hasn't rejoined after a certain period of time.
// ralam oct 23, 2018
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, true)
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, userId, leftFlag = true)
Users2x.setUserLeftFlag(liveMeeting.users2x, msg.body.userId)
Users2x.setUserLeftFlag(liveMeeting.users2x, userId)
}
if (msg.body.loggedOut) {
log.info("Setting user logged out flag. user {} meetingId={}", msg.body.userId, msg.header.meetingId)
if (loggedOut) {
log.info("Setting user logged out flag. user {} meetingId={}", userId, meetingId)
for {
ru <- RegisteredUsers.findWithUserId(msg.body.userId, liveMeeting.registeredUsers)
ru <- RegisteredUsers.findWithUserId(userId, liveMeeting.registeredUsers)
} yield {
RegisteredUsers.setUserLoggedOutFlag(liveMeeting.registeredUsers, ru)
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, ru.id, ru.sessionToken, "user_loggedout", outGW)
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, ru.id, ru.sessionToken, "user_loggedout", outGW)
}
}
state
@ -39,4 +50,5 @@ trait UserLeaveReqMsgHdlr extends HandlerHelpers {
state
}
}
}

View File

@ -13,7 +13,7 @@ trait UserReactionTimeExpiredCmdMsgHdlr extends RightsManagementTrait {
def handleUserReactionTimeExpiredCmdMsg(msg: UserReactionTimeExpiredCmdMsg) {
val isNodeUser = msg.header.userId.equals("nodeJSapp")
if (isNodeUser) {
Users2x.setReactionEmoji(liveMeeting.users2x, msg.body.userId, "none")
Users2x.setReactionEmoji(liveMeeting.users2x, msg.body.userId, "none", 0)
}
}
}

View File

@ -44,7 +44,7 @@ object UsersApp {
u <- RegisteredUsers.findWithUserId(guest.guest, liveMeeting.registeredUsers)
} yield {
RegisteredUsers.setWaitingForApproval(liveMeeting.registeredUsers, u, guest.status)
UserStateDAO.updateGuestStatus(guest.guest, guest.status, approvedBy)
UserStateDAO.updateGuestStatus(liveMeeting.props.meetingProp.intId, guest.guest, guest.status, approvedBy)
// send message to user that he has been approved
val event = MsgBuilder.buildGuestApprovedEvtMsg(
@ -131,7 +131,7 @@ object UsersApp {
// println(s"ejectUserFromMeeting will cause a automaticallyAssignPresenter for user=${user}")
automaticallyAssignPresenter(outGW, liveMeeting)
}
UserStateDAO.updateEjected(userId, reason, reasonCode, ejectedBy)
UserStateDAO.updateEjected(meetingId, userId, reason, reasonCode, ejectedBy)
}
for {
@ -158,16 +158,17 @@ class UsersApp(
with RegisterUserReqMsgHdlr
with ChangeUserRoleCmdMsgHdlr
with SetUserSpeechLocaleMsgHdlr
with SetUserSpeechOptionsMsgHdlr
with SyncGetUsersMeetingRespMsgHdlr
with LogoutAndEndMeetingCmdMsgHdlr
with SetRecordingStatusCmdMsgHdlr
with RecordAndClearPreviousMarkersCmdMsgHdlr
with SendRecordingTimerInternalMsgHdlr
with GetRecordingStatusReqMsgHdlr
with SelectRandomViewerReqMsgHdlr
with AssignPresenterReqMsgHdlr
with ChangeUserPinStateReqMsgHdlr
with ChangeUserMobileFlagReqMsgHdlr
with UserConnectionAliveReqMsgHdlr
with ChangeUserReactionEmojiReqMsgHdlr
with ChangeUserRaiseHandReqMsgHdlr
with ChangeUserAwayReqMsgHdlr

View File

@ -75,7 +75,7 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers {
}
private def sendFailedValidateAuthTokenRespMsg(msg: ValidateAuthTokenReqMsg, failReason: String, failReasonCode: String) = {
UserDAO.updateJoinError(msg.body.userId, failReasonCode, failReason)
UserDAO.updateJoinError(msg.header.meetingId, 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)

View File

@ -17,7 +17,7 @@ trait SyncGetVoiceUsersMsgHdlr {
callerNum = u.callerNum, color = u.color, muted = u.muted, talking = u.talking, listenOnly = u.listenOnly)
}
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetVoiceUsersRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetVoiceUsersRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val body = SyncGetVoiceUsersRespMsgBody(voiceUsers)

View File

@ -32,7 +32,7 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
}
def registerUserInRegisteredUsers() = {
val regUser = RegisteredUsers.create(msg.body.intId, msg.body.voiceUserId,
val regUser = RegisteredUsers.create(liveMeeting.props.meetingProp.intId, msg.body.intId, msg.body.voiceUserId,
msg.body.callerIdName, Roles.VIEWER_ROLE, msg.body.intId, "", "", userColor,
true, true, GuestStatus.WAIT, true, "", Map(), false)
RegisteredUsers.add(liveMeeting.registeredUsers, regUser, liveMeeting.props.meetingProp.intId)
@ -42,6 +42,7 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
val newUser = UserState(
intId = msg.body.intId,
extId = msg.body.voiceUserId,
meetingId = liveMeeting.props.meetingProp.intId,
name = msg.body.callerIdName,
role = Roles.VIEWER_ROLE,
guest = true,
@ -58,7 +59,6 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
avatar = "",
color = userColor,
clientType = if (isDialInUser) "dial-in-user" else "",
pickExempted = false,
userLeftFlag = UserLeftFlag(false, 0)
)
Users2x.add(liveMeeting.users2x, newUser)

View File

@ -40,14 +40,14 @@ trait UserLeftVoiceConfEvtMsgHdlr {
UsersApp.guestWaitingLeft(liveMeeting, user.intId, outGW)
}
Users2x.remove(liveMeeting.users2x, user.intId)
UserDAO.delete(user.intId)
UserDAO.softDelete(user.meetingId, user.intId)
VoiceApp.removeUserFromVoiceConf(liveMeeting, outGW, msg.body.voiceUserId)
}
for {
user <- VoiceUsers.findWithVoiceUserId(liveMeeting.voiceUsers, msg.body.voiceUserId)
} yield {
VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, user.intId)
VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, liveMeeting.props.meetingProp.intId, user.intId)
broadcastEvent(user)
}

View File

@ -238,7 +238,7 @@ object VoiceApp extends SystemConfiguration {
): Unit = {
for {
u <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, userid)
oldU <- VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, userid)
oldU <- VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, u.meetingId, u.intId)
} yield {
val event = MsgBuilder.buildEjectUserFromVoiceConfSysMsg(liveMeeting.props.meetingProp.intId, liveMeeting.props.voiceProp.voiceConf, oldU.voiceUserId)
outGW.send(event)
@ -302,7 +302,6 @@ object VoiceApp extends SystemConfiguration {
)
outGW.send(msgEvent)
}
checkAndEjectOldDuplicateVoiceConfUser(intId, liveMeeting, outGW)
val isListenOnly = if (callerIdName.startsWith("LISTENONLY")) true else false
@ -310,6 +309,7 @@ object VoiceApp extends SystemConfiguration {
val voiceUserState = VoiceUserState(
intId,
voiceUserId,
meetingId = liveMeeting.props.meetingProp.intId,
callingWith,
callerIdName,
callerIdNum,
@ -393,7 +393,7 @@ object VoiceApp extends SystemConfiguration {
for {
user <- VoiceUsers.findWithVoiceUserId(liveMeeting.voiceUsers, voiceUserId)
} yield {
VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, user.intId)
VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, user.meetingId, user.intId)
broadcastEvent(user)
}

View File

@ -40,7 +40,7 @@ trait VoiceConfCallStateEvtMsgHdlr {
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)
UserVoiceConfStateDAO.insertOrUpdate(liveMeeting.props.meetingProp.intId, msg.body.userId, msg.body.voiceConf, msg.body.callSession, msg.body.clientSession, msg.body.callState)
}
}
}

View File

@ -13,9 +13,10 @@ trait SyncGetWebcamInfoRespMsgHdlr {
this: WebcamApp2x =>
def handleSyncGetWebcamInfoRespMsg(liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(
val routing = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
liveMeeting.props.meetingProp.intId,
liveMeeting.props.systemProps.html5InstanceId.toString
"nodeJSapp"
)
val envelope = BbbCoreEnvelope(SyncGetWebcamInfoRespMsg.NAME, routing)
val header = BbbClientMsgHeader(

View File

@ -3,9 +3,10 @@ package org.bigbluebutton.core.apps.webcam
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.PermissionCheck
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.db.MeetingUsersPoliciesDAO
import org.bigbluebutton.core.db.{ MeetingUsersPoliciesDAO, NotificationDAO }
import org.bigbluebutton.core.models.{ RegisteredUsers, Roles, Users2x }
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core2.message.senders.MsgBuilder
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
trait UpdateWebcamsOnlyForModeratorCmdMsgHdlr {
this: WebcamApp2x =>
@ -63,6 +64,7 @@ trait UpdateWebcamsOnlyForModeratorCmdMsgHdlr {
Vector()
)
bus.outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
} else {
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
meetingId,
@ -73,9 +75,20 @@ trait UpdateWebcamsOnlyForModeratorCmdMsgHdlr {
Vector()
)
bus.outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
broadcastEvent(meetingId, msg.body.setBy, value)
//Refresh graphql session for all locked viewers
for {
user <- Users2x.findAll(liveMeeting.users2x)
if user.locked
if user.role == Roles.VIEWER_ROLE
regUser <- RegisteredUsers.findWithUserId(user.intId, liveMeeting.registeredUsers)
} yield {
Sender.sendForceUserGraphqlReconnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, "webcamOnlyForMod_changed", bus.outGW)
}
}
case _ =>
}

View File

@ -47,7 +47,7 @@ trait UserBroadcastCamStartMsgHdlr {
val webcam = new WebcamStream(msg.body.stream, msg.header.userId, Set.empty)
for {
_ <- Webcams.addWebcamStream(liveMeeting.webcams, webcam)
_ <- Webcams.addWebcamStream(liveMeeting.props.meetingProp.intId, liveMeeting.webcams, webcam)
} yield broadcastEvent(meetingId, msg.header.userId, msg.body.stream)
}
}

View File

@ -11,7 +11,7 @@ trait ClearWhiteboardPubMsgHdlr extends RightsManagementTrait {
def handle(msg: ClearWhiteboardPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: ClearWhiteboardPubMsg, fullClear: Boolean): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(ClearWhiteboardEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(ClearWhiteboardEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)

View File

@ -11,7 +11,7 @@ trait DeleteWhiteboardAnnotationsPubMsgHdlr extends RightsManagementTrait {
def handle(msg: DeleteWhiteboardAnnotationsPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: DeleteWhiteboardAnnotationsPubMsg, removedAnnotationsIds: Array[String]): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(DeleteWhiteboardAnnotationsEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(DeleteWhiteboardAnnotationsEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)

View File

@ -10,7 +10,7 @@ trait GetWhiteboardAnnotationsReqMsgHdlr {
def handle(msg: GetWhiteboardAnnotationsReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: GetWhiteboardAnnotationsReqMsg, history: Array[AnnotationVO], multiUser: Array[String]): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(GetWhiteboardAnnotationsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(GetWhiteboardAnnotationsRespMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)

View File

@ -11,7 +11,7 @@ trait ModifyWhiteboardAccessPubMsgHdlr extends RightsManagementTrait {
def handle(msg: ModifyWhiteboardAccessPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: ModifyWhiteboardAccessPubMsg): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(ModifyWhiteboardAccessEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(ModifyWhiteboardAccessEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)

View File

@ -12,7 +12,7 @@ trait SendCursorPositionPubMsgHdlr extends RightsManagementTrait {
def handle(msg: SendCursorPositionPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: SendCursorPositionPubMsg): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(SendCursorPositionEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(SendCursorPositionEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
@ -30,7 +30,7 @@ trait SendCursorPositionPubMsgHdlr extends RightsManagementTrait {
//PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
} else {
broadcastEvent(msg)
PresPageCursorDAO.insertOrUpdate(msg.body.whiteboardId, msg.header.userId, msg.body.xPercent, msg.body.yPercent)
PresPageCursorDAO.insertOrUpdate(msg.body.whiteboardId, liveMeeting.props.meetingProp.intId, msg.header.userId, msg.body.xPercent, msg.body.yPercent)
}
}
}

Some files were not shown because too many files have changed in this diff Show More