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

View File

@ -48,7 +48,7 @@ updates:
# upcoming release branch # upcoming release branch
## excluding bigbluebutton-tests/playwright, bigbluebutton-tests/puppeteer, docs, bbb-graphql-client-test ## excluding bigbluebutton-tests/playwright, bigbluebutton-tests/puppeteer, docs, bbb-graphql-client-test
- package-ecosystem: npm - package-ecosystem: npm
directory: "/bbb-graphql-actions-adapter-server" directory: "/bbb-graphql-actions"
target-branch: "v3.0.x-release" target-branch: "v3.0.x-release"
schedule: schedule:
interval: daily 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 build-list: bbb-playback bbb-playback-notes bbb-playback-podcast bbb-playback-presentation bbb-playback-screenshare bbb-playback-video bbb-record-core
- package: bbb-graphql-server - package: bbb-graphql-server
build-name: bbb-graphql-server build-name: bbb-graphql-server
build-list: bbb-graphql-server bbb-graphql-middleware bbb-graphql-actions-adapter-server build-list: bbb-graphql-server bbb-graphql-middleware bbb-graphql-actions
- package: bbb-etherpad - package: bbb-etherpad
cache-files-list: bbb-etherpad.placeholder.sh cache-files-list: bbb-etherpad.placeholder.sh
cache-urls-list: https://api.github.com/repos/mconf/ep_pad_ttl/commits https://api.github.com/repos/alangecker/bbb-etherpad-plugin/commits https://api.github.com/repos/mconf/ep_redis_publisher/commits https://api.github.com/repos/alangecker/bbb-etherpad-skin/commits cache-urls-list: https://api.github.com/repos/mconf/ep_pad_ttl/commits https://api.github.com/repos/alangecker/bbb-etherpad-plugin/commits https://api.github.com/repos/mconf/ep_redis_publisher/commits https://api.github.com/repos/alangecker/bbb-etherpad-skin/commits
@ -75,7 +75,7 @@ jobs:
- package: others - package: others
build-list: bbb-mkclean bbb-pads bbb-libreoffice-docker bbb-transcription-controller bigbluebutton bbb-livekit build-list: bbb-mkclean bbb-pads bbb-libreoffice-docker bbb-transcription-controller bigbluebutton bbb-livekit
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Merge branches - name: Merge branches
uses: ./.github/actions/merge-branches uses: ./.github/actions/merge-branches
- name: Set cache-key vars - name: Set cache-key vars
@ -90,7 +90,7 @@ jobs:
- name: Handle cache - name: Handle cache
if: matrix.cache-files-list != '' if: matrix.cache-files-list != ''
id: cache-action id: cache-action
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: artifacts.tar path: artifacts.tar
key: ${{ runner.os }}-${{ matrix.package }}-${{ env.BIGBLUEBUTTON_RELEASE }}-commits-${{ env.CACHE_KEY_FILES }}-urls-${{ env.CACHE_KEY_URLS }} 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 echo "${{ matrix.build-list || matrix.package }}" | xargs -n 1 ./build/setup.sh
tar cvf artifacts.tar artifacts/ tar cvf artifacts.tar artifacts/
- name: Archive packages - name: Archive packages
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: artifacts_${{ matrix.package }}.tar name: artifacts_${{ matrix.package }}.tar
path: artifacts.tar path: artifacts.tar
@ -112,74 +112,76 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: 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: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Merge branches - name: Merge branches
uses: ./.github/actions/merge-branches uses: ./.github/actions/merge-branches
- run: ./build/get_external_dependencies.sh - run: ./build/get_external_dependencies.sh
- name: Download artifacts_bbb-apps-akka - name: Download artifacts_bbb-apps-akka
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: artifacts_bbb-apps-akka.tar name: artifacts_bbb-apps-akka.tar
- run: tar xf artifacts.tar - run: tar xf artifacts.tar
- name: Download artifacts_bbb-config - name: Download artifacts_bbb-config
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: artifacts_bbb-config.tar name: artifacts_bbb-config.tar
- run: tar xf artifacts.tar - run: tar xf artifacts.tar
- name: Download artifacts_bbb-export-annotations - name: Download artifacts_bbb-export-annotations
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: artifacts_bbb-export-annotations.tar name: artifacts_bbb-export-annotations.tar
- run: tar xf artifacts.tar - run: tar xf artifacts.tar
- name: Download artifacts_bbb-learning-dashboard - name: Download artifacts_bbb-learning-dashboard
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: artifacts_bbb-learning-dashboard.tar name: artifacts_bbb-learning-dashboard.tar
- run: tar xf artifacts.tar - run: tar xf artifacts.tar
- name: Download artifacts_bbb-playback-record - name: Download artifacts_bbb-playback-record
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: artifacts_bbb-playback-record.tar name: artifacts_bbb-playback-record.tar
- run: tar xf artifacts.tar - run: tar xf artifacts.tar
- name: Download artifacts_bbb-graphql-server - name: Download artifacts_bbb-graphql-server
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: artifacts_bbb-graphql-server.tar name: artifacts_bbb-graphql-server.tar
- run: tar xf artifacts.tar - run: tar xf artifacts.tar
- name: Download artifacts_bbb-etherpad - name: Download artifacts_bbb-etherpad
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: artifacts_bbb-etherpad.tar name: artifacts_bbb-etherpad.tar
- run: tar xf artifacts.tar - run: tar xf artifacts.tar
- name: Download artifacts_bbb-freeswitch - name: Download artifacts_bbb-freeswitch
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: artifacts_bbb-freeswitch.tar name: artifacts_bbb-freeswitch.tar
- run: tar xf artifacts.tar - run: tar xf artifacts.tar
- name: Download artifacts_bbb-webrtc - name: Download artifacts_bbb-webrtc
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: artifacts_bbb-webrtc.tar name: artifacts_bbb-webrtc.tar
- run: tar xf artifacts.tar - run: tar xf artifacts.tar
- name: Download artifacts_bbb-web - name: Download artifacts_bbb-web
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: artifacts_bbb-web.tar name: artifacts_bbb-web.tar
- run: tar xf artifacts.tar - run: tar xf artifacts.tar
- name: Download artifacts_bbb-fsesl-akka - name: Download artifacts_bbb-fsesl-akka
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: artifacts_bbb-fsesl-akka.tar name: artifacts_bbb-fsesl-akka.tar
- run: tar xf artifacts.tar - run: tar xf artifacts.tar
- name: Download artifacts_bbb-html5 - name: Download artifacts_bbb-html5
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: artifacts_bbb-html5.tar name: artifacts_bbb-html5.tar
- run: tar xf artifacts.tar - run: tar xf artifacts.tar
- name: Download artifacts - name: Download artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: artifacts_others.tar name: artifacts_others.tar
- run: tar xf artifacts.tar - run: tar xf artifacts.tar
@ -253,14 +255,19 @@ jobs:
apt --purge -y remove apache2-bin apt --purge -y remove apache2-bin
' '
- name: Install BBB - name: Install BBB
timeout-minutes: 25 uses: nick-fields/retry@v3
run: | 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 sudo -i <<EOF
set -e set -e
cd /root/ && wget -nv https://raw.githubusercontent.com/bigbluebutton/bbb-install/v3.0.x-release/bbb-install.sh -O bbb-install.sh 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/ 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 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 sed -i "s/\"minify\": true,/\"minify\": false,/" /usr/share/etherpad-lite/settings.json
bbb-conf --restart bbb-conf --restart
EOF EOF
@ -279,13 +286,13 @@ jobs:
npx playwright install npx playwright install
' '
- name: Run tests - name: Run tests
uses: nick-fields/retry@v2 uses: nick-fields/retry@v3
with: with:
timeout_minutes: 25 timeout_minutes: 25
max_attempts: 3 max_attempts: 2
command: | command: |
cd ./bigbluebutton-tests/playwright cd ./bigbluebutton-tests/playwright
npm run test-chromium-ci -- --shard ${{ matrix.shard }} npm run test-chromium-ci -- --shard ${{ env.shard }}
env: env:
NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/bbb-dev/bbb-dev-ca.crt
ACTIONS_RUNNER_DEBUG: true ACTIONS_RUNNER_DEBUG: true
@ -305,13 +312,13 @@ jobs:
run: | run: |
sh -c ' 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 {} \; 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' - if: always() && github.event_name == 'pull_request'
name: Upload blob report to GitHub Actions Artifacts name: Upload blob report to GitHub Actions Artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: all-blob-reports name: blob-report-${{ matrix.shard }}
path: bigbluebutton-tests/playwright/blob-report path: bigbluebutton-tests/playwright/blob-report
- if: failure() - if: failure()
name: Prepare artifacts (configs and logs) name: Prepare artifacts (configs and logs)
@ -337,42 +344,56 @@ jobs:
chmod a+r -R /home/runner/work/bigbluebutton/bigbluebutton/configs chmod a+r -R /home/runner/work/bigbluebutton/bigbluebutton/configs
bbb-conf --zip bbb-conf --zip
ls -t /root/*.tar.gz | head -1 | xargs -I '{}' cp '{}' /home/runner/work/bigbluebutton/bigbluebutton/bbb-logs.tar.gz 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 EOF
- if: failure() - if: failure()
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: bbb-configs-${{ env.MATRIX_SHARD }} name: bbb-configs-${{ env.MATRIX_SHARD }}
path: configs path: configs
- if: failure() - if: failure()
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: bbb-logs-${{ env.MATRIX_SHARD }} name: bbb-logs-${{ env.MATRIX_SHARD }}
path: ./bbb-logs.tar.gz path: ./bbb-logs.tar.gz
upload-report: upload-report:
if: always() if: always() && !contains(github.event.head_commit.message, 'Merge pull request')
needs: install-and-run-tests needs: install-and-run-tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
hasReportData: ${{ needs.install-and-run-tests.result == 'success' || needs.install-and-run-tests.result == 'failure' }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
- name: Install dependencies - name: Install dependencies
if: ${{ env.hasReportData }}
working-directory: ./bigbluebutton-tests/playwright working-directory: ./bigbluebutton-tests/playwright
run: npm ci run: npm ci
- name: Download all blob reports from GitHub Actions Artifacts - name: Merge artifacts
uses: actions/download-artifact@v3 uses: actions/upload-artifact/merge@v4
with: with:
name: all-blob-reports 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 path: bigbluebutton-tests/playwright/all-blob-reports
merge-multiple: true
- name: Merge into HTML Report - name: Merge into HTML Report
if: ${{ env.hasReportData }}
working-directory: ./bigbluebutton-tests/playwright working-directory: ./bigbluebutton-tests/playwright
run: npx playwright merge-reports --reporter html ./all-blob-reports run: npx playwright merge-reports --reporter html ./all-blob-reports
- name: Upload HTML tests report - name: Upload HTML tests report
uses: actions/upload-artifact@v3 if: ${{ env.hasReportData }}
uses: actions/upload-artifact@v4
with: with:
name: tests-report name: tests-report
overwrite: true
path: | path: |
bigbluebutton-tests/playwright/playwright-report bigbluebutton-tests/playwright/playwright-report
bigbluebutton-tests/playwright/test-results bigbluebutton-tests/playwright/test-results
@ -385,7 +406,7 @@ jobs:
echo ${{ github.run_id }} > ./pr-comment-data/workflow_id echo ${{ github.run_id }} > ./pr-comment-data/workflow_id
- name: Upload PR data for auto-comment - name: Upload PR data for auto-comment
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: pr-comment-data name: pr-comment-data
path: pr-comment-data path: pr-comment-data

View File

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

View File

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

View File

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

View File

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

2
.gitignore vendored
View File

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

View File

@ -17,7 +17,7 @@ As such, we recommend that all administrators deploy 2.7 going forward. You'll
## Reporting a Vulnerability ## 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 - 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. - 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 daemonGroup in Linux := group
javaOptions in Universal ++= Seq("-J-Xms130m", "-J-Xmx256m", "-Dconfig.file=/etc/bigbluebutton/bbb-apps-akka.conf", "-Dlogback.configurationFile=conf/logback.xml") javaOptions in Universal ++= Seq("-J-Xms130m", "-J-Xmx256m", "-Dconfig.file=/etc/bigbluebutton/bbb-apps-akka.conf", "-Dlogback.configurationFile=conf/logback.xml")
javaOptions in reStart ++= Seq("-Dconfig.file=/etc/bigbluebutton/bbb-apps-akka.conf", "-Dlogback.configurationFile=conf/logback.xml")
debianPackageDependencies in Debian ++= Seq("java17-runtime-headless", "bash") debianPackageDependencies in Debian ++= Seq("java17-runtime-headless", "bash")

View File

@ -16,7 +16,7 @@ object Dependencies {
val pekkoHttpVersion = "1.0.0" val pekkoHttpVersion = "1.0.0"
val gson = "2.8.9" val gson = "2.8.9"
val jackson = "2.13.5" val jackson = "2.13.5"
val logback = "1.2.11" val logback = "1.2.13"
val quicklens = "1.7.5" val quicklens = "1.7.5"
val spray = "1.3.6" val spray = "1.3.6"
@ -68,7 +68,6 @@ object Dependencies {
val postgresql = "org.postgresql" % "postgresql" % Versions.postgresql val postgresql = "org.postgresql" % "postgresql" % Versions.postgresql
val jacksonDataFormat = "com.fasterxml.jackson.dataformat" % "jackson-dataformat-yaml" % Versions.jacksonDataFormat val jacksonDataFormat = "com.fasterxml.jackson.dataformat" % "jackson-dataformat-yaml" % Versions.jacksonDataFormat
val snakeYaml = "org.yaml" % "snakeyaml"
} }
object Test { object Test {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -95,7 +95,7 @@ object PermissionCheck extends SystemConfiguration {
for { for {
regUser <- RegisteredUsers.findWithUserId(userId, liveMeeting.registeredUsers) regUser <- RegisteredUsers.findWithUserId(userId, liveMeeting.registeredUsers)
} yield { } 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 { } else {
// TODO: get this object a context so it can use the akka logging system // 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 = { def setRunning(model: TimerModel, running: Boolean): Unit = {
//If it is running and will stop, calculate new Accumulated
if(getRunning(model) && !running) {
val now = System.currentTimeMillis()
val accumulated = getAccumulated(model) + Math.abs(now - getStartedAt(model)).toInt
this.setAccumulated(model, accumulated)
}
model.running = running model.running = running
} }

View File

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

View File

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

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

View File

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

View File

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

View File

@ -53,8 +53,8 @@ trait BreakoutRoomUsersUpdateMsgHdlr {
breakoutRoomUser <- updatedRoom.users breakoutRoomUser <- updatedRoom.users
u <- RegisteredUsers.findWithBreakoutRoomId(breakoutRoomUser.id, liveMeeting.registeredUsers) u <- RegisteredUsers.findWithBreakoutRoomId(breakoutRoomUser.id, liveMeeting.registeredUsers)
} yield u.id } yield u.id
UserBreakoutRoomDAO.updateLastBreakoutRoom(usersInRoom, updatedRoom) UserBreakoutRoomDAO.updateLastBreakoutRoom(props.meetingProp.intId, usersInRoom, updatedRoom)
BreakoutRoomUserDAO.updateUserJoined(usersInRoom, updatedRoom) BreakoutRoomUserDAO.updateUserJoined(props.meetingProp.intId, usersInRoom, updatedRoom)
model.update(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.breakout.BreakoutHdlrHelpers.getRedirectUrls
import org.bigbluebutton.core.apps.{PermissionCheck, RightsManagementTrait} import org.bigbluebutton.core.apps.{PermissionCheck, RightsManagementTrait}
import org.bigbluebutton.core.bus.BigBlueButtonEvent import org.bigbluebutton.core.bus.BigBlueButtonEvent
import org.bigbluebutton.core.db.BreakoutRoomUserDAO import org.bigbluebutton.core.db.{BreakoutRoomUserDAO, NotificationDAO}
import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.EjectReasonCode import org.bigbluebutton.core.models.EjectReasonCode
import org.bigbluebutton.core.running.{MeetingActor, OutMsgRouter} import org.bigbluebutton.core.running.{MeetingActor, OutMsgRouter}
@ -57,7 +57,7 @@ trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
) )
//Update database //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 //Send notification to moved User
@ -75,6 +75,7 @@ trait ChangeUserBreakoutReqMsgHdlr extends RightsManagementTrait {
Vector(roomTo.shortName) Vector(roomTo.shortName)
) )
outGW.send(notifyUserEvent) outGW.send(notifyUserEvent)
NotificationDAO.insert(notifyUserEvent)
} }
} }

View File

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

View File

@ -30,13 +30,13 @@ trait EjectUserFromBreakoutInternalMsgHdlr {
) )
//TODO inform reason //TODO inform reason
UserDAO.delete(registeredUser.id) UserDAO.softDelete(registeredUser.meetingId, registeredUser.id)
// send a system message to force disconnection // send a system message to force disconnection
Sender.sendDisconnectClientSysMsg(msg.breakoutId, registeredUser.id, msg.ejectedBy, msg.reasonCode, outGW) Sender.sendDisconnectClientSysMsg(msg.breakoutId, registeredUser.id, msg.ejectedBy, msg.reasonCode, outGW)
// Force reconnection with graphql to refresh permissions // 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 //send users update to parent meeting
BreakoutHdlrHelpers.updateParentMeetingWithUsers(liveMeeting, eventBus) 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.domain.{ MeetingEndReason, MeetingState2x }
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter } import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait } 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.core2.message.senders.MsgBuilder
import org.bigbluebutton.core.db.BreakoutRoomDAO
trait EndAllBreakoutRoomsMsgHdlr extends RightsManagementTrait { trait EndAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
this: MeetingActor => this: MeetingActor =>
@ -22,13 +21,7 @@ trait EndAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting) PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
state state
} else { } else {
for { endAllBreakoutRooms(eventBus, liveMeeting, state, MeetingEndReason.BREAKOUT_ENDED_BY_MOD)
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)
}
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg( val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
meetingId, meetingId,
@ -39,7 +32,8 @@ trait EndAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
Vector() Vector()
) )
outGW.send(notifyEvent) outGW.send(notifyEvent)
} NotificationDAO.insert(notifyEvent)
BreakoutRoomDAO.updateRoomsEnded(meetingId) BreakoutRoomDAO.updateRoomsEnded(meetingId)
state.update(None) state.update(None)
} }

View File

@ -1,7 +1,10 @@
package org.bigbluebutton.core.apps.breakout 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.bus.{ BigBlueButtonEvent, InternalEventBus }
import org.bigbluebutton.core.models.Pads
import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting, OutMsgRouter } import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting, OutMsgRouter }
trait EndBreakoutRoomInternalMsgHdlr extends HandlerHelpers { trait EndBreakoutRoomInternalMsgHdlr extends HandlerHelpers {
@ -19,12 +22,68 @@ trait EndBreakoutRoomInternalMsgHdlr extends HandlerHelpers {
} }
if (liveMeeting.props.breakoutProps.captureNotes) { if (liveMeeting.props.breakoutProps.captureNotes) {
for {
group <- Pads.getGroup(liveMeeting.pads, "notes")
} yield {
val filename = liveMeeting.props.breakoutProps.captureNotesFilename val filename = liveMeeting.props.breakoutProps.captureNotesFilename
val captureNotesEvent = BigBlueButtonEvent(msg.parentId, CaptureSharedNotesReqInternalMsg(msg.breakoutId, filename)) val userId: String = "system"
eventBus.publish(captureNotesEvent) 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) log.info("Breakout room {} ended by parent meeting {}.", msg.breakoutId, msg.parentId)
sendEndMeetingDueToExpiry(msg.reason, eventBus, outGW, liveMeeting, "system") 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 package org.bigbluebutton.core.apps.breakout
import org.bigbluebutton.common2.msgs._ 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.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.bus.BigBlueButtonEvent import org.bigbluebutton.core.bus.BigBlueButtonEvent
import org.bigbluebutton.core.db.NotificationDAO
import org.bigbluebutton.core.domain.MeetingState2x 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.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core2.message.senders.{ MsgBuilder } import org.bigbluebutton.core2.message.senders.MsgBuilder
trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait { trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
this: MeetingActor => this: MeetingActor =>
@ -32,7 +33,7 @@ trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
val event = buildSendMessageToAllBreakoutRoomsEvtMsg(msg.header.userId, msg.body.msg, breakoutModel.rooms.size) val event = buildSendMessageToAllBreakoutRoomsEvtMsg(msg.header.userId, msg.body.msg, breakoutModel.rooms.size)
outGW.send(event) outGW.send(event)
val notifyModeratorEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg( val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
msg.header.userId, msg.header.userId,
liveMeeting.props.meetingProp.intId, liveMeeting.props.meetingProp.intId,
"info", "info",
@ -41,7 +42,8 @@ trait SendMessageToAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
"Message for chat sent successfully", "Message for chat sent successfully",
Vector(s"${breakoutModel.rooms.size}") 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) 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) sender <- GroupChatApp.findGroupChatUser(SystemUser.ID, liveMeeting.users2x)
chat <- state.groupChats.find(GroupChatApp.MAIN_PUBLIC_CHAT) chat <- state.groupChats.find(GroupChatApp.MAIN_PUBLIC_CHAT)
} yield { } yield {
val groupChatMsgFromUser = GroupChatMsgFromUser(sender.id, sender.copy(name = msg.senderName), true, msg.msg) val groupChatMsgFromUser = GroupChatMsgFromUser(sender.id, sender.copy(name = msg.senderName), msg.msg)
val gcm = GroupChatApp.toGroupChatMessage(sender.copy(name = msg.senderName), groupChatMsgFromUser) val gcm = GroupChatApp.toGroupChatMessage(sender.copy(name = msg.senderName), groupChatMsgFromUser, emphasizedText = true)
val gcs = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm, GroupChatMessageType.BREAKOUTROOM_MOD_MSG) val gcs = GroupChatApp.addGroupChatMessage(liveMeeting.props.meetingProp.intId, chat, state.groupChats, gcm, GroupChatMessageType.BREAKOUTROOM_MOD_MSG)
val event = buildGroupChatMessageBroadcastEvtMsg( val event = buildGroupChatMessageBroadcastEvtMsg(

View File

@ -2,6 +2,7 @@ package org.bigbluebutton.core.apps.breakout
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.{ BreakoutModel, PermissionCheck, RightsManagementTrait } import org.bigbluebutton.core.apps.{ BreakoutModel, PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.db.UserDAO
import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.VoiceUsers import org.bigbluebutton.core.models.VoiceUsers
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter } 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." val reason = "No permission to transfer user to voice breakout."
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting) PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
} else { } else {
processRequest(msg) processTransferUserToMeetingRequest(msg)
} }
state state
} }
def processRequest(msg: TransferUserToMeetingRequestMsg) { def processTransferUserToMeetingRequest(msg: TransferUserToMeetingRequestMsg) {
if (msg.body.fromMeetingId == liveMeeting.props.meetingProp.intId) { if (msg.body.fromMeetingId == liveMeeting.props.meetingProp.intId) {
// want to transfer from parent meeting to breakout // want to transfer from parent meeting to breakout
for { for {
@ -32,6 +33,7 @@ trait TransferUserToMeetingRequestHdlr extends RightsManagementTrait {
from <- getVoiceConf(msg.body.fromMeetingId, model) from <- getVoiceConf(msg.body.fromMeetingId, model)
voiceUser <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId) voiceUser <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId)
} yield { } yield {
UserDAO.transferUserToBreakoutRoomAsAudioOnly(msg.body.userId, msg.body.fromMeetingId, msg.body.toMeetingId)
val event = buildTransferUserToVoiceConfSysMsg(from, to, voiceUser.voiceUserId) val event = buildTransferUserToVoiceConfSysMsg(from, to, voiceUser.voiceUserId)
outGW.send(event) outGW.send(event)
} }
@ -53,6 +55,7 @@ trait TransferUserToMeetingRequestHdlr extends RightsManagementTrait {
room <- model.find(msg.body.fromMeetingId) room <- model.find(msg.body.fromMeetingId)
voiceUser <- room.voiceUsers.find(p => p.id == msg.body.userId) voiceUser <- room.voiceUsers.find(p => p.id == msg.body.userId)
} yield { } yield {
UserDAO.transferUserToBreakoutRoomAsAudioOnly(msg.body.userId, msg.body.fromMeetingId, msg.body.toMeetingId)
val event = buildTransferUserToVoiceConfSysMsg(from, to, voiceUser.voiceUserId) val event = buildTransferUserToVoiceConfSysMsg(from, to, voiceUser.voiceUserId)
outGW.send(event) 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.api.{ SendTimeRemainingAuditInternalMsg, UpdateBreakoutRoomTimeInternalMsg }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait } import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.bus.BigBlueButtonEvent import org.bigbluebutton.core.bus.BigBlueButtonEvent
import org.bigbluebutton.core.db.{ BreakoutRoomDAO, MeetingDAO } import org.bigbluebutton.core.db.{ BreakoutRoomDAO, MeetingDAO, NotificationDAO }
import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter } import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender } import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
@ -63,9 +63,10 @@ trait UpdateBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
Vector(s"${msg.body.timeInMinutes}") Vector(s"${msg.body.timeInMinutes}")
) )
outGW.send(notifyEvent) outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
} }
val notifyModeratorEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg( val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
msg.header.userId, msg.header.userId,
liveMeeting.props.meetingProp.intId, liveMeeting.props.meetingProp.intId,
"info", "info",
@ -74,7 +75,8 @@ trait UpdateBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
"Sent to the moderator that requested breakout duration change", "Sent to the moderator that requested breakout duration change",
Vector(s"${msg.body.timeInMinutes}") 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) log.debug("Updating {} minutes for breakout rooms time in meeting {}", msg.body.timeInMinutes, props.meetingProp.intId)
BreakoutRoomDAO.updateRoomsDuration(props.meetingProp.intId, newDurationInSeconds) BreakoutRoomDAO.updateRoomsDuration(props.meetingProp.intId, newDurationInSeconds)

View File

@ -4,8 +4,9 @@ import org.apache.pekko.actor.ActorContext
import org.apache.pekko.event.Logging import org.apache.pekko.event.Logging
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.running.{ LiveMeeting } import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait } import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.db.{ CaptionLocaleDAO, CaptionTypes }
class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementTrait { class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementTrait {
val log = Logging(context.system, getClass) val log = Logging(context.system, getClass)
@ -84,6 +85,7 @@ class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementT
val event = UpdateCaptionOwnerEvtMsg(header, body) val event = UpdateCaptionOwnerEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event) val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent) 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)) { 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 { val newState = for {
createdBy <- GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x) createdBy <- GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x)
} yield { } yield {
val msgs = msg.body.msg.map(m => GroupChatApp.toGroupChatMessage(createdBy, m)) val msgs = msg.body.msg.map(m => GroupChatApp.toGroupChatMessage(createdBy, m, emphasizedText = false))
val users = { val users = {
if (msg.body.access == GroupChatAccess.PRIVATE) { if (msg.body.access == GroupChatAccess.PRIVATE) {
val cu = msg.body.users.toSet + msg.header.userId val cu = msg.body.users.toSet + msg.header.userId

View File

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

View File

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

View File

@ -11,7 +11,7 @@ trait SyncGetGroupChatsInfoMsgHdlr {
def handleSyncGetGroupChatsInfo(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = { def handleSyncGetGroupChatsInfo(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
def buildSyncGetGroupChatsRespMsg(allChats: Vector[GroupChatInfo]): BbbCommonEnvCoreMsg = { 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 envelope = BbbCoreEnvelope(SyncGetGroupChatsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetGroupChatsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp") val header = BbbClientMsgHeader(SyncGetGroupChatsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val body = SyncGetGroupChatsRespMsgBody(allChats) val body = SyncGetGroupChatsRespMsgBody(allChats)
@ -21,7 +21,7 @@ trait SyncGetGroupChatsInfoMsgHdlr {
} }
def buildSyncGetGroupChatMsgsRespMsg(msgs: Vector[GroupChatMsgToUser], chatId: String): BbbCommonEnvCoreMsg = { 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 envelope = BbbCoreEnvelope(SyncGetGroupChatMsgsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetGroupChatMsgsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp") val header = BbbClientMsgHeader(SyncGetGroupChatMsgsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val body = SyncGetGroupChatMsgsRespMsgBody(chatId, msgs) 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.core.running.OutMsgRouter
import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait } import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.db.LayoutDAO import org.bigbluebutton.core.db.{ LayoutDAO, NotificationDAO }
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait BroadcastLayoutMsgHdlr extends RightsManagementTrait { trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
this: LayoutApp2x => this: LayoutApp2x =>
@ -60,5 +61,19 @@ trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
val msgEvent = BbbCommonEnvCoreMsg(envelope, event) val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent) outGW.send(msgEvent)
if (body.pushLayout) {
val notifyEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
fromUserId,
liveMeeting.props.meetingProp.intId,
"info",
"user",
"app.layoutUpdate.label",
"Notification to when the presenter changes size of cams",
Vector()
)
outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
}
} }
} }

View File

@ -9,7 +9,7 @@ trait SyncGetMeetingInfoRespMsgHdlr {
val outGW: OutMsgRouter val outGW: OutMsgRouter
def handleSyncGetMeetingInfoRespMsg(props: DefaultProps): Unit = { 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 envelope = BbbCoreEnvelope(SyncGetMeetingInfoRespMsg.NAME, routing)
val header = BbbCoreBaseHeader(SyncGetMeetingInfoRespMsg.NAME) val header = BbbCoreBaseHeader(SyncGetMeetingInfoRespMsg.NAME)

View File

@ -10,17 +10,6 @@ trait PadCreateGroupReqMsgHdlr {
def handle(msg: PadCreateGroupReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = { 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 { val padEnabled = msg.body.model match {
case "notes" => !liveMeeting.props.meetingProp.disabledFeatures.contains("sharedNotes") case "notes" => !liveMeeting.props.meetingProp.disabledFeatures.contains("sharedNotes")
case "captions" => !liveMeeting.props.meetingProp.disabledFeatures.contains("captions") case "captions" => !liveMeeting.props.meetingProp.disabledFeatures.contains("captions")
@ -29,7 +18,7 @@ trait PadCreateGroupReqMsgHdlr {
if (padEnabled && !Pads.hasGroup(liveMeeting.pads, msg.body.externalId)) { 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) 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 { Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
case Some(group) => { 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) SharedNotesDAO.insert(liveMeeting.props.meetingProp.intId, group, msg.body.padId, msg.body.name)
broadcastEvent(group.externalId, group.userId, 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 => this: PadsApp2x =>
def handle(msg: PadGroupCreatedEvtMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = { 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 { Pads.getGroup(liveMeeting.pads, msg.body.externalId) match {
case Some(group) => { case Some(group) => {
Pads.setGroupId(liveMeeting.pads, msg.body.externalId, msg.body.groupId) 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 _ => case _ =>
} }

View File

@ -24,7 +24,7 @@ trait PadSessionDeletedSysMsgHdlr {
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match { Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
case Some(group) => { 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) broadcastEvent(group.externalId, msg.body.userId, msg.body.sessionId)
} }
case _ => case _ =>

View File

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

View File

@ -24,6 +24,7 @@ trait PadUpdatedSysMsgHdlr {
Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match { Pads.getGroupById(liveMeeting.pads, msg.body.groupId) match {
case Some(group) => { 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) 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) 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 package org.bigbluebutton.core.apps.plugin
import org.bigbluebutton.common2.msgs.PluginDataChannelPushEntryMsg
import org.bigbluebutton.ClientSettings import org.bigbluebutton.ClientSettings
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.db.PluginDataChannelEntryDAO
import org.bigbluebutton.core.db.{ PluginDataChannelMessageDAO }
import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.{ Roles, Users2x } import org.bigbluebutton.core.models.{ Roles, Users2x }
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting } import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting }
trait DispatchPluginDataChannelMessageMsgHdlr extends HandlerHelpers { trait 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 pluginsDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("plugins")
val meetingId = liveMeeting.props.meetingProp.intId val meetingId = liveMeeting.props.meetingProp.intId
@ -21,11 +21,11 @@ trait DispatchPluginDataChannelMessageMsgHdlr extends HandlerHelpers {
if (!pluginsConfig.contains(msg.body.pluginName)) { if (!pluginsConfig.contains(msg.body.pluginName)) {
println(s"Plugin '${msg.body.pluginName}' not found.") println(s"Plugin '${msg.body.pluginName}' not found.")
} else if (!pluginsConfig(msg.body.pluginName).dataChannels.contains(msg.body.dataChannel)) { } else if (!pluginsConfig(msg.body.pluginName).dataChannels.contains(msg.body.channelName)) {
println(s"Data channel '${msg.body.dataChannel}' not found in plugin '${msg.body.pluginName}'.") println(s"Data channel '${msg.body.channelName}' not found in plugin '${msg.body.pluginName}'.")
} else { } else {
val hasPermission = for { val hasPermission = for {
writePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.dataChannel).writePermission writePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).writePermission
} yield { } yield {
writePermission.toLowerCase match { writePermission.toLowerCase match {
case "all" => true case "all" => true
@ -36,12 +36,13 @@ trait DispatchPluginDataChannelMessageMsgHdlr extends HandlerHelpers {
} }
if (!hasPermission.contains(true)) { 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 { } else {
PluginDataChannelMessageDAO.insert( PluginDataChannelEntryDAO.insert(
meetingId, meetingId,
msg.body.pluginName, msg.body.pluginName,
msg.body.dataChannel, msg.body.channelName,
msg.body.subChannelName,
msg.header.userId, msg.header.userId,
msg.body.payloadJson, msg.body.payloadJson,
msg.body.toRoles, 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.actor.ActorContext
import org.apache.pekko.event.Logging import org.apache.pekko.event.Logging
import org.bigbluebutton.common2.msgs.PluginDataChannelDeleteEntryMsgBody
class PluginHdlrs(implicit val context: ActorContext) class PluginHdlrs(implicit val context: ActorContext)
extends DispatchPluginDataChannelMessageMsgHdlr { extends PluginDataChannelPushEntryMsgHdlr
with PluginDataChannelDeleteEntryMsgHdlr
with PluginDataChannelResetMsgHdlr {
val log = Logging(context.system, getClass) 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.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.models.Polls import org.bigbluebutton.core.models.Polls
import org.bigbluebutton.core.running.{ LiveMeeting } import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core.models.Users2x import org.bigbluebutton.core.models.Users2x
trait RespondToPollReqMsgHdlr { trait RespondToPollReqMsgHdlr {
@ -12,45 +12,12 @@ trait RespondToPollReqMsgHdlr {
def handle(msg: RespondToPollReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = { def handle(msg: RespondToPollReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastPollUpdatedEvent(msg: RespondToPollReqMsg, pollId: String, poll: SimplePollResultOutVO): Unit = { if (!Polls.hasUserAlreadyResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls)) {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(PollUpdatedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(PollUpdatedEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
val body = PollUpdatedEvtMsgBody(pollId, poll)
val event = PollUpdatedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
def broadcastUserRespondedToPollRecordMsg(msg: RespondToPollReqMsg, pollId: String, answerId: Int, answer: String, isSecret: Boolean): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(UserRespondedToPollRecordMsg.NAME, routing)
val header = BbbClientMsgHeader(UserRespondedToPollRecordMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
val body = UserRespondedToPollRecordMsgBody(pollId, answerId, answer, isSecret)
val event = UserRespondedToPollRecordMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
def broadcastUserRespondedToPollRespMsg(msg: RespondToPollReqMsg, pollId: String, answerIds: Seq[Int], sendToId: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, sendToId)
val envelope = BbbCoreEnvelope(UserRespondedToPollRespMsg.NAME, routing)
val header = BbbClientMsgHeader(UserRespondedToPollRespMsg.NAME, liveMeeting.props.meetingProp.intId, sendToId)
val body = UserRespondedToPollRespMsgBody(pollId, msg.header.userId, answerIds)
val event = UserRespondedToPollRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
if (Polls.checkUserResponded(msg.body.pollId, msg.header.userId, liveMeeting.polls) == false) {
for { for {
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToPollReqMsg(msg.header.userId, msg.body.pollId, (pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToPollReqMsg(msg.header.userId, msg.body.pollId,
msg.body.questionId, msg.body.answerIds, liveMeeting) msg.body.questionId, msg.body.answerIds, liveMeeting)
} yield { } yield {
broadcastPollUpdatedEvent(msg, pollId, updatedPoll) PollHdlrHelpers.broadcastPollUpdatedEvent(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, updatedPoll)
for { for {
poll <- Polls.getPoll(pollId, liveMeeting.polls) poll <- Polls.getPoll(pollId, liveMeeting.polls)
} yield { } yield {
@ -58,14 +25,14 @@ trait RespondToPollReqMsgHdlr {
answerId <- msg.body.answerIds answerId <- msg.body.answerIds
} yield { } yield {
val answerText = poll.questions(0).answers.get(answerId).key val answerText = poll.questions(0).answers.get(answerId).key
broadcastUserRespondedToPollRecordMsg(msg, pollId, answerId, answerText, poll.isSecret) PollHdlrHelpers.broadcastUserRespondedToPollRecordMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, answerId, answerText, poll.isSecret)
} }
} }
for { for {
presenter <- Users2x.findPresenter(liveMeeting.users2x) presenter <- Users2x.findPresenter(liveMeeting.users2x)
} yield { } yield {
broadcastUserRespondedToPollRespMsg(msg, pollId, msg.body.answerIds, presenter.intId) PollHdlrHelpers.broadcastUserRespondedToPollRespMsg(bus.outGW, liveMeeting.props.meetingProp.intId, msg.header.userId, pollId, msg.body.answerIds, presenter.intId)
} }
} }
} else { } else {

View File

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

View File

@ -8,7 +8,7 @@ import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.Polls import org.bigbluebutton.core.models.Polls
import org.bigbluebutton.core.running.LiveMeeting import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core.apps.{PermissionCheck, RightsManagementTrait} import org.bigbluebutton.core.apps.{PermissionCheck, RightsManagementTrait}
import org.bigbluebutton.core.db.{ ChatMessageDAO, JsonUtils } import org.bigbluebutton.core.db.{ChatMessageDAO, JsonUtils, NotificationDAO}
import org.bigbluebutton.core2.message.senders.MsgBuilder import org.bigbluebutton.core2.message.senders.MsgBuilder
import spray.json.DefaultJsonProtocol.jsonFormat2 import spray.json.DefaultJsonProtocol.jsonFormat2
@ -37,6 +37,7 @@ trait ShowPollResultReqMsgHdlr extends RightsManagementTrait {
Vector() Vector()
) )
bus.outGW.send(notifyEvent) bus.outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
// SendWhiteboardAnnotationPubMsg // SendWhiteboardAnnotationPubMsg
val annotationRouting = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId) 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 package org.bigbluebutton.core.apps.presentationpod
import org.bigbluebutton.common2.msgs._ 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.groupchats.GroupChatApp
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait } import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.apps.presentationpod.PresentationSender
import org.bigbluebutton.core.bus.MessageBus import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.db.{ ChatMessageDAO, PresPresentationDAO } import org.bigbluebutton.core.db.{ ChatMessageDAO, PresPresentationDAO }
import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.domain.MeetingState2x
@ -46,7 +45,7 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
def buildNewPresFileAvailable(annotatedFileURI: String, originalFileURI: String, convertedFileURI: String, def buildNewPresFileAvailable(annotatedFileURI: String, originalFileURI: String, convertedFileURI: String,
presId: String, fileStateType: String): NewPresFileAvailableMsg = { presId: String, fileStateType: String): NewPresFileAvailableMsg = {
val header = BbbClientMsgHeader(NewPresFileAvailableMsg.NAME, "not-used", "not-used") val header = BbbClientMsgHeader(NewPresFileAvailableMsg.NAME, "not-used", "not-used")
val body = NewPresFileAvailableMsgBody(annotatedFileURI, originalFileURI, convertedFileURI, presId, fileStateType) val body = NewPresFileAvailableMsgBody(annotatedFileURI, originalFileURI, convertedFileURI, presId, fileStateType, "")
NewPresFileAvailableMsg(header, body) NewPresFileAvailableMsg(header, body)
} }
@ -160,7 +159,7 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
val pages: List[Int] = m.body.pages // Desired presentation pages for export val pages: List[Int] = m.body.pages // Desired presentation pages for export
val pagesRange: List[Int] = if (allPages) (1 to pageCount).toList else pages val pagesRange: List[Int] = if (allPages) (1 to pageCount).toList else pages
val exportJob: ExportJob = new ExportJob(jobId, JobTypes.DOWNLOAD, "annotated_slides", presId, presLocation, allPages, pagesRange, meetingId, ""); val exportJob: ExportJob = new ExportJob(jobId, JobTypes.DOWNLOAD, currentPres.get.name, "annotated_slides", presId, presLocation, allPages, pagesRange, meetingId, "");
val storeAnnotationPages: List[PresentationPageForExport] = getPresentationPagesForExport(pagesRange, pageCount, presId, currentPres, liveMeeting); val storeAnnotationPages: List[PresentationPageForExport] = getPresentationPagesForExport(pagesRange, pageCount, presId, currentPres, liveMeeting);
val isPresentationOriginalOrConverted = m.body.fileStateType == "Original" || m.body.fileStateType == "Converted" val isPresentationOriginalOrConverted = m.body.fileStateType == "Original" || m.body.fileStateType == "Converted"
@ -226,7 +225,7 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
val currentPage: PresentationPage = PresentationInPod.getCurrentPage(currentPres.get).get val currentPage: PresentationPage = PresentationInPod.getCurrentPage(currentPres.get).get
val pagesRange: List[Int] = if (allPages) (1 to pageCount).toList else List(currentPage.num) val pagesRange: List[Int] = if (allPages) (1 to pageCount).toList else List(currentPage.num)
val exportJob: ExportJob = ExportJob(jobId, JobTypes.CAPTURE_PRESENTATION, filename, presId, presLocation, allPages, pagesRange, parentMeetingId, presentationUploadToken) val exportJob: ExportJob = ExportJob(jobId, JobTypes.CAPTURE_PRESENTATION, filename, filename, presId, presLocation, allPages, pagesRange, parentMeetingId, presentationUploadToken)
val storeAnnotationPages: List[PresentationPageForExport] = getPresentationPagesForExport(pagesRange, pageCount, presId, currentPres, liveMeeting); val storeAnnotationPages: List[PresentationPageForExport] = getPresentationPagesForExport(pagesRange, pageCount, presId, currentPres, liveMeeting);
val annotationCount: Int = storeAnnotationPages.map(_.annotations.size).sum val annotationCount: Int = storeAnnotationPages.map(_.annotations.size).sum
@ -252,11 +251,10 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
liveMeeting.props.meetingProp.intId, m.body.presId liveMeeting.props.meetingProp.intId, m.body.presId
) )
//TODO let frontend choose the name in favor of internationalization
if (m.body.fileStateType == "Annotated") { if (m.body.fileStateType == "Annotated") {
val presentationDownloadInfo = Map( val presentationDownloadInfo = Map(
"fileURI" -> m.body.annotatedFileURI, "fileURI" -> m.body.annotatedFileURI,
"filename" -> "annotated_slides.pdf" "filename" -> m.body.fileName
) )
ChatMessageDAO.insertSystemMsg(liveMeeting.props.meetingProp.intId, GroupChatApp.MAIN_PUBLIC_CHAT, "", GroupChatMessageType.PRESENTATION, presentationDownloadInfo, "") ChatMessageDAO.insertSystemMsg(liveMeeting.props.meetingProp.intId, GroupChatApp.MAIN_PUBLIC_CHAT, "", GroupChatMessageType.PRESENTATION, presentationDownloadInfo, "")
} else if (m.body.fileStateType == "Converted") { } else if (m.body.fileStateType == "Converted") {
@ -269,35 +267,8 @@ trait MakePresentationDownloadReqMsgHdlr extends RightsManagementTrait {
bus.outGW.send(buildBroadcastNewPresFileAvailable(m, liveMeeting)) 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 = { def handle(m: PresAnnStatusMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
PresPresentationDAO.updateExportToChat(m.body.presId, m.body.status, m.body.pageNumber, m.body.error) PresPresentationDAO.updateExportToChat(m.body.presId, m.body.status, m.body.pageNumber, m.body.error)
bus.outGW.send(buildBroadcastPresAnnStatusMsg(m, liveMeeting)) bus.outGW.send(buildBroadcastPresAnnStatusMsg(m, liveMeeting))
} }
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, def handle(msg: PresentationConversionUpdateSysPubMsg, state: MeetingState2x,
liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = { liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
def broadcastEvent(msg: PresentationConversionUpdateSysPubMsg): Unit = { // broadcastEvent(msg)
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)
state state
} }
} }

View File

@ -15,7 +15,7 @@ trait SetPresenterInDefaultPodInternalMsgHdlr {
msg: SetPresenterInDefaultPodInternalMsg, state: MeetingState2x, msg: SetPresenterInDefaultPodInternalMsg, state: MeetingState2x,
liveMeeting: LiveMeeting, bus: MessageBus liveMeeting: LiveMeeting, bus: MessageBus
): MeetingState2x = { ): 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") log.info("Presenter pod change will trigger a presenter change")
SetPresenterInPodActionHandler.handleAction(state, liveMeeting, bus.outGW, "", PresentationPod.DEFAULT_PRESENTATION_POD, msg.presenterId) 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 handleSyncGetPresentationPods(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
def buildSyncGetPresentationPodsRespMsg(pods: Vector[PresentationPodVO]): BbbCommonEnvCoreMsg = { 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 envelope = BbbCoreEnvelope(SyncGetPresentationPodsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetPresentationPodsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp") val header = BbbClientMsgHeader(SyncGetPresentationPodsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")

View File

@ -9,9 +9,10 @@ trait SyncGetScreenshareInfoRespMsgHdlr {
this: ScreenshareApp2x => this: ScreenshareApp2x =>
def handleSyncGetScreenshareInfoRespMsg(liveMeeting: LiveMeeting, bus: MessageBus): Unit = { 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.meetingProp.intId,
liveMeeting.props.systemProps.html5InstanceId.toString "nodeJSapp"
) )
val envelope = BbbCoreEnvelope(SyncGetScreenshareInfoRespMsg.NAME, routing) val envelope = BbbCoreEnvelope(SyncGetScreenshareInfoRespMsg.NAME, routing)
val header = BbbClientMsgHeader( 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" val reason = "You need to be the presenter or moderator to deactivate timer"
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting) PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
} else { } else {
TimerModel.setIsActive(liveMeeting.timerModel, false) TimerModel.setRunning(liveMeeting.timerModel, running = false)
TimerModel.setIsActive(liveMeeting.timerModel, active = false)
TimerModel.setStopwatch(liveMeeting.timerModel, stopwatch = true)
TimerModel.reset(liveMeeting.timerModel)
TimerDAO.update(liveMeeting.props.meetingProp.intId, liveMeeting.timerModel) TimerDAO.update(liveMeeting.props.meetingProp.intId, liveMeeting.timerModel)
broadcastEvent() broadcastEvent()
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
package org.bigbluebutton.core.apps.users package org.bigbluebutton.core.apps.users
import org.bigbluebutton.ClientSettings.{ getConfigPropertyValueByPath, getConfigPropertyValueByPathAsIntOrElse }
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.RightsManagementTrait import org.bigbluebutton.core.apps.RightsManagementTrait
import org.bigbluebutton.core.models.{ UserState, Users2x } import org.bigbluebutton.core.models.{ UserState, Users2x }
@ -29,9 +30,11 @@ trait ChangeUserReactionEmojiReqMsgHdlr extends RightsManagementTrait {
outGW.send(msgEventChange) outGW.send(msgEventChange)
} }
//Get durationInSeconds from Client config
val userReactionExpire = getConfigPropertyValueByPathAsIntOrElse(liveMeeting.clientSettings, "public.userReaction.expire", 30)
for { for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
newUserState <- Users2x.setReactionEmoji(liveMeeting.users2x, user.intId, msg.body.reactionEmoji) newUserState <- Users2x.setReactionEmoji(liveMeeting.users2x, user.intId, msg.body.reactionEmoji, userReactionExpire)
} yield { } yield {
if (user.reactionEmoji != msg.body.reactionEmoji) { if (user.reactionEmoji != msg.body.reactionEmoji) {
broadcast(newUserState, 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.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait } import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.LockSettingsUtil import org.bigbluebutton.LockSettingsUtil
import org.bigbluebutton.core.db.NotificationDAO
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender } import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait { trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait {
@ -43,6 +44,7 @@ trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait {
Vector() Vector()
) )
outGW.send(notifyEvent) outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
Users2x.changeRole(liveMeeting.users2x, uvo, msg.body.role) Users2x.changeRole(liveMeeting.users2x, uvo, msg.body.role)
val event = buildUserRoleChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.userId, val event = buildUserRoleChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.userId,
@ -59,6 +61,7 @@ trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait {
Vector() Vector()
) )
outGW.send(notifyEvent) outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
val newUvo: UserState = Users2x.changeRole(liveMeeting.users2x, uvo, msg.body.role) val newUvo: UserState = Users2x.changeRole(liveMeeting.users2x, uvo, msg.body.role)
val event = buildUserRoleChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.userId, val event = buildUserRoleChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.userId,
@ -73,7 +76,7 @@ trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait {
for { for {
u <- RegisteredUsers.findWithUserId(uvo.intId, liveMeeting.registeredUsers) u <- RegisteredUsers.findWithUserId(uvo.intId, liveMeeting.registeredUsers)
} yield { } 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) user <- Users2x.findAll(liveMeeting.users2x)
} yield { } yield {
//Don't clear away and RaiseHand //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) sendClearedAllUsersReactionEvtMsg(outGW, liveMeeting.props.meetingProp.intId, msg.header.userId)
} else { } else {

View File

@ -74,7 +74,7 @@ trait EjectUserFromMeetingCmdMsgHdlr extends RightsManagementTrait {
Sender.sendDisconnectClientSysMsg(meetingId, ru.id, ejectedBy, EjectReasonCode.EJECT_USER, outGW) Sender.sendDisconnectClientSysMsg(meetingId, ru.id, ejectedBy, EjectReasonCode.EJECT_USER, outGW)
// Force reconnection with graphql to refresh permissions // 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 { } else {
// User is ejecting self, so just eject this userid not all sessions if joined using multiple // 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) Sender.sendDisconnectClientSysMsg(meetingId, userId, ejectedBy, EjectReasonCode.EJECT_USER, outGW)
// Force reconnection with graphql to refresh permissions // 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 { for {
regUser <- RegisteredUsers.findWithUserId(userId, liveMeeting.registeredUsers) regUser <- RegisteredUsers.findWithUserId(userId, liveMeeting.registeredUsers)
} yield { } 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 { for {
u <- RegisteredUsers.findWithUserId(uvo.intId, liveMeeting.registeredUsers) u <- RegisteredUsers.findWithUserId(uvo.intId, liveMeeting.registeredUsers)
} yield { } 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) 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 package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.db.NotificationDAO
import org.bigbluebutton.core.models._ import org.bigbluebutton.core.models._
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter } import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core.util.ColorPicker import org.bigbluebutton.core.util.ColorPicker
@ -49,7 +50,7 @@ trait RegisterUserReqMsgHdlr {
Sender.sendDisconnectClientSysMsg(meetingId, userToRemove.id, SystemUser.ID, EjectReasonCode.DUPLICATE_USER, outGW) Sender.sendDisconnectClientSysMsg(meetingId, userToRemove.id, SystemUser.ID, EjectReasonCode.DUPLICATE_USER, outGW)
// Force reconnection with graphql to refresh permissions // 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 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.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, 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) guestStatus, msg.body.excludeFromDashboard, msg.body.enforceLayout, msg.body.customParameters, false)
@ -107,6 +108,7 @@ trait RegisterUserReqMsgHdlr {
Vector(s"${regUser.name}") Vector(s"${regUser.name}")
) )
outGW.send(notifyEvent) outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
case GuestStatus.DENY => case GuestStatus.DENY =>
val g = GuestApprovedVO(regUser.id, GuestStatus.DENY) val g = GuestApprovedVO(regUser.id, GuestStatus.DENY)
UsersApp.approveOrRejectGuest(liveMeeting, outGW, g, SystemUser.ID) 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.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core2.message.senders.MsgBuilder import org.bigbluebutton.core2.message.senders.MsgBuilder
import org.bigbluebutton.core.apps.voice.VoiceApp import org.bigbluebutton.core.apps.voice.VoiceApp
import org.bigbluebutton.core.db.MeetingRecordingDAO import org.bigbluebutton.core.db.{ MeetingRecordingDAO, NotificationDAO }
trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait { trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
this: UsersApp => this: UsersApp =>
@ -49,6 +49,7 @@ trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
Vector() Vector()
) )
outGW.send(notifyEvent) outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
MeetingStatus2x.recordingStarted(liveMeeting.status) MeetingStatus2x.recordingStarted(liveMeeting.status)
MeetingRecordingDAO.insertRecording(liveMeeting.props.meetingProp.intId, msg.body.setBy) MeetingRecordingDAO.insertRecording(liveMeeting.props.meetingProp.intId, msg.body.setBy)
@ -75,6 +76,7 @@ trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
Vector() Vector()
) )
outGW.send(notifyEvent) outGW.send(notifyEvent)
NotificationDAO.insert(notifyEvent)
MeetingStatus2x.recordingStopped(liveMeeting.status) MeetingStatus2x.recordingStopped(liveMeeting.status)
MeetingRecordingDAO.updateStopped(liveMeeting.props.meetingProp.intId, msg.body.setBy) 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 val outGW: OutMsgRouter
def handleSyncGetUsersMeetingRespMsg(): Unit = { 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 envelope = BbbCoreEnvelope(SyncGetUsersMeetingRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetUsersMeetingRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp") val header = BbbClientMsgHeader(SyncGetUsersMeetingRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")

View File

@ -1,6 +1,7 @@
package org.bigbluebutton.core.apps.users package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.db.UserDAO
import org.bigbluebutton.core.models.{ Users2x, VoiceUserState, VoiceUsers } import org.bigbluebutton.core.models.{ Users2x, VoiceUserState, VoiceUsers }
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter } import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
@ -29,10 +30,10 @@ trait UserConnectedToGlobalAudioMsgHdlr {
for { for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
} yield { } yield {
val vu = VoiceUserState( val vu = VoiceUserState(
intId = user.intId, intId = user.intId,
voiceUserId = user.intId, voiceUserId = user.intId,
meetingId = props.meetingProp.intId,
callingWith = "flash", callingWith = "flash",
callerName = user.name, callerName = user.name,
callerNum = 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 { for {
user <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId) user <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId)
} yield { } yield {
VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, user.intId) VoiceUsers.removeWithIntId(liveMeeting.voiceUsers, liveMeeting.props.meetingProp.intId, user.intId)
broadcastEvent(user) 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) { if (reconnectingUser.userLeftFlag.left) {
log.info("Resetting flag that user left meeting. user {}", msg.body.userId) log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
// User has reconnected. Just reset it's flag. ralam Oct 23, 2018 // User has reconnected. Just reset it's flag. ralam Oct 23, 2018
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false) sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, leftFlag = false)
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId) Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
} }
state state

View File

@ -2,7 +2,7 @@ package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs.UserJoinMeetingReqMsg import org.bigbluebutton.common2.msgs.UserJoinMeetingReqMsg
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers
import org.bigbluebutton.core.db.{ UserDAO, UserStateDAO } import org.bigbluebutton.core.db.{ NotificationDAO, UserDAO, UserStateDAO }
import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models._ import org.bigbluebutton.core.models._
import org.bigbluebutton.core.running._ import org.bigbluebutton.core.running._
@ -51,14 +51,14 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
notifyPreviousUsersWithSameExtId(regUser) notifyPreviousUsersWithSameExtId(regUser)
clearCachedVoiceUser(regUser) clearCachedVoiceUser(regUser)
clearExpiredUserState(regUser) clearExpiredUserState(regUser)
invalidateUserGraphqlConnection(regUser) ForceUserGraphqlReconnection(regUser)
newState newState
} }
private def handleFailedUserJoin(msg: UserJoinMeetingReqMsg, failReason: String, failReasonCode: String) = { 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) 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 state
} }
@ -71,7 +71,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
private def resetUserLeftFlag(msg: UserJoinMeetingReqMsg) = { private def resetUserLeftFlag(msg: UserJoinMeetingReqMsg) = {
log.info("Resetting flag that user left meeting. user {}", msg.body.userId) log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false) sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, leftFlag = false)
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId) Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
} }
@ -137,6 +137,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
Vector(newUser.name) Vector(newUser.name)
) )
outGW.send(notifyUserEvent) outGW.send(notifyUserEvent)
NotificationDAO.insert(notifyUserEvent)
} }
private def clearCachedVoiceUser(regUser: RegisteredUser) = private def clearCachedVoiceUser(regUser: RegisteredUser) =
@ -144,9 +145,9 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
VoiceUsers.recoverVoiceUser(liveMeeting.voiceUsers, regUser.id) VoiceUsers.recoverVoiceUser(liveMeeting.voiceUsers, regUser.id)
private def clearExpiredUserState(regUser: RegisteredUser) = private def clearExpiredUserState(regUser: RegisteredUser) =
UserStateDAO.updateExpired(regUser.id, false) UserStateDAO.updateExpired(regUser.meetingId, regUser.id, false)
private def invalidateUserGraphqlConnection(regUser: RegisteredUser) = private def ForceUserGraphqlReconnection(regUser: RegisteredUser) =
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, "user_joined", outGW) 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 package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs.UserLeaveReqMsg import org.bigbluebutton.common2.msgs.UserLeaveReqMsg
import org.bigbluebutton.core.api.{ UserClosedAllGraphqlConnectionsInternalMsg }
import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.{ RegisteredUsers, Users2x } import org.bigbluebutton.core.models.{ RegisteredUsers, Users2x }
import org.bigbluebutton.core.running.{ HandlerHelpers, MeetingActor, OutMsgRouter } import org.bigbluebutton.core.running.{ HandlerHelpers, MeetingActor, OutMsgRouter }
@ -12,26 +13,36 @@ trait UserLeaveReqMsgHdlr extends HandlerHelpers {
val outGW: OutMsgRouter val outGW: OutMsgRouter
def handleUserLeaveReqMsg(msg: UserLeaveReqMsg, state: MeetingState2x): MeetingState2x = { def handleUserLeaveReqMsg(msg: UserLeaveReqMsg, state: MeetingState2x): MeetingState2x = {
Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) match { handleUserLeaveReq(msg.body.userId, msg.header.meetingId, msg.body.loggedOut, state)
}
def handleUserClosedAllGraphqlConnectionsInternalMsg(msg: UserClosedAllGraphqlConnectionsInternalMsg, state: MeetingState2x): MeetingState2x = {
log.info("Received user closed all graphql connections. user {} meetingId={}", msg.userId, liveMeeting.props.meetingProp.intId)
handleUserLeaveReq(msg.userId, liveMeeting.props.meetingProp.intId, loggedOut = false, state)
}
def handleUserLeaveReq(userId: String, meetingId: String, loggedOut: Boolean, state: MeetingState2x): MeetingState2x = {
Users2x.findWithIntId(liveMeeting.users2x, userId) match {
case Some(reconnectingUser) => case Some(reconnectingUser) =>
log.info("Received user left meeting. user {} meetingId={}", msg.body.userId, msg.header.meetingId) log.info("Received user left meeting. user {} meetingId={}", userId, meetingId)
if (!reconnectingUser.userLeftFlag.left) { if (!reconnectingUser.userLeftFlag.left) {
log.info("Setting user left flag. user {} meetingId={}", msg.body.userId, msg.header.meetingId) log.info("Setting user left flag. user {} meetingId={}", userId, meetingId)
// Just flag that user has left as the user might be reconnecting. // Just flag that user has left as the user might be reconnecting.
// An audit will remove this user if it hasn't rejoined after a certain period of time. // An audit will remove this user if it hasn't rejoined after a certain period of time.
// ralam oct 23, 2018 // ralam oct 23, 2018
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, true) sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, userId, leftFlag = true)
Users2x.setUserLeftFlag(liveMeeting.users2x, msg.body.userId) Users2x.setUserLeftFlag(liveMeeting.users2x, userId)
} }
if (msg.body.loggedOut) { if (loggedOut) {
log.info("Setting user logged out flag. user {} meetingId={}", msg.body.userId, msg.header.meetingId) log.info("Setting user logged out flag. user {} meetingId={}", userId, meetingId)
for { for {
ru <- RegisteredUsers.findWithUserId(msg.body.userId, liveMeeting.registeredUsers) ru <- RegisteredUsers.findWithUserId(userId, liveMeeting.registeredUsers)
} yield { } yield {
RegisteredUsers.setUserLoggedOutFlag(liveMeeting.registeredUsers, ru) RegisteredUsers.setUserLoggedOutFlag(liveMeeting.registeredUsers, ru)
Sender.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 state
@ -39,4 +50,5 @@ trait UserLeaveReqMsgHdlr extends HandlerHelpers {
state state
} }
} }
} }

View File

@ -13,7 +13,7 @@ trait UserReactionTimeExpiredCmdMsgHdlr extends RightsManagementTrait {
def handleUserReactionTimeExpiredCmdMsg(msg: UserReactionTimeExpiredCmdMsg) { def handleUserReactionTimeExpiredCmdMsg(msg: UserReactionTimeExpiredCmdMsg) {
val isNodeUser = msg.header.userId.equals("nodeJSapp") val isNodeUser = msg.header.userId.equals("nodeJSapp")
if (isNodeUser) { 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) u <- RegisteredUsers.findWithUserId(guest.guest, liveMeeting.registeredUsers)
} yield { } yield {
RegisteredUsers.setWaitingForApproval(liveMeeting.registeredUsers, u, guest.status) 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 // send message to user that he has been approved
val event = MsgBuilder.buildGuestApprovedEvtMsg( val event = MsgBuilder.buildGuestApprovedEvtMsg(
@ -131,7 +131,7 @@ object UsersApp {
// println(s"ejectUserFromMeeting will cause a automaticallyAssignPresenter for user=${user}") // println(s"ejectUserFromMeeting will cause a automaticallyAssignPresenter for user=${user}")
automaticallyAssignPresenter(outGW, liveMeeting) automaticallyAssignPresenter(outGW, liveMeeting)
} }
UserStateDAO.updateEjected(userId, reason, reasonCode, ejectedBy) UserStateDAO.updateEjected(meetingId, userId, reason, reasonCode, ejectedBy)
} }
for { for {
@ -158,16 +158,17 @@ class UsersApp(
with RegisterUserReqMsgHdlr with RegisterUserReqMsgHdlr
with ChangeUserRoleCmdMsgHdlr with ChangeUserRoleCmdMsgHdlr
with SetUserSpeechLocaleMsgHdlr with SetUserSpeechLocaleMsgHdlr
with SetUserSpeechOptionsMsgHdlr
with SyncGetUsersMeetingRespMsgHdlr with SyncGetUsersMeetingRespMsgHdlr
with LogoutAndEndMeetingCmdMsgHdlr with LogoutAndEndMeetingCmdMsgHdlr
with SetRecordingStatusCmdMsgHdlr with SetRecordingStatusCmdMsgHdlr
with RecordAndClearPreviousMarkersCmdMsgHdlr with RecordAndClearPreviousMarkersCmdMsgHdlr
with SendRecordingTimerInternalMsgHdlr with SendRecordingTimerInternalMsgHdlr
with GetRecordingStatusReqMsgHdlr with GetRecordingStatusReqMsgHdlr
with SelectRandomViewerReqMsgHdlr
with AssignPresenterReqMsgHdlr with AssignPresenterReqMsgHdlr
with ChangeUserPinStateReqMsgHdlr with ChangeUserPinStateReqMsgHdlr
with ChangeUserMobileFlagReqMsgHdlr with ChangeUserMobileFlagReqMsgHdlr
with UserConnectionAliveReqMsgHdlr
with ChangeUserReactionEmojiReqMsgHdlr with ChangeUserReactionEmojiReqMsgHdlr
with ChangeUserRaiseHandReqMsgHdlr with ChangeUserRaiseHandReqMsgHdlr
with ChangeUserAwayReqMsgHdlr with ChangeUserAwayReqMsgHdlr

View File

@ -75,7 +75,7 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers {
} }
private def sendFailedValidateAuthTokenRespMsg(msg: ValidateAuthTokenReqMsg, failReason: String, failReasonCode: String) = { 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, val event = MsgBuilder.buildValidateAuthTokenRespMsg(liveMeeting.props.meetingProp.intId, msg.header.userId, msg.body.authToken, false, false, 0,
0, failReasonCode, failReason) 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) 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 envelope = BbbCoreEnvelope(SyncGetVoiceUsersRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetVoiceUsersRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp") val header = BbbClientMsgHeader(SyncGetVoiceUsersRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val body = SyncGetVoiceUsersRespMsgBody(voiceUsers) val body = SyncGetVoiceUsersRespMsgBody(voiceUsers)

View File

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

View File

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

View File

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

View File

@ -40,7 +40,7 @@ trait VoiceConfCallStateEvtMsgHdlr {
outGW.send(msgEvent) outGW.send(msgEvent)
if (msg.body.userId.nonEmpty) { 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 => this: WebcamApp2x =>
def handleSyncGetWebcamInfoRespMsg(liveMeeting: LiveMeeting, bus: MessageBus): Unit = { 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.meetingProp.intId,
liveMeeting.props.systemProps.html5InstanceId.toString "nodeJSapp"
) )
val envelope = BbbCoreEnvelope(SyncGetWebcamInfoRespMsg.NAME, routing) val envelope = BbbCoreEnvelope(SyncGetWebcamInfoRespMsg.NAME, routing)
val header = BbbClientMsgHeader( val header = BbbClientMsgHeader(

View File

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

View File

@ -47,7 +47,7 @@ trait UserBroadcastCamStartMsgHdlr {
val webcam = new WebcamStream(msg.body.stream, msg.header.userId, Set.empty) val webcam = new WebcamStream(msg.body.stream, msg.header.userId, Set.empty)
for { for {
_ <- Webcams.addWebcamStream(liveMeeting.webcams, webcam) _ <- Webcams.addWebcamStream(liveMeeting.props.meetingProp.intId, liveMeeting.webcams, webcam)
} yield broadcastEvent(meetingId, msg.header.userId, msg.body.stream) } 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 handle(msg: ClearWhiteboardPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: ClearWhiteboardPubMsg, fullClear: Boolean): 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 envelope = BbbCoreEnvelope(ClearWhiteboardEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(ClearWhiteboardEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId) 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 handle(msg: DeleteWhiteboardAnnotationsPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: DeleteWhiteboardAnnotationsPubMsg, removedAnnotationsIds: Array[String]): 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 envelope = BbbCoreEnvelope(DeleteWhiteboardAnnotationsEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(DeleteWhiteboardAnnotationsEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId) 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 handle(msg: GetWhiteboardAnnotationsReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: GetWhiteboardAnnotationsReqMsg, history: Array[AnnotationVO], multiUser: Array[String]): 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 envelope = BbbCoreEnvelope(GetWhiteboardAnnotationsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(GetWhiteboardAnnotationsRespMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId) 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 handle(msg: ModifyWhiteboardAccessPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: ModifyWhiteboardAccessPubMsg): 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 envelope = BbbCoreEnvelope(ModifyWhiteboardAccessEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(ModifyWhiteboardAccessEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId) 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 handle(msg: SendCursorPositionPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: SendCursorPositionPubMsg): 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 envelope = BbbCoreEnvelope(SendCursorPositionEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(SendCursorPositionEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId) 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) //PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
} else { } else {
broadcastEvent(msg) 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