Skip to content

Commit

Permalink
feat(html5): Added captionLocale property and related logic (#20272)
Browse files Browse the repository at this point in the history
* [captionLocale] - added captionLocale

* [captionLocale] - changes in review

* [captionLocale] - Added new mutation captionAddLocale and removed captionSetOwner

* [captionLocale] - add action validation

* [captionLocale] - Changes in review

* [captionLocale] - fix update of graphql server

* [captionLocale] - merge conflicts
  • Loading branch information
GuiLeme committed Jun 3, 2024
1 parent a70aab0 commit bbf43f1
Show file tree
Hide file tree
Showing 30 changed files with 247 additions and 159 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,6 @@ class CaptionModel {
return None
}

def updateTranscriptOwner(name: String, locale: String, ownerId: String): Map[String, TranscriptVO] = {
var updatedTranscripts = new HashMap[String, TranscriptVO]

// clear owner from previous locale
if (ownerId.length > 0) {
findTranscriptByOwnerId(ownerId).foreach(t => {
val oldTranscript = t._2.copy(ownerId = "")

transcripts += t._1 -> oldTranscript
updatedTranscripts += t._1 -> oldTranscript
})
}
// change the owner if it does exist
if (transcripts contains name) {
val newTranscript = transcripts(name).copy(ownerId = ownerId)

transcripts += name -> newTranscript
updatedTranscripts += name -> newTranscript
} else { // create the locale if it doesn't exist
val addedTranscript = createTranscript(name, locale, ownerId)
updatedTranscripts += name -> addedTranscript
}

updatedTranscripts
}

def getHistory(): Map[String, TranscriptVO] = {
transcripts
}
Expand Down Expand Up @@ -97,20 +71,6 @@ class CaptionModel {
locale
}

def checkCaptionOwnerLogOut(userId: String): Option[(String, TranscriptVO)] = {
var rtnTranscript: Option[(String, TranscriptVO)] = None

if (userId.length > 0) {
findTranscriptByOwnerId(userId).foreach(t => {
val oldTranscript = t._2.copy(ownerId = "")

transcripts += t._1 -> oldTranscript
rtnTranscript = Some((t._1, oldTranscript))
})
}
rtnTranscript
}

def isUserCaptionOwner(userId: String, name: String): Boolean = {
var isOwner: Boolean = false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,10 @@ class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementT
liveMeeting.captionModel.getHistory()
}

def updateCaptionOwner(liveMeeting: LiveMeeting, name: String, locale: String, userId: String): Map[String, TranscriptVO] = {
liveMeeting.captionModel.updateTranscriptOwner(name, locale, userId)
}

def editCaptionHistory(liveMeeting: LiveMeeting, userId: String, startIndex: Integer, endIndex: Integer, name: String, text: String): Boolean = {
liveMeeting.captionModel.editHistory(userId, startIndex, endIndex, name, text)
}

def checkCaptionOwnerLogOut(liveMeeting: LiveMeeting, userId: String): Option[(String, TranscriptVO)] = {
liveMeeting.captionModel.checkCaptionOwnerLogOut(userId)
}

def isUserCaptionOwner(liveMeeting: LiveMeeting, userId: String, name: String): Boolean = {
liveMeeting.captionModel.isUserCaptionOwner(userId, name)
}
Expand Down Expand Up @@ -75,46 +67,25 @@ class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementT
broadcastEvent(msg, getCaptionHistory(liveMeeting))
}

def handle(msg: UpdateCaptionOwnerPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastUpdateCaptionOwnerEvent(name: String, locale: String, newOwnerId: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, newOwnerId)
val envelope = BbbCoreEnvelope(UpdateCaptionOwnerEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(UpdateCaptionOwnerEvtMsg.NAME, liveMeeting.props.meetingProp.intId, newOwnerId)
def handle(msg: AddCaptionLocalePubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastAddCaptionLocaleEvent(locale: String, userId: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, userId)
val envelope = BbbCoreEnvelope(AddCaptionLocaleEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(AddCaptionLocaleEvtMsg.NAME, liveMeeting.props.meetingProp.intId, userId)

val body = UpdateCaptionOwnerEvtMsgBody(name, locale, newOwnerId)
val event = UpdateCaptionOwnerEvtMsg(header, body)
val body = AddCaptionLocaleEvtMsgBody(locale)
val event = AddCaptionLocaleEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
CaptionLocaleDAO.insertOrUpdateCaptionLocale(liveMeeting.props.meetingProp.intId, locale, CaptionTypes.TYPED, newOwnerId)
CaptionLocaleDAO.insertOrUpdateCaptionLocale(liveMeeting.props.meetingProp.intId, locale, CaptionTypes.TYPED, userId)
}

if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to change caption owners."
val reason = "No permission to add caption locale."
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
} else {
updateCaptionOwner(liveMeeting, msg.body.name, msg.body.locale, msg.body.ownerId).foreach(f => {
broadcastUpdateCaptionOwnerEvent(f._1, f._2.locale, f._2.ownerId)
})
}
}

def handleUserLeavingMsg(userId: String, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastUpdateCaptionOwnerEvent(name: String, locale: String, newOwnerId: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, newOwnerId)
val envelope = BbbCoreEnvelope(UpdateCaptionOwnerEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(UpdateCaptionOwnerEvtMsg.NAME, liveMeeting.props.meetingProp.intId, newOwnerId)

val body = UpdateCaptionOwnerEvtMsgBody(name, locale, newOwnerId)
val event = UpdateCaptionOwnerEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}

for {
transcriptInfo <- checkCaptionOwnerLogOut(liveMeeting, userId)
} yield {
broadcastUpdateCaptionOwnerEvent(transcriptInfo._1, transcriptInfo._2.locale, transcriptInfo._2.ownerId)
broadcastAddCaptionLocaleEvent(msg.body.locale, msg.header.userId)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.bigbluebutton.core.apps.users

import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.RightsManagementTrait
import org.bigbluebutton.core.models.{ UserState, Users2x }
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }

trait SetUserCaptionLocaleMsgHdlr extends RightsManagementTrait {
this: UsersApp =>

val liveMeeting: LiveMeeting
val outGW: OutMsgRouter

def handleSetUserCaptionLocaleReqMsg(msg: SetUserCaptionLocaleReqMsg): Unit = {
log.info("handleSetUserCaptionLocaleReqMsg: locale={} provider={} userId={}", msg.body.locale, msg.body.provider, msg.header.userId)

def broadcastUserCaptionLocaleChanged(user: UserState, locale: String, provider: String): Unit = {
val routingChange = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
liveMeeting.props.meetingProp.intId, user.intId
)
val envelopeChange = BbbCoreEnvelope(UserCaptionLocaleChangedEvtMsg.NAME, routingChange)
val headerChange = BbbClientMsgHeader(UserCaptionLocaleChangedEvtMsg.NAME, liveMeeting.props.meetingProp.intId, user.intId)

val bodyChange = UserCaptionLocaleChangedEvtMsgBody(locale, provider)
val eventChange = UserCaptionLocaleChangedEvtMsg(headerChange, bodyChange)
val msgEventChange = BbbCommonEnvCoreMsg(envelopeChange, eventChange)
outGW.send(msgEventChange)
}

for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
} yield {
Users2x.setUserCaptionLocale(liveMeeting.users2x, msg.header.userId, msg.body.locale)
broadcastUserCaptionLocaleChanged(user, msg.body.locale, msg.body.provider)
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ class UsersApp(
with RegisterUserReqMsgHdlr
with ChangeUserRoleCmdMsgHdlr
with SetUserSpeechLocaleMsgHdlr
with SetUserCaptionLocaleMsgHdlr
with SetUserSpeechOptionsMsgHdlr
with SyncGetUsersMeetingRespMsgHdlr
with LogoutAndEndMeetingCmdMsgHdlr
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,28 @@ case class CaptionLocaleDbModel(
meetingId: String,
locale: String,
captionType: String,
ownerUserId: String,
createdBy: 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 createdBy = column[String]("createdBy")
val updatedAt = column[java.sql.Timestamp]("updatedAt")
def * = (meetingId, locale, captionType, ownerUserId, updatedAt) <> (CaptionLocaleDbModel.tupled, CaptionLocaleDbModel.unapply)
def * = (meetingId, locale, captionType, createdBy, updatedAt) <> (CaptionLocaleDbModel.tupled, CaptionLocaleDbModel.unapply)
}

object CaptionLocaleDAO {
def insertOrUpdateCaptionLocale(meetingId: String, locale: String, captionType: String, ownerUserId: String) = {
def insertOrUpdateCaptionLocale(meetingId: String, locale: String, captionType: String, userId: String) = {
DatabaseConnection.db.run(
TableQuery[CaptionLocaleTableDef].insertOrUpdate(
CaptionLocaleDbModel(
meetingId = meetingId,
locale = locale,
captionType = captionType,
ownerUserId = ownerUserId,
createdBy = userId,
updatedAt = new java.sql.Timestamp(System.currentTimeMillis())
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package org.bigbluebutton.core.db

import org.bigbluebutton.core.models.{UserState}
import org.bigbluebutton.core.models.UserState
import slick.jdbc.PostgresProfile.api._

import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}


case class UserEjectColumnsDbModel(
ejected: Boolean = false,
ejectReason: Option[String],
ejectReasonCode: Option[String],
ejectedByModerator: Option[String],
)
case class UserStateDbModel(
meetingId: String,
userId: String,
Expand All @@ -19,22 +26,20 @@ case class UserStateDbModel(
clientType: String,
disconnected: Boolean = false,
expired: Boolean = false,
ejected: Boolean = false,
ejectReason: Option[String],
ejectReasonCode: Option[String],
ejectedByModerator: Option[String],
ejectColumns: UserEjectColumnsDbModel,
presenter: Boolean = false,
pinned: Boolean = false,
locked: Boolean = false,
speechLocale: String,
captionLocale: String,
inactivityWarningDisplay: Boolean = false,
inactivityWarningTimeoutSecs: Option[Long],
)

class UserStateDbTableDef(tag: Tag) extends Table[UserStateDbModel](tag, None, "user") {
override def * = (
meetingId, userId,emoji,away,raiseHand,guestStatus,guestStatusSetByModerator,guestLobbyMessage,mobile,clientType,disconnected,
expired,ejected,ejectReason,ejectReasonCode,ejectedByModerator,presenter,pinned,locked,speechLocale,
expired,ejectColumns,presenter,pinned,locked,speechLocale, captionLocale,
inactivityWarningDisplay, inactivityWarningTimeoutSecs) <> (UserStateDbModel.tupled, UserStateDbModel.unapply)
val meetingId = column[String]("meetingId", O.PrimaryKey)
val userId = column[String]("userId", O.PrimaryKey)
Expand All @@ -52,10 +57,12 @@ class UserStateDbTableDef(tag: Tag) extends Table[UserStateDbModel](tag, None, "
val ejectReason = column[Option[String]]("ejectReason")
val ejectReasonCode = column[Option[String]]("ejectReasonCode")
val ejectedByModerator = column[Option[String]]("ejectedByModerator")
val ejectColumns = (ejected, ejectReason, ejectReasonCode, ejectedByModerator) <> (UserEjectColumnsDbModel.tupled, UserEjectColumnsDbModel.unapply)
val presenter = column[Boolean]("presenter")
val pinned = column[Boolean]("pinned")
val locked = column[Boolean]("locked")
val speechLocale = column[String]("speechLocale")
val captionLocale = column[String]("captionLocale")
val inactivityWarningDisplay = column[Boolean]("inactivityWarningDisplay")
val inactivityWarningTimeoutSecs = column[Option[Long]]("inactivityWarningTimeoutSecs")
}
Expand All @@ -66,8 +73,20 @@ object UserStateDAO {
TableQuery[UserStateDbTableDef]
.filter(_.meetingId === userState.meetingId)
.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))
.update((userState.presenter, userState.pin, userState.locked, userState.speechLocale, userState.emoji, userState.away, userState.raiseHand, userState.mobile, userState.clientType, userState.userLeftFlag.left))
.map(u => (u.presenter, u.pinned, u.locked, u.speechLocale, u.captionLocale, u.emoji, u.away, u.raiseHand, u.mobile, u.clientType, u.disconnected))
.update((
userState.presenter,
userState.pin,
userState.locked,
userState.speechLocale,
userState.captionLocale,
userState.emoji,
userState.away,
userState.raiseHand,
userState.mobile,
userState.clientType,
userState.userLeftFlag.left
))
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on user table!")
case Failure(e) => DatabaseConnection.logger.error(s"Error updating user: $e")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,17 @@ object Users2x {
}
}

def setUserCaptionLocale(users: Users2x, intId: String, locale: String): Option[UserState] = {
for {
u <- findWithIntId(users, intId)
} yield {
val newUser = u.modify(_.captionLocale).setTo(locale)
UserStateDAO.update(newUser)
users.save(newUser)
newUser
}
}

def hasPresenter(users: Users2x): Boolean = {
findPresenter(users) match {
case Some(p) => true
Expand Down Expand Up @@ -438,7 +449,9 @@ case class UserState(
lastInactivityInspect: Long = 0,
clientType: String,
userLeftFlag: UserLeftFlag,
speechLocale: String = ""
speechLocale: String = "",
captionLocale: String = ""

)

case class UserIdAndName(id: String, name: String)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ class ReceivedJsonMsgHandlerActor(
routeGenericMsg[UserConnectionAliveReqMsg](envelope, jsonNode)
case SetUserSpeechLocaleReqMsg.NAME =>
routeGenericMsg[SetUserSpeechLocaleReqMsg](envelope, jsonNode)
case SetUserCaptionLocaleReqMsg.NAME =>
routeGenericMsg[SetUserCaptionLocaleReqMsg](envelope, jsonNode)
case SetUserSpeechOptionsReqMsg.NAME =>
routeGenericMsg[SetUserSpeechOptionsReqMsg](envelope, jsonNode)

Expand Down Expand Up @@ -343,8 +345,8 @@ class ReceivedJsonMsgHandlerActor(
// Caption
case EditCaptionHistoryPubMsg.NAME =>
routeGenericMsg[EditCaptionHistoryPubMsg](envelope, jsonNode)
case UpdateCaptionOwnerPubMsg.NAME =>
routeGenericMsg[UpdateCaptionOwnerPubMsg](envelope, jsonNode)
case AddCaptionLocalePubMsg.NAME =>
routeGenericMsg[AddCaptionLocalePubMsg](envelope, jsonNode)
case SendCaptionHistoryReqMsg.NAME =>
routeGenericMsg[SendCaptionHistoryReqMsg](envelope, jsonNode)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,6 @@ class MeetingActor(
case m: RecordAndClearPreviousMarkersCmdMsg =>
state = usersApp.handleRecordAndClearPreviousMarkersCmdMsg(m, state)
updateUserLastActivity(m.body.setBy)
case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m)
case m: ChangeUserEmojiCmdMsg =>
handleChangeUserEmojiCmdMsg(m)
updateUserLastActivity(m.header.userId)
Expand All @@ -459,6 +458,8 @@ class MeetingActor(
case m: UserConnectionAliveReqMsg => usersApp.handleUserConnectionAliveReqMsg(m)
case m: SetUserSpeechLocaleReqMsg => usersApp.handleSetUserSpeechLocaleReqMsg(m)
case m: SetUserSpeechOptionsReqMsg => usersApp.handleSetUserSpeechOptionsReqMsg(m)
case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m)
case m: SetUserCaptionLocaleReqMsg => usersApp.handleSetUserCaptionLocaleReqMsg(m)

// Client requested to eject user
case m: EjectUserFromMeetingCmdMsg =>
Expand Down Expand Up @@ -637,7 +638,7 @@ class MeetingActor(

// Caption
case m: EditCaptionHistoryPubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
case m: UpdateCaptionOwnerPubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
case m: AddCaptionLocalePubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
case m: SendCaptionHistoryReqMsg => captionApp2x.handle(m, liveMeeting, msgBus)

// Guests
Expand Down Expand Up @@ -1027,8 +1028,6 @@ class MeetingActor(

RegisteredUsers.updateUserJoin(liveMeeting.registeredUsers, ru, joined = false)

captionApp2x.handleUserLeavingMsg(leftUser.intId, liveMeeting, msgBus)

// send a user left event for the clients to update
val userLeftMeetingEvent = MsgBuilder.buildUserLeftMeetingEvtMsg(liveMeeting.props.meetingProp.intId, u.intId)
outGW.send(userLeftMeetingEvent)
Expand Down
Loading

0 comments on commit bbf43f1

Please sign in to comment.