Merge branch 'v3.0.x-release' into docs-layout

This commit is contained in:
Anton Georgiev 2024-05-09 22:13:38 -04:00 committed by GitHub
commit a441d70226
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1215 changed files with 26982 additions and 29972 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

@ -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,55 @@ 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: Merge artifacts
uses: actions/upload-artifact/merge@v4
with:
name: all-blob-reports
pattern: blob-report-*
delete-merged: true
- name: Download all blob reports from GitHub Actions Artifacts - name: Download all blob reports from GitHub Actions Artifacts
uses: actions/download-artifact@v3 if: ${{ env.hasReportData }}
uses: actions/download-artifact@v4
with: with:
name: all-blob-reports name: all-blob-reports
path: bigbluebutton-tests/playwright/all-blob-reports path: bigbluebutton-tests/playwright/all-blob-reports
- 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 +405,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: 18 node-version: 18
cache: yarn cache: yarn

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

1
.gitignore vendored
View File

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

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("" -> "")
@ -82,6 +82,24 @@ object ClientSettings extends SystemConfiguration {
} }
} }
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("\\.")

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() {
@ -150,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
}) })
} }

View File

@ -120,13 +120,6 @@ 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
* @param breakoutId
* @param filename
*/
case class CaptureSharedNotesReqInternalMsg(breakoutId: String, filename: String) extends InMessage
/** /**
* Sent by GraphqlActionsActor to inform MeetingActor that user disconnected * Sent by GraphqlActionsActor to inform MeetingActor that user disconnected
* @param userId * @param userId
@ -138,10 +131,3 @@ case class UserClosedAllGraphqlConnectionsInternalMsg(userId: String) extends In
* @param userId * @param userId
*/ */
case class UserEstablishedGraphqlConnectionInternalMsg(userId: String) extends InMessage case class UserEstablishedGraphqlConnectionInternalMsg(userId: String) extends InMessage
// DeskShare
case class DeskShareStartedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
case class DeskShareStoppedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
case class DeskShareRTMPBroadcastStartedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) extends InMessage
case class DeskShareRTMPBroadcastStoppedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) extends InMessage
case class DeskShareGetDeskShareInfoRequest(conferenceName: String, requesterID: String, replyTo: String) extends InMessage

View File

@ -46,7 +46,7 @@ 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] = {
val wb = getWhiteboard(wbId) val wb = getWhiteboard(wbId)
var annotationsAdded = Array[AnnotationVO]() var annotationsAdded = Array[AnnotationVO]()
@ -70,7 +70,6 @@ class WhiteboardModel extends SystemConfiguration {
val newAnnotation = oldAnnotation.get.copy(annotationInfo = finalAnnotationInfo) val newAnnotation = oldAnnotation.get.copy(annotationInfo = finalAnnotationInfo)
newAnnotationsMap += (annotation.id -> newAnnotation) newAnnotationsMap += (annotation.id -> newAnnotation)
annotationsAdded :+= newAnnotation annotationsAdded :+= newAnnotation
PresAnnotationDAO.insertOrUpdate(newAnnotation, newAnnotation)
println(s"Updated annotation on page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].") println(s"Updated annotation on page [${wb.id}]. After numAnnotations=[${newAnnotationsMap.size}].")
} else { } else {
println(s"User $userId doesn't have permission to edit annotation ${annotation.id}, ignoring...") println(s"User $userId doesn't have permission to edit annotation ${annotation.id}, ignoring...")
@ -78,13 +77,14 @@ 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...") 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
@ -106,7 +106,7 @@ class WhiteboardModel extends SystemConfiguration {
private def cleanEndOrStartProps(props: Map[String, _]): Map[String, _] = { private def cleanEndOrStartProps(props: Map[String, _]): Map[String, _] = {
props.get("type") match { props.get("type") match {
case Some("binding") => props - ("x", "y") // Remove 'x' and 'y' for 'binding' type case Some("binding") => props - ("x", "y") // Remove 'x' and 'y' for 'binding' type
case Some("point") => props - ("boundShapeId", "normalizedAnchor", "isExact") // Remove unwanted properties for 'point' type case Some("point") => props - ("boundShapeId", "normalizedAnchor", "isExact", "isPrecise") // Remove unwanted properties for 'point' type
case _ => props case _ => props
} }
} }
@ -116,7 +116,7 @@ class WhiteboardModel extends SystemConfiguration {
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] = {
val wb = getWhiteboard(wbId) val wb = getWhiteboard(wbId)
var annotationsIdsRemoved = Array[String]() var annotationsIdsRemoved = Array[String]()
@ -143,15 +143,15 @@ class WhiteboardModel extends SystemConfiguration {
val updatedWb = wb.copy(annotationsMap = newAnnotationsMap) val updatedWb = wb.copy(annotationsMap = newAnnotationsMap)
saveWhiteboard(updatedWb) saveWhiteboard(updatedWb)
annotationsIdsRemoved.map(PresAnnotationDAO.delete(wbId, userId, _)) 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

@ -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

@ -30,7 +30,7 @@ trait EjectUserFromBreakoutInternalMsgHdlr {
) )
//TODO inform reason //TODO inform reason
UserDAO.softDelete(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)

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

@ -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

@ -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,8 +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 } import org.bigbluebutton.core2.message.senders.MsgBuilder
trait BroadcastLayoutMsgHdlr extends RightsManagementTrait { trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
this: LayoutApp2x => this: LayoutApp2x =>
@ -73,6 +73,7 @@ trait BroadcastLayoutMsgHdlr extends RightsManagementTrait {
Vector() Vector()
) )
outGW.send(notifyEvent) 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

@ -1,15 +1,15 @@
package org.bigbluebutton.core.apps.plugin package org.bigbluebutton.core.apps.plugin
import org.bigbluebutton.ClientSettings import org.bigbluebutton.ClientSettings
import org.bigbluebutton.common2.msgs.PluginDataChannelDeleteMessageMsg import org.bigbluebutton.common2.msgs.PluginDataChannelDeleteEntryMsg
import org.bigbluebutton.core.db.PluginDataChannelMessageDAO import org.bigbluebutton.core.db.PluginDataChannelEntryDAO
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 PluginDataChannelDeleteMessageMsgHdlr extends HandlerHelpers { trait PluginDataChannelDeleteEntryMsgHdlr extends HandlerHelpers {
def handle(msg: PluginDataChannelDeleteMessageMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = { def handle(msg: PluginDataChannelDeleteEntryMsg, 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,22 +21,23 @@ trait PluginDataChannelDeleteMessageMsgHdlr 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 {
deletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.dataChannel).deletePermission deletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).deletePermission
} yield { } yield {
deletePermission.toLowerCase match { deletePermission.toLowerCase match {
case "all" => true case "all" => true
case "moderator" => user.role == Roles.MODERATOR_ROLE case "moderator" => user.role == Roles.MODERATOR_ROLE
case "presenter" => user.presenter case "presenter" => user.presenter
case "sender" => { case "sender" => {
val senderUserId = PluginDataChannelMessageDAO.getMessageSender( val senderUserId = PluginDataChannelEntryDAO.getMessageSender(
meetingId, meetingId,
msg.body.pluginName, msg.body.pluginName,
msg.body.dataChannel, msg.body.channelName,
msg.body.messageId msg.body.subChannelName,
msg.body.entryId
) )
senderUserId == msg.header.userId senderUserId == msg.header.userId
} }
@ -45,13 +46,14 @@ trait PluginDataChannelDeleteMessageMsgHdlr extends HandlerHelpers {
} }
if (!hasPermission.contains(true)) { if (!hasPermission.contains(true)) {
println(s"No permission to delete in plugin: '${msg.body.pluginName}', data channel: '${msg.body.dataChannel}'.") println(s"No permission to delete in plugin: '${msg.body.pluginName}', data channel: '${msg.body.channelName}'.")
} else { } else {
PluginDataChannelMessageDAO.delete( PluginDataChannelEntryDAO.delete(
meetingId, meetingId,
msg.body.pluginName, msg.body.pluginName,
msg.body.dataChannel, msg.body.channelName,
msg.body.messageId 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 PluginDataChannelDispatchMessageMsgHdlr extends HandlerHelpers { trait PluginDataChannelPushEntryMsgHdlr extends HandlerHelpers {
def handle(msg: PluginDataChannelDispatchMessageMsg, 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 PluginDataChannelDispatchMessageMsgHdlr 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 PluginDataChannelDispatchMessageMsgHdlr 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

@ -2,7 +2,7 @@ package org.bigbluebutton.core.apps.plugin
import org.bigbluebutton.ClientSettings import org.bigbluebutton.ClientSettings
import org.bigbluebutton.common2.msgs.PluginDataChannelResetMsg import org.bigbluebutton.common2.msgs.PluginDataChannelResetMsg
import org.bigbluebutton.core.db.PluginDataChannelMessageDAO import org.bigbluebutton.core.db.PluginDataChannelEntryDAO
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 }
@ -21,11 +21,11 @@ trait PluginDataChannelResetMsgHdlr 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 {
deletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.dataChannel).deletePermission deletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).deletePermission
} yield { } yield {
deletePermission.toLowerCase match { deletePermission.toLowerCase match {
case "all" => true case "all" => true
@ -36,12 +36,13 @@ trait PluginDataChannelResetMsgHdlr extends HandlerHelpers {
} }
if (!hasPermission.contains(true)) { if (!hasPermission.contains(true)) {
println(s"No permission to delete (reset) in plugin: '${msg.body.pluginName}', data channel: '${msg.body.dataChannel}'.") println(s"No permission to delete (reset) in plugin: '${msg.body.pluginName}', data channel: '${msg.body.channelName}'.")
} else { } else {
PluginDataChannelMessageDAO.reset( PluginDataChannelEntryDAO.reset(
meetingId, meetingId,
msg.body.pluginName, msg.body.pluginName,
msg.body.dataChannel msg.body.channelName,
msg.body.subChannelName
) )
} }
} }

View File

@ -2,10 +2,11 @@ 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 PluginDataChannelDispatchMessageMsgHdlr extends PluginDataChannelPushEntryMsgHdlr
with PluginDataChannelDeleteMessageMsgHdlr with PluginDataChannelDeleteEntryMsgHdlr
with PluginDataChannelResetMsgHdlr { with PluginDataChannelResetMsgHdlr {
val log = Logging(context.system, getClass) val log = Logging(context.system, getClass)

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
@ -268,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, 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

@ -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

@ -3,7 +3,7 @@ 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
@ -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)
} }
} }

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,

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
@ -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

@ -1,9 +1,10 @@
package org.bigbluebutton.core.apps.users package org.bigbluebutton.core.apps.users
import org.bigbluebutton.ClientSettings.{ getConfigPropertyValueByPathAsListOfIntOrElse, getConfigPropertyValueByPathAsListOfStringOrElse }
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.db.UserConnectionStatusDAO import org.bigbluebutton.core.db.UserConnectionStatusDAO
import org.bigbluebutton.core.models.{ UserState, Users2x } import org.bigbluebutton.core.models.Users2x
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter } import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
trait UserConnectionAliveReqMsgHdlr extends RightsManagementTrait { trait UserConnectionAliveReqMsgHdlr extends RightsManagementTrait {
@ -13,13 +14,42 @@ trait UserConnectionAliveReqMsgHdlr extends RightsManagementTrait {
val outGW: OutMsgRouter val outGW: OutMsgRouter
def handleUserConnectionAliveReqMsg(msg: UserConnectionAliveReqMsg): Unit = { def handleUserConnectionAliveReqMsg(msg: UserConnectionAliveReqMsg): Unit = {
log.info("handleUserConnectionAliveReqMsg: userId={}", msg.body.userId) log.info("handleUserConnectionAliveReqMsg: networkRttInMs={} userId={}", msg.body.networkRttInMs, msg.body.userId)
for { for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
} yield { } yield {
UserConnectionStatusDAO.updateUserAlive(user.intId) 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

@ -1,25 +0,0 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.RightsManagementTrait
import org.bigbluebutton.core.db.UserConnectionStatusDAO
import org.bigbluebutton.core.models.{ UserState, Users2x }
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
trait UserConnectionUpdateRttReqMsgHdlr extends RightsManagementTrait {
this: UsersApp =>
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
def handleUserConnectionUpdateRttReqMsg(msg: UserConnectionUpdateRttReqMsg): Unit = {
log.info("handleUserConnectionUpdateRttReqMsg: networkRttInMs={} userId={}", msg.body.networkRttInMs, msg.body.userId)
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
} yield {
UserConnectionStatusDAO.updateUserRtt(user.intId, msg.body.networkRttInMs)
}
}
}

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

@ -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._
@ -58,7 +58,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
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
} }
@ -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,7 +145,7 @@ 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 ForceUserGraphqlReconnection(regUser: RegisteredUser) = private def ForceUserGraphqlReconnection(regUser: RegisteredUser) =
Sender.sendForceUserGraphqlReconnectionSysMsg(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

@ -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,18 +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 UserConnectionAliveReqMsgHdlr
with UserConnectionUpdateRttReqMsgHdlr
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.softDelete(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,7 +3,7 @@ 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.models.{ RegisteredUsers, Roles, Users2x }
import org.bigbluebutton.core.running.LiveMeeting import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender } import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
@ -64,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,
@ -74,6 +75,7 @@ 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)

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)
} }
} }
} }

View File

@ -10,8 +10,8 @@ trait SendWhiteboardAnnotationsPubMsgHdlr extends RightsManagementTrait {
def handle(msg: SendWhiteboardAnnotationsPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = { def handle(msg: SendWhiteboardAnnotationsPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: SendWhiteboardAnnotationsPubMsg, whiteboardId: String, annotations: Array[AnnotationVO], html5InstanceId: String): Unit = { def broadcastEvent(msg: SendWhiteboardAnnotationsPubMsg, whiteboardId: String, annotations: Array[AnnotationVO]): Unit = {
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, html5InstanceId) val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(SendWhiteboardAnnotationsEvtMsg.NAME, routing) val envelope = BbbCoreEnvelope(SendWhiteboardAnnotationsEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(SendWhiteboardAnnotationsEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId) val header = BbbClientMsgHeader(SendWhiteboardAnnotationsEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
@ -58,7 +58,7 @@ trait SendWhiteboardAnnotationsPubMsgHdlr extends RightsManagementTrait {
// } // }
// println("============= Printed Sanitized annotations ============") // println("============= Printed Sanitized annotations ============")
val annotations = sendWhiteboardAnnotations(msg.body.whiteboardId, msg.header.userId, msg.body.annotations, liveMeeting, isUserAmongPresenters, isUserModerator) val annotations = sendWhiteboardAnnotations(msg.body.whiteboardId, msg.header.userId, msg.body.annotations, liveMeeting, isUserAmongPresenters, isUserModerator)
broadcastEvent(msg, msg.body.whiteboardId, annotations, msg.body.html5InstanceId) broadcastEvent(msg, msg.body.whiteboardId, annotations)
} else { } else {
//val meetingId = liveMeeting.props.meetingProp.intId //val meetingId = liveMeeting.props.meetingProp.intId
//val reason = "No permission to send a whiteboard annotation." //val reason = "No permission to send a whiteboard annotation."

View File

@ -33,7 +33,7 @@ class WhiteboardApp2x(implicit val context: ActorContext)
isModerator: Boolean isModerator: Boolean
): Array[AnnotationVO] = { ): Array[AnnotationVO] = {
// println("Received whiteboard annotation. status=[" + status + "], annotationType=[" + annotationType + "]") // println("Received whiteboard annotation. status=[" + status + "], annotationType=[" + annotationType + "]")
liveMeeting.wbModel.addAnnotations(whiteboardId, requesterId, annotations, isPresenter, isModerator) liveMeeting.wbModel.addAnnotations(whiteboardId, liveMeeting.props.meetingProp.intId, requesterId, annotations, isPresenter, isModerator)
} }
def getWhiteboardAnnotations(whiteboardId: String, liveMeeting: LiveMeeting): Array[AnnotationVO] = { def getWhiteboardAnnotations(whiteboardId: String, liveMeeting: LiveMeeting): Array[AnnotationVO] = {
@ -49,7 +49,7 @@ class WhiteboardApp2x(implicit val context: ActorContext)
isPresenter: Boolean, isPresenter: Boolean,
isModerator: Boolean isModerator: Boolean
): Array[String] = { ): Array[String] = {
liveMeeting.wbModel.deleteAnnotations(whiteboardId, requesterId, annotationsIds, isPresenter, isModerator) liveMeeting.wbModel.deleteAnnotations(whiteboardId, liveMeeting.props.meetingProp.intId, requesterId, annotationsIds, isPresenter, isModerator)
} }
def getWhiteboardAccess(whiteboardId: String, liveMeeting: LiveMeeting): Array[String] = { def getWhiteboardAccess(whiteboardId: String, liveMeeting: LiveMeeting): Array[String] = {
@ -57,7 +57,7 @@ class WhiteboardApp2x(implicit val context: ActorContext)
} }
def modifyWhiteboardAccess(whiteboardId: String, multiUser: Array[String], liveMeeting: LiveMeeting) { def modifyWhiteboardAccess(whiteboardId: String, multiUser: Array[String], liveMeeting: LiveMeeting) {
liveMeeting.wbModel.modifyWhiteboardAccess(whiteboardId, multiUser) liveMeeting.wbModel.modifyWhiteboardAccess(liveMeeting.props.meetingProp.intId, whiteboardId, multiUser)
} }
def filterWhiteboardMessage(whiteboardId: String, userId: String, liveMeeting: LiveMeeting): Boolean = { def filterWhiteboardMessage(whiteboardId: String, userId: String, liveMeeting: LiveMeeting): Boolean = {

View File

@ -67,7 +67,7 @@ object BreakoutRoomDAO {
userId <- room.assignedUsers userId <- room.assignedUsers
(redirectToHtml5JoinURL, redirectJoinURL) <- BreakoutHdlrHelpers.getRedirectUrls(liveMeeting, userId, room.externalId, room.sequence.toString()) (redirectToHtml5JoinURL, redirectJoinURL) <- BreakoutHdlrHelpers.getRedirectUrls(liveMeeting, userId, room.externalId, room.sequence.toString())
} yield { } yield {
BreakoutRoomUserDAO.prepareInsert(room.id, userId, redirectToHtml5JoinURL) BreakoutRoomUserDAO.prepareInsert(room.id, liveMeeting.props.meetingProp.intId, userId, redirectToHtml5JoinURL)
} }
).transactionally) ).transactionally)
.onComplete { .onComplete {

View File

@ -11,6 +11,7 @@ import scala.util.{Failure, Success}
case class BreakoutRoomUserDbModel( case class BreakoutRoomUserDbModel(
breakoutRoomId: String, breakoutRoomId: String,
meetingId: String,
userId: String, userId: String,
joinURL: String, joinURL: String,
joinedAt: Option[java.sql.Timestamp], joinedAt: Option[java.sql.Timestamp],
@ -19,19 +20,21 @@ case class BreakoutRoomUserDbModel(
class BreakoutRoomUserDbTableDef(tag: Tag) extends Table[BreakoutRoomUserDbModel](tag, None, "breakoutRoom_user") { class BreakoutRoomUserDbTableDef(tag: Tag) extends Table[BreakoutRoomUserDbModel](tag, None, "breakoutRoom_user") {
val breakoutRoomId = column[String]("breakoutRoomId", O.PrimaryKey) val breakoutRoomId = column[String]("breakoutRoomId", O.PrimaryKey)
val meetingId = column[String]("meetingId", O.PrimaryKey)
val userId = column[String]("userId", O.PrimaryKey) val userId = column[String]("userId", O.PrimaryKey)
val joinURL = column[String]("joinURL") val joinURL = column[String]("joinURL")
val joinedAt = column[Option[java.sql.Timestamp]]("joinedAt") val joinedAt = column[Option[java.sql.Timestamp]]("joinedAt")
val assignedAt = column[Option[java.sql.Timestamp]]("assignedAt") val assignedAt = column[Option[java.sql.Timestamp]]("assignedAt")
override def * = (breakoutRoomId, userId, joinURL, joinedAt, assignedAt) <> (BreakoutRoomUserDbModel.tupled, BreakoutRoomUserDbModel.unapply) override def * = (breakoutRoomId, meetingId, userId, joinURL, joinedAt, assignedAt) <> (BreakoutRoomUserDbModel.tupled, BreakoutRoomUserDbModel.unapply)
} }
object BreakoutRoomUserDAO { object BreakoutRoomUserDAO {
def prepareInsert(breakoutRoomId: String, userId: String, joinURL: String) = { def prepareInsert(breakoutRoomId: String, meetingId: String, userId: String, joinURL: String) = {
TableQuery[BreakoutRoomUserDbTableDef].insertOrUpdate( TableQuery[BreakoutRoomUserDbTableDef].insertOrUpdate(
BreakoutRoomUserDbModel( BreakoutRoomUserDbModel(
breakoutRoomId = breakoutRoomId, breakoutRoomId = breakoutRoomId,
meetingId = meetingId,
userId = userId, userId = userId,
joinURL = joinURL, joinURL = joinURL,
joinedAt = None, joinedAt = None,
@ -40,8 +43,9 @@ object BreakoutRoomUserDAO {
) )
} }
def prepareDelete(breakoutRoomId: String, userId: String) = { def prepareDelete(breakoutRoomId: String, meetingId: String, userId: String) = {
var query = TableQuery[BreakoutRoomUserDbTableDef] var query = TableQuery[BreakoutRoomUserDbTableDef]
.filter(_.meetingId === meetingId)
.filter(_.userId === userId) .filter(_.userId === userId)
//Sometimes the user is moved before he joined in any room, in this case remove all assignments //Sometimes the user is moved before he joined in any room, in this case remove all assignments
@ -51,10 +55,10 @@ object BreakoutRoomUserDAO {
query.delete query.delete
} }
def updateRoomChanged(userId: String, fromBreakoutRoomId: String, toBreakoutRoomId: String, joinUrl: String) = { def updateRoomChanged(meetingId: String, userId: String, fromBreakoutRoomId: String, toBreakoutRoomId: String, joinUrl: String) = {
DatabaseConnection.db.run(DBIO.seq( DatabaseConnection.db.run(DBIO.seq(
BreakoutRoomUserDAO.prepareDelete(fromBreakoutRoomId, userId), BreakoutRoomUserDAO.prepareDelete(fromBreakoutRoomId, meetingId, userId),
BreakoutRoomUserDAO.prepareInsert(toBreakoutRoomId, userId, joinUrl) BreakoutRoomUserDAO.prepareInsert(toBreakoutRoomId, meetingId, userId, joinUrl)
).transactionally) ).transactionally)
.onComplete { .onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) changed on breakoutRoom_user table!") case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) changed on breakoutRoom_user table!")
@ -62,9 +66,10 @@ object BreakoutRoomUserDAO {
} }
} }
def updateUserJoined(usersInRoom: Vector[String], breakoutRoom: BreakoutRoom2x) = { def updateUserJoined(meetingId: String, usersInRoom: Vector[String], breakoutRoom: BreakoutRoom2x) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[BreakoutRoomUserDbTableDef] TableQuery[BreakoutRoomUserDbTableDef]
.filter(_.meetingId === meetingId)
.filter(_.userId inSet usersInRoom) .filter(_.userId inSet usersInRoom)
.filter(_.breakoutRoomId === breakoutRoom.id) .filter(_.breakoutRoomId === breakoutRoom.id)
.filter(_.joinedAt.isEmpty) .filter(_.joinedAt.isEmpty)
@ -76,9 +81,9 @@ object BreakoutRoomUserDAO {
} }
} }
def updateUserEjected(userId: String, breakoutRoomId: String) = { def updateUserEjected(meetingId: String, userId: String, breakoutRoomId: String) = {
DatabaseConnection.db.run(DBIO.seq( DatabaseConnection.db.run(DBIO.seq(
BreakoutRoomUserDAO.prepareDelete(userId, breakoutRoomId), BreakoutRoomUserDAO.prepareDelete(meetingId, userId, breakoutRoomId),
).transactionally) ).transactionally)
.onComplete { .onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) deleted on breakoutRoom_user table!") case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) deleted on breakoutRoom_user table!")
@ -90,7 +95,7 @@ object BreakoutRoomUserDAO {
for { for {
(redirectToHtml5JoinURL, redirectJoinURL) <- BreakoutHdlrHelpers.getRedirectUrls(liveMeeting, userId, room.externalId, room.sequence.toString) (redirectToHtml5JoinURL, redirectJoinURL) <- BreakoutHdlrHelpers.getRedirectUrls(liveMeeting, userId, room.externalId, room.sequence.toString)
} yield { } yield {
DatabaseConnection.db.run(BreakoutRoomUserDAO.prepareInsert(room.id, userId, redirectToHtml5JoinURL)) DatabaseConnection.db.run(BreakoutRoomUserDAO.prepareInsert(room.id, liveMeeting.props.meetingProp.intId, userId, redirectToHtml5JoinURL))
.onComplete { .onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on breakoutRoom_user table!") case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on breakoutRoom_user table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting breakoutRoom_user: $e") case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting breakoutRoom_user: $e")
@ -98,10 +103,11 @@ object BreakoutRoomUserDAO {
} }
} }
// def updateUserJoined(userId: String, breakoutRoomId: String) = { // def updateUserJoined(meetingId: String, userId: String, breakoutRoomId: String) = {
// DatabaseConnection.db.run( // DatabaseConnection.db.run(
// TableQuery[BreakoutRoomUserDbTableDef] // TableQuery[BreakoutRoomUserDbTableDef]
// .filter(_.breakoutRoomId === breakoutRoomId) // .filter(_.breakoutRoomId === breakoutRoomId)
// .filter(_.meetingId === meetingId)
// .filter(_.userId === userId) // .filter(_.userId === userId)
// .map(u => u.joinedAt) // .map(u => u.joinedAt)
// .update(Some(new java.sql.Timestamp(System.currentTimeMillis()))) // .update(Some(new java.sql.Timestamp(System.currentTimeMillis())))

View File

@ -9,7 +9,7 @@ case class CaptionDbModel(
meetingId: String, meetingId: String,
captionType: String, captionType: String,
userId: String, userId: String,
lang: String, locale: String,
captionText: String, captionText: String,
createdAt: java.sql.Timestamp createdAt: java.sql.Timestamp
) )
@ -19,10 +19,10 @@ class CaptionTableDef(tag: Tag) extends Table[CaptionDbModel](tag, None, "captio
val meetingId = column[String]("meetingId") val meetingId = column[String]("meetingId")
val captionType = column[String]("captionType") val captionType = column[String]("captionType")
val userId = column[String]("userId") val userId = column[String]("userId")
val lang = column[String]("lang") val locale = column[String]("locale")
val captionText = column[String]("captionText") val captionText = column[String]("captionText")
val createdAt = column[java.sql.Timestamp]("createdAt") val createdAt = column[java.sql.Timestamp]("createdAt")
def * = (captionId, meetingId, captionType, userId, lang, captionText, createdAt) <> (CaptionDbModel.tupled, CaptionDbModel.unapply) def * = (captionId, meetingId, captionType, userId, locale, captionText, createdAt) <> (CaptionDbModel.tupled, CaptionDbModel.unapply)
} }
object CaptionTypes { object CaptionTypes {
@ -32,7 +32,7 @@ object CaptionTypes {
object CaptionDAO { object CaptionDAO {
def insertOrUpdateAudioCaption(captionId: String, meetingId: String, userId: String, transcript: String, lang: String) = { def insertOrUpdateAudioCaption(captionId: String, meetingId: String, userId: String, transcript: String, locale: String) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[CaptionTableDef].insertOrUpdate( TableQuery[CaptionTableDef].insertOrUpdate(
CaptionDbModel( CaptionDbModel(
@ -40,7 +40,7 @@ object CaptionDAO {
meetingId = meetingId, meetingId = meetingId,
captionType = CaptionTypes.AUDIO_TRANSCRIPTION, captionType = CaptionTypes.AUDIO_TRANSCRIPTION,
userId = userId, userId = userId,
lang = lang, locale = locale,
captionText = transcript, captionText = transcript,
createdAt = new java.sql.Timestamp(System.currentTimeMillis()) createdAt = new java.sql.Timestamp(System.currentTimeMillis())
) )
@ -68,7 +68,7 @@ object CaptionDAO {
SELECT "captionId", "captionText", "createdAt" SELECT "captionId", "captionText", "createdAt"
FROM caption FROM caption
WHERE "meetingId" = ${meetingId} WHERE "meetingId" = ${meetingId}
AND lang = ${locale} AND locale = ${locale}
AND "captionType" = ${CaptionTypes.TYPED} AND "captionType" = ${CaptionTypes.TYPED}
order by "createdAt" desc order by "createdAt" desc
limit 2 limit 2
@ -80,13 +80,13 @@ object CaptionDAO {
LIMIT 1 LIMIT 1
) )
RETURNING *) RETURNING *)
INSERT INTO caption ("captionId", "meetingId", "captionType", "userId", "lang", "captionText", "createdAt") INSERT INTO caption ("captionId", "meetingId", "captionType", "userId", "locale", "captionText", "createdAt")
SELECT md5(random()::text || clock_timestamp()::text), ${meetingId}, 'TYPED', ${userId}, ${locale}, ${line}, current_timestamp SELECT md5(random()::text || clock_timestamp()::text), ${meetingId}, 'TYPED', ${userId}, ${locale}, ${line}, current_timestamp
WHERE NOT EXISTS (SELECT * FROM upsert) WHERE NOT EXISTS (SELECT * FROM upsert)
AND ${line} NOT IN (SELECT "captionText" AND ${line} NOT IN (SELECT "captionText"
FROM caption FROM caption
WHERE "meetingId" = ${meetingId} WHERE "meetingId" = ${meetingId}
AND lang = ${locale} AND locale = ${locale}
AND "captionType" = ${CaptionTypes.TYPED} AND "captionType" = ${CaptionTypes.TYPED}
order by "createdAt" desc order by "createdAt" desc
limit 2 limit 2

View File

@ -0,0 +1,41 @@
package org.bigbluebutton.core.db
import slick.jdbc.PostgresProfile.api._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{ Failure, Success }
case class CaptionLocaleDbModel(
meetingId: String,
locale: String,
captionType: String,
ownerUserId: String,
updatedAt: java.sql.Timestamp
)
class CaptionLocaleTableDef(tag: Tag) extends Table[CaptionLocaleDbModel](tag, None, "caption_locale") {
val meetingId = column[String]("meetingId", O.PrimaryKey)
val locale = column[String]("locale", O.PrimaryKey)
val captionType = column[String]("captionType", O.PrimaryKey)
val ownerUserId = column[String]("ownerUserId")
val updatedAt = column[java.sql.Timestamp]("updatedAt")
def * = (meetingId, locale, captionType, ownerUserId, updatedAt) <> (CaptionLocaleDbModel.tupled, CaptionLocaleDbModel.unapply)
}
object CaptionLocaleDAO {
def insertOrUpdateCaptionLocale(meetingId: String, locale: String, captionType: String, ownerUserId: String) = {
DatabaseConnection.db.run(
TableQuery[CaptionLocaleTableDef].insertOrUpdate(
CaptionLocaleDbModel(
meetingId = meetingId,
locale = locale,
captionType = captionType,
ownerUserId = ownerUserId,
updatedAt = new java.sql.Timestamp(System.currentTimeMillis())
)
)
).onComplete {
case Success(_) => DatabaseConnection.logger.debug(s"Upserted caption with ID $meetingId-$locale on CaptionLocale table")
case Failure(e) => DatabaseConnection.logger.debug(s"Error upserting caption on CaptionLocale: $e")
}
}
}

View File

@ -7,6 +7,14 @@ import org.bigbluebutton.core.apps.groupchats.GroupChatApp
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{ Failure, Success } import scala.util.{ Failure, Success }
case class MeetingSystemColumnsDbModel(
loginUrl: Option[String],
logoutUrl: Option[String],
customLogoUrl: Option[String],
bannerText: Option[String],
bannerColor: Option[String],
)
case class MeetingDbModel( case class MeetingDbModel(
meetingId: String, meetingId: String,
extId: String, extId: String,
@ -19,12 +27,11 @@ case class MeetingDbModel(
presentationUploadExternalDescription: String, presentationUploadExternalDescription: String,
presentationUploadExternalUrl: String, presentationUploadExternalUrl: String,
learningDashboardAccessToken: String, learningDashboardAccessToken: String,
logoutUrl: String, systemColumns: MeetingSystemColumnsDbModel,
customLogoUrl: Option[String],
bannerText: Option[String],
bannerColor: Option[String],
createdTime: Long, createdTime: Long,
durationInSeconds: Int, durationInSeconds: Int,
endWhenNoModerator: Boolean,
endWhenNoModeratorDelayInMinutes: Int,
endedAt: Option[java.sql.Timestamp], endedAt: Option[java.sql.Timestamp],
endedReasonCode: Option[String], endedReasonCode: Option[String],
endedBy: Option[String], endedBy: Option[String],
@ -43,12 +50,11 @@ class MeetingDbTableDef(tag: Tag) extends Table[MeetingDbModel](tag, None, "meet
presentationUploadExternalDescription, presentationUploadExternalDescription,
presentationUploadExternalUrl, presentationUploadExternalUrl,
learningDashboardAccessToken, learningDashboardAccessToken,
logoutUrl, systemColumns,
customLogoUrl,
bannerText,
bannerColor,
createdTime, createdTime,
durationInSeconds, durationInSeconds,
endWhenNoModerator,
endWhenNoModeratorDelayInMinutes,
endedAt, endedAt,
endedReasonCode, endedReasonCode,
endedBy endedBy
@ -64,12 +70,16 @@ class MeetingDbTableDef(tag: Tag) extends Table[MeetingDbModel](tag, None, "meet
val presentationUploadExternalDescription = column[String]("presentationUploadExternalDescription") val presentationUploadExternalDescription = column[String]("presentationUploadExternalDescription")
val presentationUploadExternalUrl = column[String]("presentationUploadExternalUrl") val presentationUploadExternalUrl = column[String]("presentationUploadExternalUrl")
val learningDashboardAccessToken = column[String]("learningDashboardAccessToken") val learningDashboardAccessToken = column[String]("learningDashboardAccessToken")
val logoutUrl = column[String]("logoutUrl") val loginUrl = column[Option[String]]("loginUrl")
val logoutUrl = column[Option[String]]("logoutUrl")
val customLogoUrl = column[Option[String]]("customLogoUrl") val customLogoUrl = column[Option[String]]("customLogoUrl")
val bannerText = column[Option[String]]("bannerText") val bannerText = column[Option[String]]("bannerText")
val bannerColor = column[Option[String]]("bannerColor") val bannerColor = column[Option[String]]("bannerColor")
val systemColumns = (loginUrl, logoutUrl, customLogoUrl, bannerText, bannerColor) <> (MeetingSystemColumnsDbModel.tupled, MeetingSystemColumnsDbModel.unapply)
val createdTime = column[Long]("createdTime") val createdTime = column[Long]("createdTime")
val durationInSeconds = column[Int]("durationInSeconds") val durationInSeconds = column[Int]("durationInSeconds")
val endWhenNoModerator = column[Boolean]("endWhenNoModerator")
val endWhenNoModeratorDelayInMinutes = column[Int]("endWhenNoModeratorDelayInMinutes")
val endedAt = column[Option[java.sql.Timestamp]]("endedAt") val endedAt = column[Option[java.sql.Timestamp]]("endedAt")
val endedReasonCode = column[Option[String]]("endedReasonCode") val endedReasonCode = column[Option[String]]("endedReasonCode")
val endedBy = column[Option[String]]("endedBy") val endedBy = column[Option[String]]("endedBy")
@ -91,7 +101,15 @@ object MeetingDAO {
presentationUploadExternalDescription = meetingProps.meetingProp.presentationUploadExternalDescription, presentationUploadExternalDescription = meetingProps.meetingProp.presentationUploadExternalDescription,
presentationUploadExternalUrl = meetingProps.meetingProp.presentationUploadExternalUrl, presentationUploadExternalUrl = meetingProps.meetingProp.presentationUploadExternalUrl,
learningDashboardAccessToken = meetingProps.password.learningDashboardAccessToken, learningDashboardAccessToken = meetingProps.password.learningDashboardAccessToken,
logoutUrl = meetingProps.systemProps.logoutUrl, systemColumns = MeetingSystemColumnsDbModel(
loginUrl = meetingProps.systemProps.loginUrl match {
case "" => None
case loginUrl => Some(loginUrl)
},
logoutUrl = meetingProps.systemProps.logoutUrl match {
case "" => None
case logoutUrl => Some(logoutUrl)
},
customLogoUrl = meetingProps.systemProps.customLogoURL match { customLogoUrl = meetingProps.systemProps.customLogoURL match {
case "" => None case "" => None
case logoUrl => Some(logoUrl) case logoUrl => Some(logoUrl)
@ -104,8 +122,11 @@ object MeetingDAO {
case "" => None case "" => None
case bannerColor => Some(bannerColor) case bannerColor => Some(bannerColor)
}, },
),
createdTime = meetingProps.durationProps.createdTime, createdTime = meetingProps.durationProps.createdTime,
durationInSeconds = meetingProps.durationProps.duration * 60, durationInSeconds = meetingProps.durationProps.duration * 60,
endWhenNoModerator = meetingProps.durationProps.endWhenNoModerator,
endWhenNoModeratorDelayInMinutes = meetingProps.durationProps.endWhenNoModeratorDelayInMinutes,
endedAt = None, endedAt = None,
endedReasonCode = None, endedReasonCode = None,
endedBy = None endedBy = None
@ -123,7 +144,7 @@ object MeetingDAO {
MeetingWelcomeDAO.insert(meetingProps.meetingProp.intId, meetingProps.welcomeProp) MeetingWelcomeDAO.insert(meetingProps.meetingProp.intId, meetingProps.welcomeProp)
MeetingGroupDAO.insert(meetingProps.meetingProp.intId, meetingProps.groups) MeetingGroupDAO.insert(meetingProps.meetingProp.intId, meetingProps.groups)
MeetingBreakoutDAO.insert(meetingProps.meetingProp.intId, meetingProps.breakoutProps) MeetingBreakoutDAO.insert(meetingProps.meetingProp.intId, meetingProps.breakoutProps)
TimerDAO.insert(meetingProps.meetingProp.intId) TimerDAO.insert(meetingProps.meetingProp.intId, clientSettings)
LayoutDAO.insert(meetingProps.meetingProp.intId, meetingProps.usersProp.meetingLayout) LayoutDAO.insert(meetingProps.meetingProp.intId, meetingProps.usersProp.meetingLayout)
MeetingClientSettingsDAO.insert(meetingProps.meetingProp.intId, JsonUtils.mapToJson(clientSettings)) MeetingClientSettingsDAO.insert(meetingProps.meetingProp.intId, JsonUtils.mapToJson(clientSettings))
} }
@ -182,4 +203,27 @@ object MeetingDAO {
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating endedAt=now() Meeting: $e") case Failure(e) => DatabaseConnection.logger.debug(s"Error updating endedAt=now() Meeting: $e")
} }
} }
def setAllMeetingsEnded(endedReasonCode: String, endedBy: String) = {
DatabaseConnection.db.run(
TableQuery[MeetingDbTableDef]
.filter(_.endedAt.isEmpty)
.map(a => (a.endedAt, a.endedReasonCode, a.endedBy))
.update(
(
Some(new java.sql.Timestamp(System.currentTimeMillis())),
Some(endedReasonCode),
endedBy match {
case "" => None
case c => Some(c)
}
)
)
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated all-meetings endedAt=now() on Meeting table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating all-meetings endedAt=now() on Meeting table: $e")
}
}
} }

View File

@ -0,0 +1,76 @@
package org.bigbluebutton.core.db
import org.bigbluebutton.common2.msgs.{BbbCommonEnvCoreMsg, NotifyAllInMeetingEvtMsg, NotifyRoleInMeetingEvtMsg, NotifyUserInMeetingEvtMsg}
import PostgresProfile.api._
import spray.json.JsValue
import scala.util.{Failure, Success}
import scala.concurrent.ExecutionContext.Implicits.global
case class NotificationDbModel(
// notificationId: String,
meetingId: String,
notificationType: String,
icon: String,
messageId: String,
messageDescription: String,
messageValues: JsValue,
role: Option[String],
userMeetingId: Option[String],
userId: Option[String],
createdAt: java.sql.Timestamp,
)
class NotificationDbTableDef(tag: Tag) extends Table[NotificationDbModel](tag, None, "notification") {
val meetingId = column[String]("meetingId")
val notificationType = column[String]("notificationType")
val icon = column[String]("icon")
val messageId = column[String]("messageId")
val messageDescription = column[String]("messageDescription")
val messageValues = column[JsValue]("messageValues")
val role = column[Option[String]]("role")
val userMeetingId = column[Option[String]]("userMeetingId")
val userId = column[Option[String]]("userId")
val createdAt = column[java.sql.Timestamp]("createdAt")
override def * = (meetingId, notificationType, icon, messageId, messageDescription, messageValues, role, userMeetingId, userId, createdAt) <> (NotificationDbModel.tupled, NotificationDbModel.unapply)
}
object NotificationDAO {
def insert(notification: BbbCommonEnvCoreMsg) = {
val (meetingId, notificationType, icon, messageId, messageDescription, messageValues, role, userId) = notification.core match {
case event: NotifyAllInMeetingEvtMsg =>
(event.body.meetingId, event.body.notificationType, event.body.icon, event.body.messageId, event.body.messageDescription, event.body.messageValues, None, None)
case event: NotifyRoleInMeetingEvtMsg =>
(event.body.meetingId, event.body.notificationType, event.body.icon, event.body.messageId, event.body.messageDescription, event.body.messageValues, Some(event.body.role), None)
case event: NotifyUserInMeetingEvtMsg =>
(event.body.meetingId, event.body.notificationType, event.body.icon, event.body.messageId, event.body.messageDescription, event.body.messageValues, None, Some(event.body.userId))
case _ =>
("","", "", "", "", Vector(""), None, None)
}
if (notificationType != "") {
DatabaseConnection.db.run(
TableQuery[NotificationDbTableDef].forceInsert(
NotificationDbModel(
meetingId,
notificationType,
icon,
messageId,
messageDescription,
JsonUtils.vectorToJson(messageValues),
role,
userMeetingId = userId match {
case Some(u) => Some(meetingId)
case _ => None
},
userId,
createdAt = new java.sql.Timestamp(System.currentTimeMillis())
)
)
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted/updated on Notification table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting/updating Notification: $e")
}
}
}
}

View File

@ -13,11 +13,12 @@ object Permission {
val allowedRoles = List("MODERATOR","VIEWER","PRESENTER") val allowedRoles = List("MODERATOR","VIEWER","PRESENTER")
} }
case class PluginDataChannelMessageDbModel( case class PluginDataChannelEntryDbModel(
meetingId: String, meetingId: String,
pluginName: String, pluginName: String,
dataChannel: String, channelName: String,
// messageId: Option[String] = None, subChannelName: String,
// entryId: Option[String] = None,
payloadJson: JsValue, payloadJson: JsValue,
fromUserId: String, fromUserId: String,
toRoles: Option[List[String]], toRoles: Option[List[String]],
@ -26,28 +27,30 @@ case class PluginDataChannelMessageDbModel(
deletedAt: Option[java.sql.Timestamp], deletedAt: Option[java.sql.Timestamp],
) )
class PluginDataChannelMessageDbTableDef(tag: Tag) extends Table[PluginDataChannelMessageDbModel](tag, None, "pluginDataChannelMessage") { class PluginDataChannelEntryDbTableDef(tag: Tag) extends Table[PluginDataChannelEntryDbModel](tag, None, "pluginDataChannelEntry") {
val meetingId = column[String]("meetingId", O.PrimaryKey) val meetingId = column[String]("meetingId", O.PrimaryKey)
val pluginName = column[String]("pluginName", O.PrimaryKey) val pluginName = column[String]("pluginName", O.PrimaryKey)
val dataChannel = column[String]("dataChannel", O.PrimaryKey) val channelName = column[String]("channelName", O.PrimaryKey)
// val messageId = column[Option[String]]("messageId", O.PrimaryKey) //// The messageId is generated by the database val subChannelName = column[String]("subChannelName")
// val entryId = column[Option[String]]("messageId", O.PrimaryKey) //// The messageId is generated by the database
val payloadJson = column[JsValue]("payloadJson") val payloadJson = column[JsValue]("payloadJson")
val fromUserId = column[String]("fromUserId") val fromUserId = column[String]("fromUserId")
val toRoles = column[Option[List[String]]]("toRoles") val toRoles = column[Option[List[String]]]("toRoles")
val toUserIds = column[Option[List[String]]]("toUserIds") val toUserIds = column[Option[List[String]]]("toUserIds")
val createdAt = column[java.sql.Timestamp]("createdAt") val createdAt = column[java.sql.Timestamp]("createdAt")
val deletedAt = column[Option[java.sql.Timestamp]]("deletedAt") val deletedAt = column[Option[java.sql.Timestamp]]("deletedAt")
override def * = (meetingId, pluginName, dataChannel, payloadJson, fromUserId, toRoles, toUserIds, createdAt, deletedAt) <> (PluginDataChannelMessageDbModel.tupled, PluginDataChannelMessageDbModel.unapply) override def * = (meetingId, pluginName, channelName, subChannelName, payloadJson, fromUserId, toRoles, toUserIds, createdAt, deletedAt) <> (PluginDataChannelEntryDbModel.tupled, PluginDataChannelEntryDbModel.unapply)
} }
object PluginDataChannelMessageDAO { object PluginDataChannelEntryDAO {
def insert(meetingId: String, pluginName: String, dataChannel: String, senderUserId: String, payloadJson: String, toRoles: List[String], toUserIds: List[String]) = { def insert(meetingId: String, pluginName: String, channelName: String, subChannelName: String, senderUserId: String, payloadJson: String, toRoles: List[String], toUserIds: List[String]) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[PluginDataChannelMessageDbTableDef].forceInsert( TableQuery[PluginDataChannelEntryDbTableDef].forceInsert(
PluginDataChannelMessageDbModel( PluginDataChannelEntryDbModel(
meetingId = meetingId, meetingId = meetingId,
pluginName = pluginName, pluginName = pluginName,
dataChannel = dataChannel, channelName = channelName,
subChannelName = subChannelName,
payloadJson = JsonUtils.stringToJson(payloadJson), payloadJson = JsonUtils.stringToJson(payloadJson),
fromUserId = senderUserId, fromUserId = senderUserId,
toRoles = toRoles.map(_.toUpperCase).filter(Permission.allowedRoles.contains) match { toRoles = toRoles.map(_.toUpperCase).filter(Permission.allowedRoles.contains) match {
@ -60,54 +63,62 @@ object PluginDataChannelMessageDAO {
) )
) )
).onComplete { ).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on PluginDataChannelMessage table!") case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on PluginDataChannelEntry table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting PluginDataChannelMessage: $e") case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting PluginDataChannelEntry: $e")
} }
} }
def reset(meetingId: String, pluginName: String, dataChannel: String) = { def reset(meetingId: String, pluginName: String,
channelName: String, subChannelName: String) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[PluginDataChannelMessageDbTableDef] TableQuery[PluginDataChannelEntryDbTableDef]
.filter(_.meetingId === meetingId) .filter(_.meetingId === meetingId)
.filter(_.pluginName === pluginName) .filter(_.pluginName === pluginName)
.filter(_.dataChannel === dataChannel) .filter(_.channelName === channelName)
.filter(_.subChannelName === subChannelName)
.filter(_.deletedAt.isEmpty)
.map(u => (u.deletedAt)) .map(u => (u.deletedAt))
.update(Some(new java.sql.Timestamp(System.currentTimeMillis()))) .update(Some(new java.sql.Timestamp(System.currentTimeMillis())))
).onComplete { ).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated deleted=now() on pluginDataChannelMessage table!") case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated deleted=now() on pluginDataChannelEntry table!")
case Failure(e) => DatabaseConnection.logger.error(s"Error updating deleted=now() pluginDataChannelMessage: $e") case Failure(e) => DatabaseConnection.logger.error(s"Error updating deleted=now() pluginDataChannelEntry: $e")
} }
} }
def getMessageSender(meetingId: String, pluginName: String, dataChannel: String, messageId: String): String = { def getMessageSender(meetingId: String, pluginName: String, channelName: String,
subChannelName: String, entryId: String): String = {
val query = sql"""SELECT "fromUserId" val query = sql"""SELECT "fromUserId"
FROM "pluginDataChannelMessage" FROM "pluginDataChannelEntry"
WHERE "deletedAt" is null WHERE "deletedAt" is null
AND "meetingId" = ${meetingId} AND "meetingId" = ${meetingId}
AND "pluginName" = ${pluginName} AND "pluginName" = ${pluginName}
AND "dataChannel" = ${dataChannel} AND "channelName" = ${channelName}
AND "messageId" = ${messageId}""".as[String].headOption AND "subChannelName" = ${subChannelName}
AND "entryId" = ${entryId}""".as[String].headOption
Await.result(DatabaseConnection.db.run(query), Duration.Inf) match { Await.result(DatabaseConnection.db.run(query), Duration.Inf) match {
case Some(userId) => userId case Some(userId) => userId
case None => { case None => {
logger.debug("Message {} not found in database (maybe it was deleted).", messageId) logger.debug("Message {} not found in database (maybe it was deleted).", entryId)
"" ""
} }
} }
} }
def delete(meetingId: String, pluginName: String, dataChannel: String, messageId: String) = { def delete(meetingId: String, pluginName: String,
channelName: String, subChannelName: String, entryId: String) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
sqlu"""UPDATE "pluginDataChannelMessage" SET sqlu"""UPDATE "pluginDataChannelEntry" SET
"deletedAt" = current_timestamp "deletedAt" = current_timestamp
WHERE "meetingId" = ${meetingId} WHERE "deletedAt" is null
AND "meetingId" = ${meetingId}
AND "pluginName" = ${pluginName} AND "pluginName" = ${pluginName}
AND "dataChannel" = ${dataChannel} AND "channelName" = ${channelName}
AND "messageId" = ${messageId}""" AND "subChannelName" = ${subChannelName}
AND "entryId" = ${entryId}"""
).onComplete { ).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated deleted=now() on pluginDataChannelMessage table!") case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated deleted=now() on pluginDataChannelEntry table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating deleted=now() pluginDataChannelMessage: $e") case Failure(e) => DatabaseConnection.logger.debug(s"Error updating deleted=now() pluginDataChannelEntry: $e")
} }
} }

View File

@ -10,23 +10,26 @@ import scala.util.{ Failure, Success }
case class PollResponseDbModel( case class PollResponseDbModel(
pollId: String, pollId: String,
optionId: Option[Int], optionId: Option[Int],
meetingId: Option[String],
userId: Option[String] userId: Option[String]
) )
class PollResponseDbTableDef(tag: Tag) extends Table[PollResponseDbModel](tag, None, "poll_response") { class PollResponseDbTableDef(tag: Tag) extends Table[PollResponseDbModel](tag, None, "poll_response") {
val pollId = column[String]("pollId") val pollId = column[String]("pollId")
val optionId = column[Option[Int]]("optionId") val optionId = column[Option[Int]]("optionId")
val meetingId = column[Option[String]]("meetingId")
val userId = column[Option[String]]("userId") val userId = column[Option[String]]("userId")
val * = (pollId, optionId, userId) <> (PollResponseDbModel.tupled, PollResponseDbModel.unapply) val * = (pollId, optionId, meetingId, userId) <> (PollResponseDbModel.tupled, PollResponseDbModel.unapply)
} }
object PollResponseDAO { object PollResponseDAO {
def insert(poll: Poll, userId: String, seqOptionIds: Seq[Int]) = { def insert(poll: Poll, meetingId: String, userId: String, seqOptionIds: Seq[Int]) = {
//Clear previous responses of the user and add all //Clear previous responses of the user and add all
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[PollResponseDbTableDef] TableQuery[PollResponseDbTableDef]
.filter(_.pollId === poll.id) .filter(_.pollId === poll.id)
.filter(_.meetingId === meetingId)
.filter(_.userId === userId) .filter(_.userId === userId)
.delete .delete
).onComplete { ).onComplete {
@ -39,6 +42,9 @@ object PollResponseDAO {
PollResponseDbModel( PollResponseDbModel(
pollId = poll.id, pollId = poll.id,
optionId = Some(optionId), optionId = Some(optionId),
meetingId = {
if (poll.isSecret) None else Some(meetingId)
},
userId = { userId = {
if (poll.isSecret) None else Some(userId) if (poll.isSecret) None else Some(userId)
} }
@ -57,6 +63,7 @@ object PollResponseDAO {
PollResponseDbModel( PollResponseDbModel(
pollId = poll.id, pollId = poll.id,
optionId = None, optionId = None,
meetingId = Some(meetingId),
userId = Some(userId) userId = Some(userId)
) )
) )

View File

@ -3,6 +3,7 @@ package org.bigbluebutton.core.db
import com.github.tminglei.slickpg._ import com.github.tminglei.slickpg._
import org.apache.pekko.http.scaladsl.model.ParsingException import org.apache.pekko.http.scaladsl.model.ParsingException
import org.bigbluebutton.common2.domain.SimpleVoteOutVO import org.bigbluebutton.common2.domain.SimpleVoteOutVO
import spray.json.DefaultJsonProtocol.{ StringJsonFormat, vectorFormat }
import spray.json.{ JsArray, JsBoolean, JsNumber, JsObject, JsString, JsValue, JsonWriter, _ } import spray.json.{ JsArray, JsBoolean, JsNumber, JsObject, JsString, JsValue, JsonWriter, _ }
import scala.util.{ Failure, Success, Try } import scala.util.{ Failure, Success, Try }
@ -55,6 +56,10 @@ object JsonUtils {
genericMap.toJson genericMap.toJson
} }
def vectorToJson(genericVector: Vector[String]) = {
genericVector.toJson
}
def stringToJson(jsonString: String): JsValue = { def stringToJson(jsonString: String): JsValue = {
Try(jsonString.parseJson) match { Try(jsonString.parseJson) match {
case Success(jsValue) => jsValue case Success(jsValue) => jsValue

View File

@ -1,13 +1,14 @@
package org.bigbluebutton.core.db package org.bigbluebutton.core.db
import org.bigbluebutton.common2.msgs.AnnotationVO import org.bigbluebutton.common2.msgs.AnnotationVO
import PostgresProfile.api._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{ Failure, Success } import scala.util.{ Failure, Success }
import slick.jdbc.PostgresProfile.api._
import scala.concurrent.ExecutionContext.Implicits.global
case class PresAnnotationDbModel( case class PresAnnotationDbModel(
annotationId: String, annotationId: String,
pageId: String, pageId: String,
meetingId: String,
userId: String, userId: String,
annotationInfo: String, annotationInfo: String,
lastHistorySequence: Int, lastHistorySequence: Int,
@ -17,17 +18,18 @@ case class PresAnnotationDbModel(
class PresAnnotationDbTableDef(tag: Tag) extends Table[PresAnnotationDbModel](tag, None, "pres_annotation") { class PresAnnotationDbTableDef(tag: Tag) extends Table[PresAnnotationDbModel](tag, None, "pres_annotation") {
val annotationId = column[String]("annotationId", O.PrimaryKey) val annotationId = column[String]("annotationId", O.PrimaryKey)
val pageId = column[String]("pageId") val pageId = column[String]("pageId")
val meetingId = column[String]("meetingId")
val userId = column[String]("userId") val userId = column[String]("userId")
val annotationInfo = column[String]("annotationInfo") val annotationInfo = column[String]("annotationInfo")
val lastHistorySequence = column[Int]("lastHistorySequence") val lastHistorySequence = column[Int]("lastHistorySequence")
val lastUpdatedAt = column[java.sql.Timestamp]("lastUpdatedAt") val lastUpdatedAt = column[java.sql.Timestamp]("lastUpdatedAt")
// def whiteboard = foreignKey("whiteboard_fk", whiteboardId, Whiteboards)(_.whiteboardId, onDelete = ForeignKeyAction.Cascade) // def whiteboard = foreignKey("whiteboard_fk", whiteboardId, Whiteboards)(_.whiteboardId, onDelete = ForeignKeyAction.Cascade)
def * = (annotationId, pageId, userId, annotationInfo, lastHistorySequence, lastUpdatedAt) <> (PresAnnotationDbModel.tupled, PresAnnotationDbModel.unapply) def * = (annotationId, pageId, meetingId, userId, annotationInfo, lastHistorySequence, lastUpdatedAt) <> (PresAnnotationDbModel.tupled, PresAnnotationDbModel.unapply)
} }
object PresAnnotationDAO { object PresAnnotationDAO {
def insertOrUpdate(annotation: AnnotationVO, annotationDiff: AnnotationVO) = { def insertOrUpdate(meetingId: String, annotation: AnnotationVO, annotationDiff: AnnotationVO) = {
PresAnnotationHistoryDAO.insert(annotationDiff).onComplete { PresAnnotationHistoryDAO.insert(meetingId, annotationDiff).onComplete {
case Success(sequence) => { case Success(sequence) => {
DatabaseConnection.logger.debug(s"Sequence generated to PresAnnotationHistory record: $sequence") DatabaseConnection.logger.debug(s"Sequence generated to PresAnnotationHistory record: $sequence")
DatabaseConnection.db.run( DatabaseConnection.db.run(
@ -35,6 +37,7 @@ object PresAnnotationDAO {
PresAnnotationDbModel( PresAnnotationDbModel(
annotationId = annotation.id, annotationId = annotation.id,
pageId = annotation.wbId, pageId = annotation.wbId,
meetingId = meetingId,
userId = annotation.userId, userId = annotation.userId,
annotationInfo = JsonUtils.mapToJson(annotation.annotationInfo).compactPrint, annotationInfo = JsonUtils.mapToJson(annotation.annotationInfo).compactPrint,
lastHistorySequence = sequence.getOrElse(0), lastHistorySequence = sequence.getOrElse(0),
@ -42,8 +45,8 @@ object PresAnnotationDAO {
) )
) )
).onComplete { ).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on PresAnnotation table!") case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted or updated on PresAnnotation table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting PresAnnotation: $e") case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting or updating PresAnnotation: $e")
} }
} }
@ -51,15 +54,44 @@ object PresAnnotationDAO {
} }
} }
def delete(wbId: String, userId: String, annotationId: String) = { def prepareInsertOrUpdate(meetingId: String, annotation: AnnotationVO) = {
TableQuery[PresAnnotationDbTableDef].insertOrUpdate(
PresAnnotationDbModel(
annotationId = annotation.id,
pageId = annotation.wbId,
meetingId = meetingId,
userId = annotation.userId,
annotationInfo = JsonUtils.mapToJson(annotation.annotationInfo).compactPrint,
lastHistorySequence = 0,
lastUpdatedAt = new java.sql.Timestamp(System.currentTimeMillis())
)
)
}
PresAnnotationHistoryDAO.delete(wbId, userId, annotationId).onComplete { def insertOrUpdateMap(meetingId: String, annotations: Array[AnnotationVO]) = {
DatabaseConnection.db.run(
DBIO.sequence(
annotations.map { annotation =>
prepareInsertOrUpdate(meetingId, annotation)
}.toVector
).transactionally
)
.onComplete {
case Success(rowsAffected) =>
DatabaseConnection.logger.debug(s"${rowsAffected.sum} row(s) inserted or updated on PresAnnotation table!")
case Failure(e) =>
DatabaseConnection.logger.debug(s"Error inserting or updating PresAnnotation: $e")
}
}
def delete(wbId: String, meetingId: String, userId: String, annotationId: String) = {
PresAnnotationHistoryDAO.delete(wbId, meetingId, userId, annotationId).onComplete {
case Success(sequence) => { case Success(sequence) => {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[PresAnnotationDbTableDef] TableQuery[PresAnnotationDbTableDef]
.filter(_.annotationId === annotationId) .filter(_.annotationId === annotationId)
.map(a => (a.annotationInfo, a.lastHistorySequence, a.lastUpdatedAt)) .map(a => (a.annotationInfo, a.lastHistorySequence, a.meetingId, a.userId, a.lastUpdatedAt))
.update("", sequence.getOrElse(0), new java.sql.Timestamp(System.currentTimeMillis())) .update("", sequence.getOrElse(0), meetingId, userId, new java.sql.Timestamp(System.currentTimeMillis()))
).onComplete { ).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated annotationInfo=null on PresAnnotation table!") case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated annotationInfo=null on PresAnnotation table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating annotationInfo=null PresAnnotation: $e") case Failure(e) => DatabaseConnection.logger.debug(s"Error updating annotationInfo=null PresAnnotation: $e")
@ -69,4 +101,16 @@ object PresAnnotationDAO {
} }
} }
def delete(meetingId: String, userId: String, annotationIds: Array[String]) = {
DatabaseConnection.db.run(
TableQuery[PresAnnotationDbTableDef]
.filter(_.annotationId inSet annotationIds)
.map(a => (a.annotationInfo, a.lastHistorySequence, a.meetingId, a.userId, a.lastUpdatedAt))
.update("", 0, meetingId, userId, new java.sql.Timestamp(System.currentTimeMillis()))
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated annotationInfo=null on PresAnnotation table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating annotationInfo=null PresAnnotation: $e")
}
}
} }

View File

@ -7,6 +7,7 @@ case class PresAnnotationHistoryDbModel(
sequence: Option[Int] = None, sequence: Option[Int] = None,
annotationId: String, annotationId: String,
pageId: String, pageId: String,
meetingId: String,
userId: String, userId: String,
annotationInfo: String annotationInfo: String
// lastUpdatedAt: java.sql.Timestamp = new java.sql.Timestamp(System.currentTimeMillis()) // lastUpdatedAt: java.sql.Timestamp = new java.sql.Timestamp(System.currentTimeMillis())
@ -16,16 +17,17 @@ class PresAnnotationHistoryDbTableDef(tag: Tag) extends Table[PresAnnotationHist
val sequence = column[Option[Int]]("sequence", O.PrimaryKey, O.AutoInc) val sequence = column[Option[Int]]("sequence", O.PrimaryKey, O.AutoInc)
val annotationId = column[String]("annotationId") val annotationId = column[String]("annotationId")
val pageId = column[String]("pageId") val pageId = column[String]("pageId")
val meetingId = column[String]("meetingId")
val userId = column[String]("userId") val userId = column[String]("userId")
val annotationInfo = column[String]("annotationInfo") val annotationInfo = column[String]("annotationInfo")
// val lastUpdatedAt = column[java.sql.Timestamp]("lastUpdatedAt") // val lastUpdatedAt = column[java.sql.Timestamp]("lastUpdatedAt")
// def whiteboard = foreignKey("whiteboard_fk", whiteboardId, Whiteboards)(_.whiteboardId, onDelete = ForeignKeyAction.Cascade) // def whiteboard = foreignKey("whiteboard_fk", whiteboardId, Whiteboards)(_.whiteboardId, onDelete = ForeignKeyAction.Cascade)
def * = (sequence, annotationId, pageId, userId, annotationInfo) <> (PresAnnotationHistoryDbModel.tupled, PresAnnotationHistoryDbModel.unapply) def * = (sequence, annotationId, pageId, meetingId, userId, annotationInfo) <> (PresAnnotationHistoryDbModel.tupled, PresAnnotationHistoryDbModel.unapply)
} }
object PresAnnotationHistoryDAO { object PresAnnotationHistoryDAO {
def insert(annotationDiff: AnnotationVO) = { def insert(meetingId: String, annotationDiff: AnnotationVO) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[PresAnnotationHistoryDbTableDef].returning( TableQuery[PresAnnotationHistoryDbTableDef].returning(
TableQuery[PresAnnotationHistoryDbTableDef].map(_.sequence) TableQuery[PresAnnotationHistoryDbTableDef].map(_.sequence)
@ -33,13 +35,14 @@ object PresAnnotationHistoryDAO {
None, None,
annotationId = annotationDiff.id, annotationId = annotationDiff.id,
pageId = annotationDiff.wbId, pageId = annotationDiff.wbId,
meetingId = meetingId,
userId = annotationDiff.userId, userId = annotationDiff.userId,
annotationInfo = JsonUtils.mapToJson(annotationDiff.annotationInfo).compactPrint annotationInfo = JsonUtils.mapToJson(annotationDiff.annotationInfo).compactPrint
) )
) )
} }
def delete(wbId: String, userId: String, annotationId: String) = { def delete(wbId: String, meetingId: String, userId: String, annotationId: String) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[PresAnnotationHistoryDbTableDef].returning( TableQuery[PresAnnotationHistoryDbTableDef].returning(
TableQuery[PresAnnotationHistoryDbTableDef].map(_.sequence) TableQuery[PresAnnotationHistoryDbTableDef].map(_.sequence)
@ -47,6 +50,7 @@ object PresAnnotationHistoryDAO {
None, None,
annotationId = annotationId, annotationId = annotationId,
pageId = wbId, pageId = wbId,
meetingId = meetingId,
userId = userId, userId = userId,
annotationInfo = "" annotationInfo = ""
) )

View File

@ -8,6 +8,7 @@ import scala.util.{ Failure, Success }
case class PresPageCursorDbModel( case class PresPageCursorDbModel(
pageId: String, pageId: String,
meetingId: String,
userId: String, userId: String,
xPercent: Double, xPercent: Double,
yPercent: Double, yPercent: Double,
@ -16,11 +17,11 @@ case class PresPageCursorDbModel(
class PresPageCursorDbTableDef(tag: Tag) extends Table[PresPageCursorDbModel](tag, None, "pres_page_cursor") { class PresPageCursorDbTableDef(tag: Tag) extends Table[PresPageCursorDbModel](tag, None, "pres_page_cursor") {
override def * = ( override def * = (
pageId, userId, xPercent, yPercent, lastUpdatedAt pageId, meetingId, userId, xPercent, yPercent, lastUpdatedAt
) <> (PresPageCursorDbModel.tupled, PresPageCursorDbModel.unapply) ) <> (PresPageCursorDbModel.tupled, PresPageCursorDbModel.unapply)
def pk = primaryKey("pres_page_cursor_pkey", (pageId, userId)) val pageId = column[String]("pageId", O.PrimaryKey)
val pageId = column[String]("pageId") val meetingId = column[String]("meetingId", O.PrimaryKey)
val userId = column[String]("userId") val userId = column[String]("userId", O.PrimaryKey)
val xPercent = column[Double]("xPercent") val xPercent = column[Double]("xPercent")
val yPercent = column[Double]("yPercent") val yPercent = column[Double]("yPercent")
val lastUpdatedAt = column[java.sql.Timestamp]("lastUpdatedAt") val lastUpdatedAt = column[java.sql.Timestamp]("lastUpdatedAt")
@ -28,11 +29,12 @@ class PresPageCursorDbTableDef(tag: Tag) extends Table[PresPageCursorDbModel](ta
object PresPageCursorDAO { object PresPageCursorDAO {
def insertOrUpdate(pageId: String, userId: String, xPercent: Double, yPercent: Double) = { def insertOrUpdate(pageId: String, meetingId: String, userId: String, xPercent: Double, yPercent: Double) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[PresPageCursorDbTableDef].insertOrUpdate( TableQuery[PresPageCursorDbTableDef].insertOrUpdate(
PresPageCursorDbModel( PresPageCursorDbModel(
pageId = pageId, pageId = pageId,
meetingId = meetingId,
userId = userId, userId = userId,
xPercent = xPercent, xPercent = xPercent,
yPercent = yPercent, yPercent = yPercent,

View File

@ -8,33 +8,35 @@ import scala.util.{Failure, Success}
case class PresPageWritersDbModel( case class PresPageWritersDbModel(
pageId: String, pageId: String,
meetingId: String,
userId: String, userId: String,
changedModeOn: Long, changedModeOn: Long,
) )
class PresPageWritersDbTableDef(tag: Tag) extends Table[PresPageWritersDbModel](tag, None, "pres_page_writers") { class PresPageWritersDbTableDef(tag: Tag) extends Table[PresPageWritersDbModel](tag, None, "pres_page_writers") {
override def * = ( override def * = (
pageId, userId, changedModeOn) <> (PresPageWritersDbModel.tupled, PresPageWritersDbModel.unapply) pageId, meetingId, userId, changedModeOn) <> (PresPageWritersDbModel.tupled, PresPageWritersDbModel.unapply)
def pk = primaryKey("pres_page_writers_pkey", (pageId, userId)) val pageId = column[String]("pageId", O.PrimaryKey)
val pageId = column[String]("pageId") val meetingId = column[String]("meetingId", O.PrimaryKey)
val userId = column[String]("userId") val userId = column[String]("userId", O.PrimaryKey)
val changedModeOn = column[Long]("changedModeOn") val changedModeOn = column[Long]("changedModeOn")
} }
object PresPageWritersDAO { object PresPageWritersDAO {
def updateMultiuser(whiteboard: Whiteboard) = { def updateMultiuser(meetingId: String, whiteboard: Whiteboard) = {
val deleteQuery = TableQuery[PresPageWritersDbTableDef] val deleteQuery = TableQuery[PresPageWritersDbTableDef]
.filter(_.pageId === whiteboard.id) .filter(_.pageId === whiteboard.id)
if(whiteboard.multiUser.length > 0) { if(whiteboard.multiUser.length > 0) {
deleteQuery.filter(_.meetingId === meetingId)
deleteQuery.filterNot(_.userId inSet whiteboard.multiUser) deleteQuery.filterNot(_.userId inSet whiteboard.multiUser)
} }
DatabaseConnection.db.run(deleteQuery.delete).onComplete { DatabaseConnection.db.run(deleteQuery.delete).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"Users deleted from Whiteboard ${whiteboard.id}") case Success(rowsAffected) => DatabaseConnection.logger.debug(s"Users deleted from pres_page_writers ${whiteboard.id}")
case Failure(e) => DatabaseConnection.logger.error(s"Error deleting users from whiteboard: $e") case Failure(e) => DatabaseConnection.logger.error(s"Error deleting users from pres_page_writers: $e")
} }
for { for {
@ -44,6 +46,7 @@ object PresPageWritersDAO {
TableQuery[PresPageWritersDbTableDef].insertOrUpdate( TableQuery[PresPageWritersDbTableDef].insertOrUpdate(
PresPageWritersDbModel( PresPageWritersDbModel(
pageId = whiteboard.id, pageId = whiteboard.id,
meetingId = meetingId,
userId = userId, userId = userId,
changedModeOn = whiteboard.changedModeOn changedModeOn = whiteboard.changedModeOn
) )

View File

@ -35,10 +35,11 @@ object SharedNotesSessionDAO {
} }
} }
def delete(intId: String, sessionId: String) = { def delete(meetingId: String, userId: String, sessionId: String) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[SharedNotesSessionDbTableDef] TableQuery[SharedNotesSessionDbTableDef]
.filter(_.userId === intId) .filter(_.meetingId === meetingId)
.filter(_.userId === userId)
.filter(_.sessionId === sessionId) .filter(_.sessionId === sessionId)
.delete .delete
).onComplete { ).onComplete {

View File

@ -1,5 +1,6 @@
package org.bigbluebutton.core.db package org.bigbluebutton.core.db
import org.bigbluebutton.ClientSettings.{getConfigPropertyValueByPathAsBooleanOrElse, getConfigPropertyValueByPathAsIntOrElse}
import org.bigbluebutton.core.apps.TimerModel import org.bigbluebutton.core.apps.TimerModel
import org.bigbluebutton.core.apps.TimerModel.{getAccumulated, getEndedAt, getIsActive, getRunning, getStartedAt, getStopwatch, getTime, getTrack} import org.bigbluebutton.core.apps.TimerModel.{getAccumulated, getEndedAt, getIsActive, getRunning, getStartedAt, getStopwatch, getTime, getTrack}
import slick.jdbc.PostgresProfile.api._ import slick.jdbc.PostgresProfile.api._
@ -33,7 +34,12 @@ class TimerDbTableDef(tag: Tag) extends Table[TimerDbModel](tag, None, "timer")
} }
object TimerDAO { object TimerDAO {
def insert(meetingId: String) = { def insert(meetingId: String, clientSettings: Map[String, Object]) = {
val timerEnabled = getConfigPropertyValueByPathAsBooleanOrElse(clientSettings, "public.timer.enabled", alternativeValue = true)
if(timerEnabled) {
val timerDefaultTimeInMinutes = getConfigPropertyValueByPathAsIntOrElse(clientSettings, "public.timer.time", 5)
val timerDefaultTimeInMilli = timerDefaultTimeInMinutes * 60000
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[TimerDbTableDef].insertOrUpdate( TableQuery[TimerDbTableDef].insertOrUpdate(
TimerDbModel( TimerDbModel(
@ -41,7 +47,7 @@ object TimerDAO {
stopwatch = true, stopwatch = true,
running = false, running = false,
active = false, active = false,
time = 300000, time = timerDefaultTimeInMilli,
accumulated = 0, accumulated = 0,
startedOn = 0, startedOn = 0,
endedOn = 0, endedOn = 0,
@ -53,6 +59,7 @@ object TimerDAO {
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting Timer: $e") case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting Timer: $e")
} }
} }
}
def update(meetingId: String, timerModel: TimerModel) = { def update(meetingId: String, timerModel: TimerModel) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(

View File

@ -8,6 +8,7 @@ import scala.util.{Failure, Success }
case class UserBreakoutRoomDbModel( case class UserBreakoutRoomDbModel(
breakoutRoomId: String, breakoutRoomId: String,
meetingId: String,
userId: String, userId: String,
isDefaultName: Boolean, isDefaultName: Boolean,
sequence: Int, sequence: Int,
@ -17,7 +18,8 @@ case class UserBreakoutRoomDbModel(
class UserBreakoutRoomDbTableDef(tag: Tag) extends Table[UserBreakoutRoomDbModel](tag, None, "user_breakoutRoom") { class UserBreakoutRoomDbTableDef(tag: Tag) extends Table[UserBreakoutRoomDbModel](tag, None, "user_breakoutRoom") {
override def * = ( override def * = (
breakoutRoomId, userId, isDefaultName, sequence, shortName, currentlyInRoom) <> (UserBreakoutRoomDbModel.tupled, UserBreakoutRoomDbModel.unapply) breakoutRoomId, meetingId, userId, isDefaultName, sequence, shortName, currentlyInRoom) <> (UserBreakoutRoomDbModel.tupled, UserBreakoutRoomDbModel.unapply)
val meetingId = column[String]("meetingId", O.PrimaryKey)
val userId = column[String]("userId", O.PrimaryKey) val userId = column[String]("userId", O.PrimaryKey)
val breakoutRoomId = column[String]("breakoutRoomId") val breakoutRoomId = column[String]("breakoutRoomId")
val isDefaultName = column[Boolean]("isDefaultName") val isDefaultName = column[Boolean]("isDefaultName")
@ -28,11 +30,11 @@ class UserBreakoutRoomDbTableDef(tag: Tag) extends Table[UserBreakoutRoomDbModel
object UserBreakoutRoomDAO { object UserBreakoutRoomDAO {
def updateLastBreakoutRoom(userId: String, breakoutRoom: BreakoutRoom2x) = { def updateLastBreakoutRoom(meetingId: String, userId: String, breakoutRoom: BreakoutRoom2x) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserBreakoutRoomDbTableDef].insertOrUpdate( TableQuery[UserBreakoutRoomDbTableDef].insertOrUpdate(
UserBreakoutRoomDbModel( UserBreakoutRoomDbModel(
meetingId = meetingId,
userId = userId, userId = userId,
breakoutRoomId = breakoutRoom.id, breakoutRoomId = breakoutRoom.id,
isDefaultName = breakoutRoom.isDefaultName, isDefaultName = breakoutRoom.isDefaultName,
@ -49,10 +51,11 @@ object UserBreakoutRoomDAO {
} }
} }
def updateLastBreakoutRoom(usersInRoom: Vector[String], breakoutRoom: BreakoutRoom2x) = { def updateLastBreakoutRoom(meetingId:String, usersInRoom: Vector[String], breakoutRoom: BreakoutRoom2x) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserBreakoutRoomDbTableDef] TableQuery[UserBreakoutRoomDbTableDef]
.filter(_.meetingId === meetingId)
.filterNot(_.userId inSet usersInRoom) .filterNot(_.userId inSet usersInRoom)
.filter(_.breakoutRoomId === breakoutRoom.id) .filter(_.breakoutRoomId === breakoutRoom.id)
.map(u_bk => u_bk.currentlyInRoom) .map(u_bk => u_bk.currentlyInRoom)
@ -68,6 +71,7 @@ object UserBreakoutRoomDAO {
} yield { } yield {
TableQuery[UserBreakoutRoomDbTableDef].insertOrUpdate( TableQuery[UserBreakoutRoomDbTableDef].insertOrUpdate(
UserBreakoutRoomDbModel( UserBreakoutRoomDbModel(
meetingId = meetingId,
userId = userId, userId = userId,
breakoutRoomId = breakoutRoom.id, breakoutRoomId = breakoutRoom.id,
isDefaultName = breakoutRoom.isDefaultName, isDefaultName = breakoutRoom.isDefaultName,

View File

@ -8,24 +8,27 @@ import scala.util.{Failure, Success}
case class UserCameraDbModel( case class UserCameraDbModel(
streamId: String, streamId: String,
meetingId: String,
userId: String, userId: String,
) )
class UserCameraDbTableDef(tag: Tag) extends Table[UserCameraDbModel](tag, None, "user_camera") { class UserCameraDbTableDef(tag: Tag) extends Table[UserCameraDbModel](tag, None, "user_camera") {
override def * = ( override def * = (
streamId, userId) <> (UserCameraDbModel.tupled, UserCameraDbModel.unapply) streamId, meetingId, userId) <> (UserCameraDbModel.tupled, UserCameraDbModel.unapply)
val streamId = column[String]("streamId", O.PrimaryKey) val streamId = column[String]("streamId", O.PrimaryKey)
val meetingId = column[String]("meetingId")
val userId = column[String]("userId") val userId = column[String]("userId")
} }
object UserCameraDAO { object UserCameraDAO {
def insert(webcam: WebcamStream) = { def insert(meetingId: String, webcam: WebcamStream) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserCameraDbTableDef].forceInsert( TableQuery[UserCameraDbTableDef].forceInsert(
UserCameraDbModel( UserCameraDbModel(
streamId = webcam.streamId, streamId = webcam.streamId,
userId = webcam.userId meetingId = meetingId,
userId = webcam.userId,
) )
) )
).onComplete { ).onComplete {

View File

@ -14,7 +14,7 @@ case class UserClientSettingsDbModel(
class UserClientSettingsDbTableDef(tag: Tag) extends Table[UserClientSettingsDbModel](tag, "user_clientSettings") { class UserClientSettingsDbTableDef(tag: Tag) extends Table[UserClientSettingsDbModel](tag, "user_clientSettings") {
val userId = column[String]("userId", O.PrimaryKey) val userId = column[String]("userId", O.PrimaryKey)
val meetingId = column[String]("meetingId") val meetingId = column[String]("meetingId", O.PrimaryKey)
val userClientSettingsJson = column[JsValue]("userClientSettingsJson") val userClientSettingsJson = column[JsValue]("userClientSettingsJson")
override def * : ProvenShape[UserClientSettingsDbModel] = (userId, meetingId, userClientSettingsJson) <> (UserClientSettingsDbModel.tupled, UserClientSettingsDbModel.unapply) override def * : ProvenShape[UserClientSettingsDbModel] = (userId, meetingId, userClientSettingsJson) <> (UserClientSettingsDbModel.tupled, UserClientSettingsDbModel.unapply)

View File

@ -8,19 +8,21 @@ case class UserConnectionStatusDbModel(
userId: String, userId: String,
meetingId: String, meetingId: String,
connectionAliveAt: Option[java.sql.Timestamp], connectionAliveAt: Option[java.sql.Timestamp],
userClientResponseAt: Option[java.sql.Timestamp], networkRttInMs: Option[Double],
networkRttInMs: Option[Double] status: String,
statusUpdatedAt: Option[java.sql.Timestamp]
) )
class UserConnectionStatusDbTableDef(tag: Tag) extends Table[UserConnectionStatusDbModel](tag, None, "user_connectionStatus") { class UserConnectionStatusDbTableDef(tag: Tag) extends Table[UserConnectionStatusDbModel](tag, None, "user_connectionStatus") {
override def * = ( override def * = (
userId, meetingId, connectionAliveAt, userClientResponseAt, networkRttInMs userId, meetingId, connectionAliveAt, networkRttInMs, status, statusUpdatedAt
) <> (UserConnectionStatusDbModel.tupled, UserConnectionStatusDbModel.unapply) ) <> (UserConnectionStatusDbModel.tupled, UserConnectionStatusDbModel.unapply)
val userId = column[String]("userId", O.PrimaryKey) val userId = column[String]("userId", O.PrimaryKey)
val meetingId = column[String]("meetingId") val meetingId = column[String]("meetingId", O.PrimaryKey)
val connectionAliveAt = column[Option[java.sql.Timestamp]]("connectionAliveAt") val connectionAliveAt = column[Option[java.sql.Timestamp]]("connectionAliveAt")
val userClientResponseAt = column[Option[java.sql.Timestamp]]("userClientResponseAt")
val networkRttInMs = column[Option[Double]]("networkRttInMs") val networkRttInMs = column[Option[Double]]("networkRttInMs")
val status = column[String]("status")
val statusUpdatedAt = column[Option[java.sql.Timestamp]]("statusUpdatedAt")
} }
object UserConnectionStatusDAO { object UserConnectionStatusDAO {
@ -32,8 +34,9 @@ object UserConnectionStatusDAO {
userId = userId, userId = userId,
meetingId = meetingId, meetingId = meetingId,
connectionAliveAt = None, connectionAliveAt = None,
userClientResponseAt = None, networkRttInMs = None,
networkRttInMs = None status = "normal",
statusUpdatedAt = None
) )
) )
).onComplete { ).onComplete {
@ -42,28 +45,24 @@ object UserConnectionStatusDAO {
} }
} }
def updateUserAlive(userId: String) = { def updateUserAlive(meetingId: String, userId: String, rtt: Option[Double], status: String) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserConnectionStatusDbTableDef] TableQuery[UserConnectionStatusDbTableDef]
.filter(_.meetingId === meetingId)
.filter(_.userId === userId) .filter(_.userId === userId)
.map(t => (t.connectionAliveAt)) .map(t => (t.connectionAliveAt, t.networkRttInMs, t.status, t.statusUpdatedAt))
.update(Some(new java.sql.Timestamp(System.currentTimeMillis()))) .update(
(
Some(new java.sql.Timestamp(System.currentTimeMillis())),
rtt,
status,
Some(new java.sql.Timestamp(System.currentTimeMillis())),
)
)
).onComplete { ).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated connectionAliveAt on UserConnectionStatus table!") case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated connectionAliveAt on UserConnectionStatus table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating connectionAliveAt on UserConnectionStatus: $e") case Failure(e) => DatabaseConnection.logger.debug(s"Error updating connectionAliveAt on UserConnectionStatus: $e")
} }
} }
def updateUserRtt(userId: String, networkRttInMs: Double) = {
DatabaseConnection.db.run(
TableQuery[UserConnectionStatusDbTableDef]
.filter(_.userId === userId)
.map(t => (t.networkRttInMs, t.userClientResponseAt))
.update((Some(networkRttInMs), Some(new java.sql.Timestamp(System.currentTimeMillis()))))
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated networkRttInMs on UserConnectionStatus table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating networkRttInMs on UserConnectionStatus: $e")
}
}
} }

View File

@ -7,27 +7,30 @@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{ Failure, Success } import scala.util.{ Failure, Success }
case class UserCustomParameterDbModel( case class UserCustomParameterDbModel(
meetingId: String,
userId: String, userId: String,
parameter: String, parameter: String,
value: String value: String
) )
class UserCustomParameterDbTableDef(tag: Tag) extends Table[UserCustomParameterDbModel](tag, "user_customParameter") { class UserCustomParameterDbTableDef(tag: Tag) extends Table[UserCustomParameterDbModel](tag, "user_customParameter") {
val meetingId = column[String]("meetingId", O.PrimaryKey)
val userId = column[String]("userId", O.PrimaryKey) val userId = column[String]("userId", O.PrimaryKey)
val parameter = column[String]("parameter", O.PrimaryKey) val parameter = column[String]("parameter", O.PrimaryKey)
val value = column[String]("value") val value = column[String]("value")
override def * : ProvenShape[UserCustomParameterDbModel] = (userId, parameter, value) <> (UserCustomParameterDbModel.tupled, UserCustomParameterDbModel.unapply) override def * : ProvenShape[UserCustomParameterDbModel] = (meetingId, userId, parameter, value) <> (UserCustomParameterDbModel.tupled, UserCustomParameterDbModel.unapply)
} }
object UserCustomParameterDAO { object UserCustomParameterDAO {
def insert(userId: String, customParameters: Map[String, String]) = { def insert(meetingId: String, userId: String, customParameters: Map[String, String]) = {
DatabaseConnection.db.run(DBIO.sequence( DatabaseConnection.db.run(DBIO.sequence(
for { for {
parameter <- customParameters parameter <- customParameters
} yield { } yield {
TableQuery[UserCustomParameterDbTableDef].insertOrUpdate( TableQuery[UserCustomParameterDbTableDef].insertOrUpdate(
UserCustomParameterDbModel( UserCustomParameterDbModel(
meetingId = meetingId,
userId = userId, userId = userId,
parameter = parameter._1, parameter = parameter._1,
value = parameter._2 value = parameter._2

View File

@ -6,9 +6,9 @@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success} import scala.util.{Failure, Success}
case class UserDbModel( case class UserDbModel(
meetingId: String,
userId: String, userId: String,
extId: String, extId: String,
meetingId: String,
name: String, name: String,
role: String, role: String,
avatar: String = "", avatar: String = "",
@ -32,10 +32,11 @@ case class UserDbModel(
class UserDbTableDef(tag: Tag) extends Table[UserDbModel](tag, None, "user") { class UserDbTableDef(tag: Tag) extends Table[UserDbModel](tag, None, "user") {
override def * = ( override def * = (
userId,extId,meetingId,name,role,avatar,color, sessionToken, authToken, authed,joined,joinErrorCode, joinErrorMessage, banned,loggedOut,guest,guestStatus,registeredOn,excludeFromDashboard, enforceLayout) <> (UserDbModel.tupled, UserDbModel.unapply) meetingId,userId,extId,name,role,avatar,color, sessionToken, authToken, authed,joined,joinErrorCode,
joinErrorMessage, banned,loggedOut,guest,guestStatus,registeredOn,excludeFromDashboard, enforceLayout) <> (UserDbModel.tupled, UserDbModel.unapply)
val meetingId = column[String]("meetingId", O.PrimaryKey)
val userId = column[String]("userId", O.PrimaryKey) val userId = column[String]("userId", O.PrimaryKey)
val extId = column[String]("extId") val extId = column[String]("extId")
val meetingId = column[String]("meetingId")
val name = column[String]("name") val name = column[String]("name")
val role = column[String]("role") val role = column[String]("role")
val avatar = column[String]("avatar") val avatar = column[String]("avatar")
@ -89,7 +90,7 @@ object UserDAO {
case Success(rowsAffected) => { case Success(rowsAffected) => {
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted in User table!") DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted in User table!")
UserConnectionStatusDAO.insert(meetingId, regUser.id) UserConnectionStatusDAO.insert(meetingId, regUser.id)
UserCustomParameterDAO.insert(regUser.id, regUser.customParameters) UserCustomParameterDAO.insert(meetingId, regUser.id, regUser.customParameters)
UserClientSettingsDAO.insert(regUser.id, meetingId) UserClientSettingsDAO.insert(regUser.id, meetingId)
ChatUserDAO.insertUserPublicChat(meetingId, regUser.id) ChatUserDAO.insertUserPublicChat(meetingId, regUser.id)
} }
@ -100,6 +101,7 @@ object UserDAO {
def update(regUser: RegisteredUser) = { def update(regUser: RegisteredUser) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserDbTableDef] TableQuery[UserDbTableDef]
.filter(_.meetingId === regUser.meetingId)
.filter(_.userId === regUser.id) .filter(_.userId === regUser.id)
.map(u => (u.guest, u.guestStatus, u.role, u.authed, u.joined, u.banned, u.loggedOut)) .map(u => (u.guest, u.guestStatus, u.role, u.authed, u.joined, u.banned, u.loggedOut))
.update((regUser.guest, regUser.guestStatus, regUser.role, regUser.authed, regUser.joined, regUser.banned, regUser.loggedOut)) .update((regUser.guest, regUser.guestStatus, regUser.role, regUser.authed, regUser.joined, regUser.banned, regUser.loggedOut))
@ -113,18 +115,20 @@ object UserDAO {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserDbTableDef] TableQuery[UserDbTableDef]
.filter(_.meetingId === voiceUserState.meetingId)
.filter(_.userId === voiceUserState.intId) .filter(_.userId === voiceUserState.intId)
.map(u => (u.guest, u.guestStatus, u.authed, u.joined)) .map(u => (u.guest, u.guestStatus, u.authed, u.joined))
.update((false, "ALLOW", true, true)) .update((false, "ALLOW", true, true))
).onComplete { ).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on user voice table!") case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on user table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating user voice: $e") case Failure(e) => DatabaseConnection.logger.debug(s"Error updating user table: $e")
} }
} }
def updateJoinError(userId: String, joinErrorCode: String, joinErrorMessage: String) = { def updateJoinError(meetingId: String, userId: String, joinErrorCode: String, joinErrorMessage: String) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserDbTableDef] TableQuery[UserDbTableDef]
.filter(_.meetingId === meetingId)
.filter(_.userId === userId) .filter(_.userId === userId)
.map(u => (u.joined, u.joinErrorCode, u.joinErrorMessage)) .map(u => (u.joined, u.joinErrorCode, u.joinErrorMessage))
.update((false, Some(joinErrorCode), Some(joinErrorMessage))) .update((false, Some(joinErrorCode), Some(joinErrorMessage)))
@ -134,11 +138,11 @@ object UserDAO {
} }
} }
def softDelete(meetingId: String, userId: String) = {
def softDelete(intId: String) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserDbTableDef] TableQuery[UserDbTableDef]
.filter(_.userId === intId) .filter(_.meetingId === meetingId)
.filter(_.userId === userId)
.map(u => (u.loggedOut)) .map(u => (u.loggedOut))
.update((true)) .update((true))
).onComplete { ).onComplete {
@ -159,6 +163,62 @@ object UserDAO {
} }
} }
def transferUserToBreakoutRoomAsAudioOnly(userId: String, meetingIdFrom: String, meetingIdTo: String) = {
//Create a copy of the user using the same userId, but with the meetingId of the breakoutRoom
//The user will be flagged by `transferredFromParentMeeting=true`
DatabaseConnection.db.run(
sqlu"""
WITH upsert AS (
UPDATE "user"
SET "loggedOut"=false
where "userId" = ${userId}
and "meetingId" = ${meetingIdTo}
RETURNING *)
insert into "user"("meetingId","userId","extId","name","role","guest","authed","guestStatus","locked",
"color","loggedOut","expired","ejected","joined","registeredOn","transferredFromParentMeeting","clientType")
select
${meetingIdTo} as "meetingId",
"userId",
"extId",
"name",
"role",
true as "guest",
true as "authed",
'ALLOW' as "guestStatus",
false as "locked",
"color",
"loggedOut",
"expired",
"ejected",
"joined",
"registeredOn",
true as "transferredFromParentMeeting",
'dial-in-user' as "clientType"
from "user"
where "userId" = ${userId}
and "meetingId" = ${meetingIdFrom}
and NOT EXISTS (SELECT * FROM upsert)
"""
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted in user (transferredFromParentMeeting) table")
case Failure(e) => DatabaseConnection.logger.error(s"Error inserting user (transferredFromParentMeeting): $e")
}
//Set user as loggedOut in the old meeting (if it is from transferred origin)
DatabaseConnection.db.run(
sqlu"""update "user"
set "loggedOut" = true
where "userId" = ${userId}
and "meetingId" = ${meetingIdFrom}
and "transferredFromParentMeeting" is true
"""
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated in user (transferredFromParentMeeting) table")
case Failure(e) => DatabaseConnection.logger.error(s"Error updating user (transferredFromParentMeeting): $e")
}
}
def permanentlyDeleteAllFromMeeting(meetingId: String) = { def permanentlyDeleteAllFromMeeting(meetingId: String) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserDbTableDef] TableQuery[UserDbTableDef]

View File

@ -7,6 +7,7 @@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{ Failure, Success } import scala.util.{ Failure, Success }
case class UserReactionDbModel( case class UserReactionDbModel(
meetingId: String,
userId: String, userId: String,
reactionEmoji: String, reactionEmoji: String,
durationInSeconds: Int, durationInSeconds: Int,
@ -14,19 +15,21 @@ case class UserReactionDbModel(
) )
class UserReactionDbTableDef(tag: Tag) extends Table[UserReactionDbModel](tag, "user_reaction") { class UserReactionDbTableDef(tag: Tag) extends Table[UserReactionDbModel](tag, "user_reaction") {
val meetingId = column[String]("meetingId")
val userId = column[String]("userId") val userId = column[String]("userId")
val reactionEmoji = column[String]("reactionEmoji") val reactionEmoji = column[String]("reactionEmoji")
val durationInSeconds = column[Int]("durationInSeconds") val durationInSeconds = column[Int]("durationInSeconds")
val createdAt = column[java.sql.Timestamp]("createdAt") val createdAt = column[java.sql.Timestamp]("createdAt")
override def * : ProvenShape[UserReactionDbModel] = (userId, reactionEmoji, durationInSeconds, createdAt) <> (UserReactionDbModel.tupled, UserReactionDbModel.unapply) override def * : ProvenShape[UserReactionDbModel] = (meetingId, userId, reactionEmoji, durationInSeconds, createdAt) <> (UserReactionDbModel.tupled, UserReactionDbModel.unapply)
} }
object UserReactionDAO { object UserReactionDAO {
def insert(userId: String, reactionEmoji: String, durationInSeconds: Int) = { def insert(meetingId: String, userId: String, reactionEmoji: String, durationInSeconds: Int) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserReactionDbTableDef].forceInsert( TableQuery[UserReactionDbTableDef].forceInsert(
UserReactionDbModel( UserReactionDbModel(
meetingId = meetingId,
userId = userId, userId = userId,
reactionEmoji = reactionEmoji, reactionEmoji = reactionEmoji,
durationInSeconds = durationInSeconds, durationInSeconds = durationInSeconds,

View File

@ -7,6 +7,7 @@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success} import scala.util.{Failure, Success}
case class UserStateDbModel( case class UserStateDbModel(
meetingId: String,
userId: String, userId: String,
emoji: String = "none", emoji: String = "none",
away: Boolean = false, away: Boolean = false,
@ -26,11 +27,16 @@ case class UserStateDbModel(
pinned: Boolean = false, pinned: Boolean = false,
locked: Boolean = false, locked: Boolean = false,
speechLocale: String, speechLocale: String,
inactivityWarningDisplay: Boolean = false,
inactivityWarningTimeoutSecs: Option[Long],
) )
class UserStateDbTableDef(tag: Tag) extends Table[UserStateDbModel](tag, None, "user") { class UserStateDbTableDef(tag: Tag) extends Table[UserStateDbModel](tag, None, "user") {
override def * = ( override def * = (
userId,emoji,away,raiseHand,guestStatus,guestStatusSetByModerator,guestLobbyMessage,mobile,clientType,disconnected,expired,ejected,ejectReason,ejectReasonCode,ejectedByModerator,presenter,pinned,locked,speechLocale) <> (UserStateDbModel.tupled, UserStateDbModel.unapply) meetingId, userId,emoji,away,raiseHand,guestStatus,guestStatusSetByModerator,guestLobbyMessage,mobile,clientType,disconnected,
expired,ejected,ejectReason,ejectReasonCode,ejectedByModerator,presenter,pinned,locked,speechLocale,
inactivityWarningDisplay, inactivityWarningTimeoutSecs) <> (UserStateDbModel.tupled, UserStateDbModel.unapply)
val meetingId = column[String]("meetingId", O.PrimaryKey)
val userId = column[String]("userId", O.PrimaryKey) val userId = column[String]("userId", O.PrimaryKey)
val emoji = column[String]("emoji") val emoji = column[String]("emoji")
val away = column[Boolean]("away") val away = column[Boolean]("away")
@ -50,12 +56,15 @@ class UserStateDbTableDef(tag: Tag) extends Table[UserStateDbModel](tag, None, "
val pinned = column[Boolean]("pinned") val pinned = column[Boolean]("pinned")
val locked = column[Boolean]("locked") val locked = column[Boolean]("locked")
val speechLocale = column[String]("speechLocale") val speechLocale = column[String]("speechLocale")
val inactivityWarningDisplay = column[Boolean]("inactivityWarningDisplay")
val inactivityWarningTimeoutSecs = column[Option[Long]]("inactivityWarningTimeoutSecs")
} }
object UserStateDAO { object UserStateDAO {
def update(userState: UserState) = { def update(userState: UserState) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserStateDbTableDef] TableQuery[UserStateDbTableDef]
.filter(_.meetingId === userState.meetingId)
.filter(_.userId === userState.intId) .filter(_.userId === userState.intId)
.map(u => (u.presenter, u.pinned, u.locked, u.speechLocale, u.emoji, u.away, u.raiseHand, u.mobile, u.clientType, u.disconnected)) .map(u => (u.presenter, u.pinned, u.locked, u.speechLocale, u.emoji, u.away, u.raiseHand, u.mobile, u.clientType, u.disconnected))
.update((userState.presenter, userState.pin, userState.locked, userState.speechLocale, userState.emoji, userState.away, userState.raiseHand, userState.mobile, userState.clientType, userState.userLeftFlag.left)) .update((userState.presenter, userState.pin, userState.locked, userState.speechLocale, userState.emoji, userState.away, userState.raiseHand, userState.mobile, userState.clientType, userState.userLeftFlag.left))
@ -65,9 +74,10 @@ object UserStateDAO {
} }
} }
def updateEjected(userId: String, ejectReason: String, ejectReasonCode: String, ejectedByModerator: String) = { def updateEjected(meetingId: String, userId: String, ejectReason: String, ejectReasonCode: String, ejectedByModerator: String) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserStateDbTableDef] TableQuery[UserStateDbTableDef]
.filter(_.meetingId === meetingId)
.filter(_.userId === userId) .filter(_.userId === userId)
.map(u => (u.ejected, u.ejectReason, u.ejectReasonCode, u.ejectedByModerator)) .map(u => (u.ejected, u.ejectReason, u.ejectReasonCode, u.ejectedByModerator))
.update((true, Some(ejectReason), Some(ejectReasonCode), Some(ejectedByModerator))) .update((true, Some(ejectReason), Some(ejectReasonCode), Some(ejectedByModerator)))
@ -77,10 +87,11 @@ object UserStateDAO {
} }
} }
def updateExpired(intId: String, expired: Boolean) = { def updateExpired(meetingId: String, userId: String, expired: Boolean) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserStateDbTableDef] TableQuery[UserStateDbTableDef]
.filter(_.userId === intId) .filter(_.meetingId === meetingId)
.filter(_.userId === userId)
.map(u => (u.expired)) .map(u => (u.expired))
.update((expired)) .update((expired))
).onComplete { ).onComplete {
@ -89,10 +100,11 @@ object UserStateDAO {
} }
} }
def updateGuestStatus(intId: String, guestStatus: String, guestStatusSetByModerator: String) = { def updateGuestStatus(meetingId: String, userId: String, guestStatus: String, guestStatusSetByModerator: String) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserStateDbTableDef] TableQuery[UserStateDbTableDef]
.filter(_.userId === intId) .filter(_.meetingId === meetingId)
.filter(_.userId === userId)
.map(u => (u.guestStatus, u.guestStatusSetByModerator)) .map(u => (u.guestStatus, u.guestStatusSetByModerator))
.update((guestStatus, .update((guestStatus,
guestStatusSetByModerator match { guestStatusSetByModerator match {
@ -107,10 +119,11 @@ object UserStateDAO {
} }
} }
def updateGuestLobbyMessage(intId: String, guestLobbyMessage: String) = { def updateGuestLobbyMessage(meetingId: String, userId: String, guestLobbyMessage: String) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserStateDbTableDef] TableQuery[UserStateDbTableDef]
.filter(_.userId === intId) .filter(_.meetingId === meetingId)
.filter(_.userId === userId)
.map(u => u.guestLobbyMessage) .map(u => u.guestLobbyMessage)
.update(Some(guestLobbyMessage)) .update(Some(guestLobbyMessage))
).onComplete { ).onComplete {
@ -119,4 +132,22 @@ object UserStateDAO {
} }
} }
def updateInactivityWarning(meetingId: String, userId: String, inactivityWarningDisplay: Boolean, inactivityWarningTimeoutSecs: Long) = {
DatabaseConnection.db.run(
TableQuery[UserStateDbTableDef]
.filter(_.meetingId === meetingId)
.filter(_.userId === userId)
.map(u => (u.inactivityWarningDisplay, u.inactivityWarningTimeoutSecs))
.update((inactivityWarningDisplay,
inactivityWarningTimeoutSecs match {
case 0 => None
case timeout: Long => Some(timeout)
case _ => None
}))
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated inactivityWarningDisplay on user table!")
case Failure(e) => DatabaseConnection.logger.error(s"Error updating inactivityWarningDisplay user: $e")
}
}
} }

View File

@ -0,0 +1,46 @@
package org.bigbluebutton.core.db
import org.bigbluebutton.core.models.{ VoiceUserState }
import slick.jdbc.PostgresProfile.api._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{ Failure, Success }
case class UserTranscriptionErrorDbModel(
meetingId: String,
userId: String,
errorCode: String,
errorMessage: String,
lastUpdatedAt: java.sql.Timestamp = new java.sql.Timestamp(System.currentTimeMillis())
)
class UserTranscriptionErrorDbTableDef(tag: Tag) extends Table[UserTranscriptionErrorDbModel](tag, None, "user_transcriptionError") {
override def * = (
meetingId, userId, errorCode, errorMessage, lastUpdatedAt
) <> (UserTranscriptionErrorDbModel.tupled, UserTranscriptionErrorDbModel.unapply)
val meetingId = column[String]("meetingId", O.PrimaryKey)
val userId = column[String]("userId", O.PrimaryKey)
val errorCode = column[String]("errorCode")
val errorMessage = column[String]("errorMessage")
val lastUpdatedAt = column[java.sql.Timestamp]("lastUpdatedAt")
}
object UserTranscriptionErrorDAO {
def insert(userId: String, meetingId: String, errorCode: String, errorMessage: String) = {
DatabaseConnection.db.run(
TableQuery[UserTranscriptionErrorDbTableDef].insertOrUpdate(
UserTranscriptionErrorDbModel(
meetingId = meetingId,
userId = userId,
errorCode = errorCode,
errorMessage = errorMessage,
lastUpdatedAt = new java.sql.Timestamp(System.currentTimeMillis()),
)
)
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on user_transcriptionError table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting user_transcriptionError: $e")
}
}
}

View File

@ -7,6 +7,7 @@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success } import scala.util.{Failure, Success }
case class UserVoiceConfStateDbModel( case class UserVoiceConfStateDbModel(
meetingId: String,
userId: String, userId: String,
voiceConf: String, voiceConf: String,
voiceConfCallSession: String, voiceConfCallSession: String,
@ -16,8 +17,9 @@ case class UserVoiceConfStateDbModel(
class UserVoiceConfStateDbTableDef(tag: Tag) extends Table[UserVoiceConfStateDbModel](tag, None, "user_voice") { class UserVoiceConfStateDbTableDef(tag: Tag) extends Table[UserVoiceConfStateDbModel](tag, None, "user_voice") {
override def * = ( override def * = (
userId, voiceConf, voiceConfCallSession, voiceConfClientSession, voiceConfCallState meetingId, userId, voiceConf, voiceConfCallSession, voiceConfClientSession, voiceConfCallState
) <> (UserVoiceConfStateDbModel.tupled, UserVoiceConfStateDbModel.unapply) ) <> (UserVoiceConfStateDbModel.tupled, UserVoiceConfStateDbModel.unapply)
val meetingId = column[String]("meetingId", O.PrimaryKey)
val userId = column[String]("userId", O.PrimaryKey) val userId = column[String]("userId", O.PrimaryKey)
val voiceConf = column[String]("voiceConf") val voiceConf = column[String]("voiceConf")
val voiceConfCallSession = column[String]("voiceConfCallSession") val voiceConfCallSession = column[String]("voiceConfCallSession")
@ -26,10 +28,11 @@ class UserVoiceConfStateDbTableDef(tag: Tag) extends Table[UserVoiceConfStateDbM
} }
object UserVoiceConfStateDAO { object UserVoiceConfStateDAO {
def insertOrUpdate(userId: String, voiceConf: String, voiceConfCallSession: String, clientSession: String, callState: String) = { def insertOrUpdate(meetingId: String, userId: String, voiceConf: String, voiceConfCallSession: String, clientSession: String, callState: String) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserVoiceConfStateDbTableDef].insertOrUpdate( TableQuery[UserVoiceConfStateDbTableDef].insertOrUpdate(
UserVoiceConfStateDbModel( UserVoiceConfStateDbModel(
meetingId = meetingId,
userId = userId, userId = userId,
voiceConf = voiceConf, voiceConf = voiceConf,
voiceConfCallSession = voiceConfCallSession, voiceConfCallSession = voiceConfCallSession,
@ -38,9 +41,7 @@ object UserVoiceConfStateDAO {
) )
) )
).onComplete { ).onComplete {
case Success(rowsAffected) => { case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on user_voice table!")
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on user_voice table!")
}
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting voice: $e") case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting voice: $e")
} }
} }

View File

@ -7,6 +7,7 @@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success } import scala.util.{Failure, Success }
case class UserVoiceDbModel( case class UserVoiceDbModel(
meetingId: String,
userId: String, userId: String,
voiceUserId: String, voiceUserId: String,
callerName: String, callerName: String,
@ -25,9 +26,10 @@ case class UserVoiceDbModel(
class UserVoiceDbTableDef(tag: Tag) extends Table[UserVoiceDbModel](tag, None, "user_voice") { class UserVoiceDbTableDef(tag: Tag) extends Table[UserVoiceDbModel](tag, None, "user_voice") {
override def * = ( override def * = (
userId, voiceUserId, callerName, callerNum, callingWith, joined, listenOnly, meetingId, userId, voiceUserId, callerName, callerNum, callingWith, joined, listenOnly,
muted, spoke, talking, floor, lastFloorTime, startTime, endTime muted, spoke, talking, floor, lastFloorTime, startTime, endTime
) <> (UserVoiceDbModel.tupled, UserVoiceDbModel.unapply) ) <> (UserVoiceDbModel.tupled, UserVoiceDbModel.unapply)
val meetingId = column[String]("meetingId", O.PrimaryKey)
val userId = column[String]("userId", O.PrimaryKey) val userId = column[String]("userId", O.PrimaryKey)
val voiceUserId = column[String]("voiceUserId") val voiceUserId = column[String]("voiceUserId")
val callerName = column[String]("callerName") val callerName = column[String]("callerName")
@ -54,6 +56,7 @@ object UserVoiceDAO {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserVoiceDbTableDef].insertOrUpdate( TableQuery[UserVoiceDbTableDef].insertOrUpdate(
UserVoiceDbModel( UserVoiceDbModel(
meetingId = voiceUserState.meetingId,
userId = voiceUserState.intId, userId = voiceUserState.intId,
voiceUserId = voiceUserState.voiceUserId, voiceUserId = voiceUserState.voiceUserId,
callerName = voiceUserState.callerName, callerName = voiceUserState.callerName,
@ -99,13 +102,15 @@ object UserVoiceDAO {
"spoke" = true, "spoke" = true,
"endTime" = null, "endTime" = null,
"startTime" = (case when "talking" is false then $now else "startTime" end) "startTime" = (case when "talking" is false then $now else "startTime" end)
WHERE "userId" = ${voiceUserState.intId}""" WHERE "meetingId" = ${voiceUserState.meetingId}
AND "userId" = ${voiceUserState.intId}"""
} else { } else {
sqlu"""UPDATE user_voice SET sqlu"""UPDATE user_voice SET
"talking" = false, "talking" = false,
"startTime" = null, "startTime" = null,
"endTime" = (case when "talking" is true then $now else "endTime" end) "endTime" = (case when "talking" is true then $now else "endTime" end)
WHERE "userId" = ${voiceUserState.intId}""" WHERE "meetingId" = ${voiceUserState.meetingId}
AND "userId" = ${voiceUserState.intId}"""
} }
DatabaseConnection.db.run(updateSql).onComplete { DatabaseConnection.db.run(updateSql).onComplete {
@ -114,10 +119,11 @@ object UserVoiceDAO {
} }
} }
def delete(intId: String) = { def delete(meetingId: String, userId: String) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserDbTableDef] TableQuery[UserDbTableDef]
.filter(_.userId === intId) .filter(_.meetingId === meetingId)
.filter(_.userId === userId)
.map(u => (u.loggedOut)) .map(u => (u.loggedOut))
.update((true)) .update((true))
).onComplete { ).onComplete {
@ -126,7 +132,7 @@ object UserVoiceDAO {
} }
} }
def deleteUserVoice(userId: String) = { def deleteUserVoice(meetingId: String,userId: String) = {
//Meteor sets this props instead of removing //Meteor sets this props instead of removing
// muted: false // muted: false
// talking: false // talking: false
@ -136,6 +142,7 @@ object UserVoiceDAO {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserVoiceDbTableDef] TableQuery[UserVoiceDbTableDef]
.filter(_.meetingId === meetingId)
.filter(_.userId === userId) .filter(_.userId === userId)
.map(u => (u.muted, u.talking, u.listenOnly, u.joined, u.spoke, u.startTime, u.endTime)) .map(u => (u.muted, u.talking, u.listenOnly, u.joined, u.spoke, u.startTime, u.endTime))
.update((false, false, false, false, false, None, None)) .update((false, false, false, false, false, None, None))

View File

@ -1,60 +0,0 @@
package org.bigbluebutton.core.db
import org.bigbluebutton.core.apps.whiteboard.Whiteboard
import slick.jdbc.PostgresProfile.api._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
case class UserWhiteboardDbModel(
whiteboardId: String,
userId: String,
changedModeOn: Long,
)
class UserWhiteboardDbTableDef(tag: Tag) extends Table[UserWhiteboardDbModel](tag, None, "user_whiteboard") {
override def * = (
whiteboardId, userId, changedModeOn) <> (UserWhiteboardDbModel.tupled, UserWhiteboardDbModel.unapply)
def pk = primaryKey("user_whiteboard_pkey", (whiteboardId, userId))
val whiteboardId = column[String]("whiteboardId")
val userId = column[String]("userId")
val changedModeOn = column[Long]("changedModeOn")
}
object UserWhiteboardDAO {
def updateMultiuser(whiteboard: Whiteboard) = {
val deleteQuery = TableQuery[UserWhiteboardDbTableDef]
.filter(_.whiteboardId === whiteboard.id)
if(whiteboard.multiUser.length > 0) {
deleteQuery.filterNot(_.userId inSet whiteboard.multiUser)
}
DatabaseConnection.db.run(deleteQuery.delete).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"Users deleted from Whiteboard ${whiteboard.id}")
case Failure(e) => DatabaseConnection.logger.error(s"Error deleting users from whiteboard: $e")
}
for {
userId <- whiteboard.multiUser
} yield {
DatabaseConnection.db.run(
TableQuery[UserWhiteboardDbTableDef].insertOrUpdate(
UserWhiteboardDbModel(
whiteboardId = whiteboard.id,
userId = userId,
changedModeOn = whiteboard.changedModeOn
)
)
).onComplete {
case Success(rowsAffected) => {
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on user_whiteboard table!")
}
case Failure(e) => DatabaseConnection.logger.error(s"Error inserting user_whiteboard: $e")
}
}
}
}

View File

@ -42,4 +42,5 @@ object MeetingEndReason {
val BREAKOUT_ENDED_BY_MOD = "BREAKOUT_ENDED_BY_MOD" val BREAKOUT_ENDED_BY_MOD = "BREAKOUT_ENDED_BY_MOD"
val ENDED_DUE_TO_NO_AUTHED_USER = "ENDED_DUE_TO_NO_AUTHED_USER" val ENDED_DUE_TO_NO_AUTHED_USER = "ENDED_DUE_TO_NO_AUTHED_USER"
val ENDED_DUE_TO_NO_MODERATOR = "ENDED_DUE_TO_NO_MODERATOR" val ENDED_DUE_TO_NO_MODERATOR = "ENDED_DUE_TO_NO_MODERATOR"
val ENDED_DUE_TO_SERVICE_INTERRUPTION = "ENDED_DUE_TO_SERVICE_INTERRUPTION"
} }

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