Merged upstream develop branch
This commit is contained in:
parent
2dfb9d6fa7
commit
5ca831ff86
2
.github/ISSUE_TEMPLATE/core-issue.md
vendored
2
.github/ISSUE_TEMPLATE/core-issue.md
vendored
@ -2,7 +2,7 @@
|
||||
name: Server Core Issue
|
||||
about: Template for creating an issue with bbb-web, akka-apps, or other server component
|
||||
title: ''
|
||||
labels: ''
|
||||
labels: 'module: core'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -2,7 +2,7 @@
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: Enhancement
|
||||
labels: 'type: enhancement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
2
.github/ISSUE_TEMPLATE/html5-issue.md
vendored
2
.github/ISSUE_TEMPLATE/html5-issue.md
vendored
@ -2,7 +2,7 @@
|
||||
name: HTML5 Issue
|
||||
about: Template for creating HTML5 Issue (frontend which you see during a session, not Greenlight).
|
||||
title: ''
|
||||
labels: HTML5 Client
|
||||
labels: 'module: client'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
2
.github/ISSUE_TEMPLATE/installation-issue.md
vendored
2
.github/ISSUE_TEMPLATE/installation-issue.md
vendored
@ -2,7 +2,7 @@
|
||||
name: Installation issue (not a support question)
|
||||
about: Template for issues encountered during installation
|
||||
title: ''
|
||||
labels: ''
|
||||
labels: 'deploy: installation'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
2
.github/ISSUE_TEMPLATE/recording-issue.md
vendored
2
.github/ISSUE_TEMPLATE/recording-issue.md
vendored
@ -2,7 +2,7 @@
|
||||
name: Recording Issue
|
||||
about: Template for creating a recording issue
|
||||
title: ''
|
||||
labels: Recording
|
||||
labels: 'module: recording'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
2
.github/workflows/check-merge-conflict.yml
vendored
2
.github/workflows/check-merge-conflict.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
- name: Check for dirty pull requests
|
||||
uses: eps1lon/actions-label-merge-conflict@releases/2.x
|
||||
with:
|
||||
dirtyLabel: has-conflicts
|
||||
dirtyLabel: "status: conflict"
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
commentOnDirty: |
|
||||
This pull request has conflicts ☹
|
||||
|
@ -2,5 +2,4 @@
|
||||
|
||||
rm -rf src/main/resources
|
||||
cp -R src/universal/conf src/main/resources
|
||||
sbt run
|
||||
|
||||
exec sbt run
|
||||
|
@ -3,4 +3,4 @@
|
||||
sbt clean stage
|
||||
sudo service bbb-apps-akka stop
|
||||
cd target/universal/stage
|
||||
./bin/bbb-apps-akka
|
||||
exec ./bin/bbb-apps-akka
|
||||
|
@ -57,7 +57,7 @@ object Boot extends App with SystemConfiguration {
|
||||
|
||||
val fromAkkaAppsMsgSenderActorRef = system.actorOf(FromAkkaAppsMsgSenderActor.props(msgSender))
|
||||
|
||||
val analyticsActorRef = system.actorOf(AnalyticsActor.props())
|
||||
val analyticsActorRef = system.actorOf(AnalyticsActor.props(analyticsIncludeChat))
|
||||
outBus2.subscribe(fromAkkaAppsMsgSenderActorRef, outBbbMsgMsgChannel)
|
||||
outBus2.subscribe(redisRecorderActor, recordServiceMessageChannel)
|
||||
|
||||
|
@ -74,6 +74,8 @@ trait SystemConfiguration {
|
||||
lazy val toAkkaTranscodeJsonChannel = Try(config.getString("eventBus.toAkkaTranscodeJsonChannel")).getOrElse("to-akka-transcode-json-channel")
|
||||
lazy val fromAkkaTranscodeJsonChannel = Try(config.getString("eventBus.fromAkkaTranscodeJsonChannel")).getOrElse("from-akka-transcode-json-channel")
|
||||
|
||||
lazy val analyticsIncludeChat = Try(config.getBoolean("analytics.includeChat")).getOrElse(true)
|
||||
|
||||
// Grab the "interface" parameter from the http config
|
||||
val httpHost = config.getString("http.interface")
|
||||
// Grab the "port" parameter from the http config
|
||||
|
@ -26,6 +26,13 @@ case class MonitorNumberOfUsersInternalMsg(meetingID: String) extends InMessage
|
||||
*/
|
||||
case class SendTimeRemainingAuditInternalMsg(meetingId: String) extends InMessage
|
||||
|
||||
/**
|
||||
* Parent message sent to breakout rooms to trigger updating clients of meeting time remaining.
|
||||
* @param meetingId
|
||||
* @param timeLeftInSec
|
||||
*/
|
||||
case class SendBreakoutTimeRemainingInternalMsg(meetingId: String, timeLeftInSec: Long) extends InMessage
|
||||
|
||||
case class SendRecordingTimerInternalMsg(meetingId: String) extends InMessage
|
||||
|
||||
case class ExtendMeetingDuration(meetingId: String, userId: String) extends InMessage
|
||||
@ -66,7 +73,7 @@ case class BreakoutRoomUsersUpdateInternalMsg(parentId: String, breakoutId: Stri
|
||||
* @param parentId
|
||||
* @param breakoutId
|
||||
*/
|
||||
case class EndBreakoutRoomInternalMsg(parentId: String, breakoutId: String) extends InMessage
|
||||
case class EndBreakoutRoomInternalMsg(parentId: String, breakoutId: String, reason: String) extends InMessage
|
||||
|
||||
// DeskShare
|
||||
case class DeskShareStartedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
|
||||
|
@ -3,7 +3,7 @@ package org.bigbluebutton.core.apps.breakout
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.api.EndBreakoutRoomInternalMsg
|
||||
import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.domain.{ MeetingEndReason, MeetingState2x }
|
||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
|
||||
@ -23,7 +23,7 @@ trait EndAllBreakoutRoomsMsgHdlr extends RightsManagementTrait {
|
||||
model <- state.breakout
|
||||
} yield {
|
||||
model.rooms.values.foreach { room =>
|
||||
eventBus.publish(BigBlueButtonEvent(room.id, EndBreakoutRoomInternalMsg(props.breakoutProps.parentId, room.id)))
|
||||
eventBus.publish(BigBlueButtonEvent(room.id, EndBreakoutRoomInternalMsg(props.breakoutProps.parentId, room.id, MeetingEndReason.BREAKOUT_ENDED_BY_MOD)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.core.api.EndBreakoutRoomInternalMsg
|
||||
import org.bigbluebutton.core.bus.{ InternalEventBus }
|
||||
import org.bigbluebutton.core.domain.MeetingEndReason
|
||||
import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting, OutMsgRouter }
|
||||
|
||||
trait EndBreakoutRoomInternalMsgHdlr extends HandlerHelpers {
|
||||
@ -14,6 +13,6 @@ trait EndBreakoutRoomInternalMsgHdlr extends HandlerHelpers {
|
||||
|
||||
def handleEndBreakoutRoomInternalMsg(msg: EndBreakoutRoomInternalMsg): Unit = {
|
||||
log.info("Breakout room {} ended by parent meeting {}.", msg.breakoutId, msg.parentId)
|
||||
sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_BY_PARENT, eventBus, outGW, liveMeeting, "system")
|
||||
sendEndMeetingDueToExpiry(msg.reason, eventBus, outGW, liveMeeting, "system")
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
package org.bigbluebutton.core.apps.breakout
|
||||
|
||||
import org.bigbluebutton.core.api.SendBreakoutTimeRemainingInternalMsg
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
|
||||
trait SendBreakoutTimeRemainingInternalMsgHdlr {
|
||||
val liveMeeting: LiveMeeting
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
def handleSendBreakoutTimeRemainingInternalMsg(msg: SendBreakoutTimeRemainingInternalMsg): Unit = {
|
||||
val event = MsgBuilder.buildMeetingTimeRemainingUpdateEvtMsg(liveMeeting.props.meetingProp.intId, msg.timeLeftInSec.toInt)
|
||||
outGW.send(event)
|
||||
}
|
||||
}
|
@ -16,10 +16,7 @@ trait SendGroupChatMessageMsgHdlr {
|
||||
def handle(msg: SendGroupChatMessageMsg, state: MeetingState2x,
|
||||
liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
|
||||
|
||||
log.debug("RECEIVED PUBLIC CHAT MESSAGE")
|
||||
log.debug("NUM GROUP CHATS = " + state.groupChats.findAllPublicChats().length)
|
||||
|
||||
var chatLocked: Boolean = false;
|
||||
var chatLocked: Boolean = false
|
||||
|
||||
for {
|
||||
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||
@ -72,20 +69,6 @@ trait SendGroupChatMessageMsgHdlr {
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x) match {
|
||||
case Some(s) => log.debug("Found sender")
|
||||
case None => log.debug("NOT FOUND sender")
|
||||
}
|
||||
|
||||
state.groupChats.find(msg.body.chatId) match {
|
||||
case Some(c) => log.debug("FOUND CHAT ID " + c.id)
|
||||
case None => log.debug("NOT FOUND CHAT ID " + msg.body.chatId)
|
||||
}
|
||||
|
||||
state.groupChats.chats.values.toVector foreach { ch =>
|
||||
log.debug("CHAT = " + ch.id)
|
||||
}
|
||||
|
||||
val newState = for {
|
||||
sender <- GroupChatApp.findGroupChatUser(msg.header.userId, liveMeeting.users2x)
|
||||
chat <- state.groupChats.find(msg.body.chatId)
|
||||
|
@ -11,7 +11,6 @@ trait RespondToPollReqMsgHdlr {
|
||||
this: PollApp2x =>
|
||||
|
||||
def handle(msg: RespondToPollReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
log.debug("Received RespondToPollReqMsg {}", RespondToPollReqMsg)
|
||||
|
||||
def broadcastPollUpdatedEvent(msg: RespondToPollReqMsg, pollId: String, poll: SimplePollResultOutVO): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
|
1
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/polls/RespondToTypedPollReqMsgHdlr.scala
Normal file → Executable file
1
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/polls/RespondToTypedPollReqMsgHdlr.scala
Normal file → Executable file
@ -11,7 +11,6 @@ trait RespondToTypedPollReqMsgHdlr {
|
||||
this: PollApp2x =>
|
||||
|
||||
def handle(msg: RespondToTypedPollReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
|
||||
log.debug("Received RespondToPollReqMsg {}", RespondToTypedPollReqMsg)
|
||||
|
||||
def broadcastPollUpdatedEvent(msg: RespondToTypedPollReqMsg, pollId: String, poll: SimplePollResultOutVO): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
|
@ -17,6 +17,7 @@ class PresentationPodHdlrs(implicit val context: ActorContext)
|
||||
with PresentationConversionUpdatePubMsgHdlr
|
||||
with PresentationPageGeneratedPubMsgHdlr
|
||||
with PresentationPageCountErrorPubMsgHdlr
|
||||
with PresentationUploadedFileTooLargeErrorPubMsgHdlr
|
||||
with PresentationUploadTokenReqMsgHdlr
|
||||
with ResizeAndMovePagePubMsgHdlr
|
||||
with SyncGetPresentationPodsMsgHdlr
|
||||
|
@ -43,7 +43,7 @@ trait PresentationUploadTokenReqMsgHdlr extends RightsManagementTrait {
|
||||
val envelope = BbbCoreEnvelope(PresentationUploadTokenSysPubMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(PresentationUploadTokenSysPubMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
|
||||
val body = PresentationUploadTokenSysPubMsgBody(msg.body.podId, token, msg.body.filename)
|
||||
val body = PresentationUploadTokenSysPubMsgBody(msg.body.podId, token, msg.body.filename, liveMeeting.props.meetingProp.intId)
|
||||
val event = PresentationUploadTokenSysPubMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
|
||||
|
@ -0,0 +1,36 @@
|
||||
package org.bigbluebutton.core.apps.presentationpod
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.bus.MessageBus
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
|
||||
trait PresentationUploadedFileTooLargeErrorPubMsgHdlr {
|
||||
this: PresentationPodHdlrs =>
|
||||
|
||||
def handle(
|
||||
msg: PresentationUploadedFileTooLargeErrorSysPubMsg, state: MeetingState2x,
|
||||
liveMeeting: LiveMeeting, bus: MessageBus
|
||||
): MeetingState2x = {
|
||||
|
||||
def broadcastEvent(msg: PresentationUploadedFileTooLargeErrorSysPubMsg): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(
|
||||
MessageTypes.BROADCAST_TO_MEETING,
|
||||
liveMeeting.props.meetingProp.intId, msg.header.userId
|
||||
)
|
||||
val envelope = BbbCoreEnvelope(PresentationUploadedFileTooLargeErrorEvtMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(
|
||||
PresentationUploadedFileTooLargeErrorEvtMsg.NAME,
|
||||
liveMeeting.props.meetingProp.intId, msg.header.userId
|
||||
)
|
||||
|
||||
val body = PresentationUploadedFileTooLargeErrorEvtMsgBody(msg.body.podId, msg.body.messageKey, msg.body.code, msg.body.presentationName, msg.body.presentationToken, msg.body.fileSize, msg.body.maxFileSize)
|
||||
val event = PresentationUploadedFileTooLargeErrorEvtMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
bus.outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
broadcastEvent(msg)
|
||||
state
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ trait LogoutAndEndMeetingCmdMsgHdlr extends RightsManagementTrait {
|
||||
u <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
|
||||
} yield {
|
||||
if (u.role == Roles.MODERATOR_ROLE) {
|
||||
endAllBreakoutRooms(eventBus, liveMeeting, state)
|
||||
endAllBreakoutRooms(eventBus, liveMeeting, state, MeetingEndReason.ENDED_AFTER_USER_LOGGED_OUT)
|
||||
log.info("Meeting {} ended by user [{}, {}} when logging out.", liveMeeting.props.meetingProp.intId,
|
||||
u.intId, u.name)
|
||||
sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_AFTER_USER_LOGGED_OUT, eventBus, outGW, liveMeeting, u.intId)
|
||||
|
@ -15,12 +15,12 @@ trait SelectRandomViewerReqMsgHdlr extends RightsManagementTrait {
|
||||
def handleSelectRandomViewerReqMsg(msg: SelectRandomViewerReqMsg): Unit = {
|
||||
log.debug("Received SelectRandomViewerReqMsg {}", SelectRandomViewerReqMsg)
|
||||
|
||||
def broadcastEvent(msg: SelectRandomViewerReqMsg, selectedUser: UserState): Unit = {
|
||||
def broadcastEvent(msg: SelectRandomViewerReqMsg, users: Vector[String], choice: Integer): 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, selectedUser.intId)
|
||||
val body = SelectRandomViewerRespMsgBody(msg.header.userId, users, choice)
|
||||
val event = SelectRandomViewerRespMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
outGW.send(msgEvent)
|
||||
@ -34,9 +34,8 @@ trait SelectRandomViewerReqMsgHdlr extends RightsManagementTrait {
|
||||
val users = Users2x.findNotPresentersNorModerators(liveMeeting.users2x)
|
||||
val randNum = new scala.util.Random
|
||||
|
||||
if (users.size > 0) {
|
||||
broadcastEvent(msg, users(randNum.nextInt(users.size)))
|
||||
}
|
||||
val userIds = users.map { case (v) => v.intId }
|
||||
broadcastEvent(msg, userIds, if (users.size == 0) -1 else randNum.nextInt(users.size))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,9 @@ trait UserConnectedToGlobalAudioMsgHdlr {
|
||||
talking = false,
|
||||
listenOnly = true,
|
||||
"kms",
|
||||
System.currentTimeMillis()
|
||||
System.currentTimeMillis(),
|
||||
floor = false,
|
||||
lastFloorTime = "0",
|
||||
)
|
||||
|
||||
VoiceUsers.add(liveMeeting.voiceUsers, vu)
|
||||
|
@ -0,0 +1,61 @@
|
||||
package org.bigbluebutton.core.apps.voice
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.models.{ VoiceUserState, VoiceUsers }
|
||||
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting, OutMsgRouter }
|
||||
|
||||
trait AudioFloorChangedVoiceConfEvtMsgHdlr {
|
||||
this: BaseMeetingActor =>
|
||||
|
||||
val liveMeeting: LiveMeeting
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
def handleAudioFloorChangedVoiceConfEvtMsg(msg: AudioFloorChangedVoiceConfEvtMsg): Unit = {
|
||||
|
||||
def broadcastEvent(vu: VoiceUserState): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(
|
||||
MessageTypes.BROADCAST_TO_MEETING,
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
vu.intId
|
||||
)
|
||||
val envelope = BbbCoreEnvelope(AudioFloorChangedEvtMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(
|
||||
AudioFloorChangedEvtMsg.NAME,
|
||||
liveMeeting.props.meetingProp.intId, vu.intId
|
||||
)
|
||||
|
||||
val body = AudioFloorChangedEvtMsgBody(
|
||||
voiceConf = msg.header.voiceConf,
|
||||
intId = vu.intId,
|
||||
voiceUserId = vu.voiceUserId,
|
||||
floor = vu.floor,
|
||||
lastFloorTime = msg.body.floorTimestamp
|
||||
)
|
||||
|
||||
val event = AudioFloorChangedEvtMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
for {
|
||||
oldFloorUser <- VoiceUsers.releasedFloor(
|
||||
liveMeeting.voiceUsers,
|
||||
msg.body.oldVoiceUserId,
|
||||
floor = false
|
||||
)
|
||||
} yield {
|
||||
broadcastEvent(oldFloorUser)
|
||||
}
|
||||
|
||||
for {
|
||||
newFloorUser <- VoiceUsers.becameFloor(
|
||||
liveMeeting.voiceUsers,
|
||||
msg.body.voiceUserId,
|
||||
true,
|
||||
msg.body.floorTimestamp
|
||||
)
|
||||
} yield {
|
||||
broadcastEvent(newFloorUser)
|
||||
}
|
||||
}
|
||||
}
|
@ -268,7 +268,9 @@ object VoiceApp extends SystemConfiguration {
|
||||
talking,
|
||||
listenOnly = isListenOnly,
|
||||
callingInto,
|
||||
System.currentTimeMillis()
|
||||
System.currentTimeMillis(),
|
||||
floor = false,
|
||||
lastFloorTime = "0"
|
||||
)
|
||||
VoiceUsers.add(liveMeeting.voiceUsers, voiceUserState)
|
||||
|
||||
|
@ -18,6 +18,7 @@ trait VoiceApp2x extends UserJoinedVoiceConfEvtMsgHdlr
|
||||
with RecordingStartedVoiceConfEvtMsgHdlr
|
||||
with VoiceConfRunningEvtMsgHdlr
|
||||
with SyncGetVoiceUsersMsgHdlr
|
||||
with AudioFloorChangedVoiceConfEvtMsgHdlr
|
||||
with VoiceConfCallStateEvtMsgHdlr
|
||||
with UserStatusVoiceConfEvtMsgHdlr {
|
||||
|
||||
|
@ -29,6 +29,7 @@ object MeetingEndReason {
|
||||
val ENDED_WHEN_LAST_USER_LEFT = "ENDED_WHEN_LAST_USER_LEFT"
|
||||
val ENDED_AFTER_USER_LOGGED_OUT = "ENDED_AFTER_USER_LOGGED_OUT"
|
||||
val ENDED_AFTER_EXCEEDING_DURATION = "ENDED_AFTER_EXCEEDING_DURATION"
|
||||
val ENDED_BY_PARENT = "ENDED_BY_PARENT"
|
||||
val BREAKOUT_ENDED_EXCEEDING_DURATION = "BREAKOUT_ENDED_EXCEEDING_DURATION"
|
||||
val BREAKOUT_ENDED_BY_MOD = "BREAKOUT_ENDED_BY_MOD"
|
||||
val ENDED_DUE_TO_NO_AUTHED_USER = "ENDED_DUE_TO_NO_AUTHED_USER"
|
||||
}
|
||||
|
@ -259,9 +259,41 @@ object Polls {
|
||||
shape += "status" -> WhiteboardKeyUtil.DRAW_END_STATUS
|
||||
|
||||
val answers = new ArrayBuffer[SimpleVoteOutVO]
|
||||
result.answers.foreach(ans => {
|
||||
answers += SimpleVoteOutVO(ans.id, ans.key, ans.numVotes)
|
||||
})
|
||||
|
||||
def sortByNumVotes(s1: SimpleVoteOutVO, s2: SimpleVoteOutVO) = {
|
||||
s1.numVotes > s2.numVotes
|
||||
}
|
||||
|
||||
val sorted_answers = result.answers.sortWith(sortByNumVotes)
|
||||
|
||||
// Limit the number of answers displayed to minimize
|
||||
// squishing the display.
|
||||
if (sorted_answers.length < 7) {
|
||||
sorted_answers.foreach(ans => {
|
||||
answers += SimpleVoteOutVO(ans.id, ans.key, ans.numVotes)
|
||||
})
|
||||
} else {
|
||||
var highestId = 0
|
||||
|
||||
for (i <- 0 until 7) {
|
||||
val ans = sorted_answers(i)
|
||||
answers += SimpleVoteOutVO(ans.id, ans.key, ans.numVotes)
|
||||
if (ans.id > highestId) {
|
||||
highestId = ans.id
|
||||
}
|
||||
}
|
||||
|
||||
var otherNumVotes = 0
|
||||
for (i <- 7 until sorted_answers.length) {
|
||||
val ans = sorted_answers(i)
|
||||
otherNumVotes += ans.numVotes
|
||||
if (ans.id > highestId) {
|
||||
highestId = ans.id
|
||||
}
|
||||
}
|
||||
|
||||
answers += SimpleVoteOutVO(highestId + 1, "...", otherNumVotes)
|
||||
}
|
||||
|
||||
shape += "result" -> answers
|
||||
|
||||
|
@ -67,6 +67,29 @@ object VoiceUsers {
|
||||
}
|
||||
}
|
||||
|
||||
def becameFloor(users: VoiceUsers, voiceUserId: String, floor: Boolean, timestamp: String): Option[VoiceUserState] = {
|
||||
for {
|
||||
u <- findWithVoiceUserId(users, voiceUserId)
|
||||
} yield {
|
||||
val vu = u.modify(_.floor).setTo(floor)
|
||||
.modify(_.lastFloorTime).setTo(timestamp)
|
||||
.modify(_.lastStatusUpdateOn).setTo(System.currentTimeMillis())
|
||||
users.save(vu)
|
||||
vu
|
||||
}
|
||||
}
|
||||
|
||||
def releasedFloor(users: VoiceUsers, voiceUserId: String, floor: Boolean): Option[VoiceUserState] = {
|
||||
for {
|
||||
u <- findWithVoiceUserId(users, voiceUserId)
|
||||
} yield {
|
||||
val vu = u.modify(_.floor).setTo(floor)
|
||||
.modify(_.lastStatusUpdateOn).setTo(System.currentTimeMillis())
|
||||
users.save(vu)
|
||||
vu
|
||||
}
|
||||
}
|
||||
|
||||
def setLastStatusUpdate(users: VoiceUsers, user: VoiceUserState): VoiceUserState = {
|
||||
val vu = user.copy(lastStatusUpdateOn = System.currentTimeMillis())
|
||||
users.save(vu)
|
||||
@ -130,16 +153,18 @@ case class VoiceUser2x(
|
||||
voiceUserId: String
|
||||
)
|
||||
case class VoiceUserVO2x(
|
||||
intId: String,
|
||||
voiceUserId: String,
|
||||
callerName: String,
|
||||
callerNum: String,
|
||||
joined: Boolean,
|
||||
locked: Boolean,
|
||||
muted: Boolean,
|
||||
talking: Boolean,
|
||||
callingWith: String,
|
||||
listenOnly: Boolean
|
||||
intId: String,
|
||||
voiceUserId: String,
|
||||
callerName: String,
|
||||
callerNum: String,
|
||||
joined: Boolean,
|
||||
locked: Boolean,
|
||||
muted: Boolean,
|
||||
talking: Boolean,
|
||||
callingWith: String,
|
||||
listenOnly: Boolean,
|
||||
floor: Boolean,
|
||||
lastFloorTime: String
|
||||
)
|
||||
|
||||
case class VoiceUserState(
|
||||
@ -152,5 +177,7 @@ case class VoiceUserState(
|
||||
talking: Boolean,
|
||||
listenOnly: Boolean,
|
||||
calledInto: String,
|
||||
lastStatusUpdateOn: Long
|
||||
lastStatusUpdateOn: Long,
|
||||
floor: Boolean,
|
||||
lastFloorTime: String
|
||||
)
|
||||
|
@ -167,6 +167,8 @@ class ReceivedJsonMsgHandlerActor(
|
||||
routeGenericMsg[MuteMeetingCmdMsg](envelope, jsonNode)
|
||||
case IsMeetingMutedReqMsg.NAME =>
|
||||
routeGenericMsg[IsMeetingMutedReqMsg](envelope, jsonNode)
|
||||
case AudioFloorChangedVoiceConfEvtMsg.NAME =>
|
||||
routeVoiceMsg[AudioFloorChangedVoiceConfEvtMsg](envelope, jsonNode)
|
||||
case CheckRunningAndRecordingVoiceConfEvtMsg.NAME =>
|
||||
routeVoiceMsg[CheckRunningAndRecordingVoiceConfEvtMsg](envelope, jsonNode)
|
||||
case UserStatusVoiceConfEvtMsg.NAME =>
|
||||
@ -234,6 +236,8 @@ class ReceivedJsonMsgHandlerActor(
|
||||
routeGenericMsg[GetAllPresentationPodsReqMsg](envelope, jsonNode)
|
||||
case PreuploadedPresentationsSysPubMsg.NAME =>
|
||||
routeGenericMsg[PreuploadedPresentationsSysPubMsg](envelope, jsonNode)
|
||||
case PresentationUploadedFileTooLargeErrorSysPubMsg.NAME =>
|
||||
routeGenericMsg[PresentationUploadedFileTooLargeErrorSysPubMsg](envelope, jsonNode)
|
||||
case PresentationConversionUpdateSysPubMsg.NAME =>
|
||||
routeGenericMsg[PresentationConversionUpdateSysPubMsg](envelope, jsonNode)
|
||||
case PresentationPageCountErrorSysPubMsg.NAME =>
|
||||
|
@ -205,12 +205,12 @@ trait HandlerHelpers extends SystemConfiguration {
|
||||
outGW.send(event)
|
||||
}
|
||||
|
||||
def endAllBreakoutRooms(eventBus: InternalEventBus, liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = {
|
||||
def endAllBreakoutRooms(eventBus: InternalEventBus, liveMeeting: LiveMeeting, state: MeetingState2x, reason: String): MeetingState2x = {
|
||||
for {
|
||||
model <- state.breakout
|
||||
} yield {
|
||||
model.rooms.values.foreach { room =>
|
||||
eventBus.publish(BigBlueButtonEvent(room.id, EndBreakoutRoomInternalMsg(liveMeeting.props.breakoutProps.parentId, room.id)))
|
||||
eventBus.publish(BigBlueButtonEvent(room.id, EndBreakoutRoomInternalMsg(liveMeeting.props.breakoutProps.parentId, room.id, reason)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,6 +86,7 @@ class MeetingActor(
|
||||
with DestroyMeetingSysCmdMsgHdlr
|
||||
with SendTimeRemainingUpdateHdlr
|
||||
with SendBreakoutTimeRemainingMsgHdlr
|
||||
with SendBreakoutTimeRemainingInternalMsgHdlr
|
||||
with ChangeLockSettingsInMeetingCmdMsgHdlr
|
||||
with SyncGetMeetingInfoRespMsgHdlr
|
||||
with ClientToServerLatencyTracerMsgHdlr
|
||||
@ -238,16 +239,23 @@ class MeetingActor(
|
||||
|
||||
case msg: ExtendMeetingDuration => handleExtendMeetingDuration(msg)
|
||||
case msg: SendTimeRemainingAuditInternalMsg =>
|
||||
state = handleSendTimeRemainingUpdate(msg, state)
|
||||
if (!liveMeeting.props.meetingProp.isBreakout) {
|
||||
// Update users of meeting remaining time.
|
||||
state = handleSendTimeRemainingUpdate(msg, state)
|
||||
}
|
||||
|
||||
// Update breakout rooms of remaining time
|
||||
state = handleSendBreakoutTimeRemainingMsg(msg, state)
|
||||
case msg: BreakoutRoomCreatedInternalMsg => state = handleBreakoutRoomCreatedInternalMsg(msg, state)
|
||||
case msg: SendBreakoutUsersAuditInternalMsg => handleSendBreakoutUsersUpdateInternalMsg(msg)
|
||||
case msg: BreakoutRoomUsersUpdateInternalMsg => state = handleBreakoutRoomUsersUpdateInternalMsg(msg, state)
|
||||
case msg: EndBreakoutRoomInternalMsg => handleEndBreakoutRoomInternalMsg(msg)
|
||||
case msg: BreakoutRoomEndedInternalMsg => state = handleBreakoutRoomEndedInternalMsg(msg, state)
|
||||
case msg: SendBreakoutTimeRemainingInternalMsg =>
|
||||
handleSendBreakoutTimeRemainingInternalMsg(msg)
|
||||
|
||||
// Screenshare
|
||||
case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg)
|
||||
case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg)
|
||||
|
||||
case msg: SendRecordingTimerInternalMsg =>
|
||||
state = usersApp.handleSendRecordingTimerInternalMsg(msg, state)
|
||||
@ -381,9 +389,10 @@ class MeetingActor(
|
||||
case m: UserTalkingInVoiceConfEvtMsg =>
|
||||
updateVoiceUserLastActivity(m.body.voiceUserId)
|
||||
handleUserTalkingInVoiceConfEvtMsg(m)
|
||||
case m: VoiceConfCallStateEvtMsg => handleVoiceConfCallStateEvtMsg(m)
|
||||
case m: VoiceConfCallStateEvtMsg => handleVoiceConfCallStateEvtMsg(m)
|
||||
|
||||
case m: RecordingStartedVoiceConfEvtMsg => handleRecordingStartedVoiceConfEvtMsg(m)
|
||||
case m: RecordingStartedVoiceConfEvtMsg => handleRecordingStartedVoiceConfEvtMsg(m)
|
||||
case m: AudioFloorChangedVoiceConfEvtMsg => handleAudioFloorChangedVoiceConfEvtMsg(m)
|
||||
case m: MuteUserCmdMsg =>
|
||||
usersApp.handleMuteUserCmdMsg(m)
|
||||
updateUserLastActivity(m.body.mutedBy)
|
||||
@ -411,49 +420,50 @@ class MeetingActor(
|
||||
case m: ChangeLockSettingsInMeetingCmdMsg =>
|
||||
handleSetLockSettings(m)
|
||||
updateUserLastActivity(m.body.setBy)
|
||||
case m: LockUserInMeetingCmdMsg => handleLockUserInMeetingCmdMsg(m)
|
||||
case m: LockUsersInMeetingCmdMsg => handleLockUsersInMeetingCmdMsg(m)
|
||||
case m: GetLockSettingsReqMsg => handleGetLockSettingsReqMsg(m)
|
||||
case m: LockUserInMeetingCmdMsg => handleLockUserInMeetingCmdMsg(m)
|
||||
case m: LockUsersInMeetingCmdMsg => handleLockUsersInMeetingCmdMsg(m)
|
||||
case m: GetLockSettingsReqMsg => handleGetLockSettingsReqMsg(m)
|
||||
|
||||
// Presentation
|
||||
case m: PreuploadedPresentationsSysPubMsg => presentationApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: AssignPresenterReqMsg => state = handlePresenterChange(m, state)
|
||||
case m: PreuploadedPresentationsSysPubMsg => presentationApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: AssignPresenterReqMsg => state = handlePresenterChange(m, state)
|
||||
|
||||
// Presentation Pods
|
||||
case m: CreateNewPresentationPodPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: RemovePresentationPodPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: GetAllPresentationPodsReqMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: SetCurrentPresentationPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationConversionCompletedSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PdfConversionInvalidErrorSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: SetCurrentPagePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: SetPresenterInPodReqMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: RemovePresentationPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: SetPresentationDownloadablePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationConversionUpdateSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationPageGeneratedSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationPageCountErrorSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationUploadTokenReqMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: ResizeAndMovePagePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationPageConvertedSysMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationPageConversionStartedSysMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationConversionEndedSysMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: CreateNewPresentationPodPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: RemovePresentationPodPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: GetAllPresentationPodsReqMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: SetCurrentPresentationPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationConversionCompletedSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PdfConversionInvalidErrorSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: SetCurrentPagePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: SetPresenterInPodReqMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: RemovePresentationPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: SetPresentationDownloadablePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationConversionUpdateSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationUploadedFileTooLargeErrorSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationPageGeneratedSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationPageCountErrorSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationUploadTokenReqMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: ResizeAndMovePagePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationPageConvertedSysMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationPageConversionStartedSysMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: PresentationConversionEndedSysMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||
|
||||
// Caption
|
||||
case m: EditCaptionHistoryPubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: UpdateCaptionOwnerPubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: SendCaptionHistoryReqMsg => captionApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: EditCaptionHistoryPubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: UpdateCaptionOwnerPubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: SendCaptionHistoryReqMsg => captionApp2x.handle(m, liveMeeting, msgBus)
|
||||
|
||||
// Guests
|
||||
case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m)
|
||||
case m: SetGuestPolicyCmdMsg => handleSetGuestPolicyMsg(m)
|
||||
case m: SetGuestLobbyMessageCmdMsg => handleSetGuestLobbyMessageMsg(m)
|
||||
case m: GuestsWaitingApprovedMsg => handleGuestsWaitingApprovedMsg(m)
|
||||
case m: GuestWaitingLeftMsg => handleGuestWaitingLeftMsg(m)
|
||||
case m: GetGuestPolicyReqMsg => handleGetGuestPolicyReqMsg(m)
|
||||
case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m)
|
||||
case m: SetGuestPolicyCmdMsg => handleSetGuestPolicyMsg(m)
|
||||
case m: SetGuestLobbyMessageCmdMsg => handleSetGuestLobbyMessageMsg(m)
|
||||
case m: GuestsWaitingApprovedMsg => handleGuestsWaitingApprovedMsg(m)
|
||||
case m: GuestWaitingLeftMsg => handleGuestWaitingLeftMsg(m)
|
||||
case m: GetGuestPolicyReqMsg => handleGetGuestPolicyReqMsg(m)
|
||||
|
||||
// Chat
|
||||
case m: GetChatHistoryReqMsg => chatApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: GetChatHistoryReqMsg => chatApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: SendPublicMessagePubMsg =>
|
||||
chatApp2x.handle(m, liveMeeting, msgBus)
|
||||
updateUserLastActivity(m.body.message.fromUserId)
|
||||
@ -565,9 +575,13 @@ class MeetingActor(
|
||||
def handleMonitorNumberOfUsers(msg: MonitorNumberOfUsersInternalMsg) {
|
||||
state = removeUsersWithExpiredUserLeftFlag(liveMeeting, state)
|
||||
|
||||
val (newState, expireReason) = ExpiryTrackerHelper.processMeetingExpiryAudit(outGW, eventBus, liveMeeting, state)
|
||||
state = newState
|
||||
expireReason foreach (reason => log.info("Meeting {} expired with reason {}", props.meetingProp.intId, reason))
|
||||
if (!liveMeeting.props.meetingProp.isBreakout) {
|
||||
// Track expiry only for non-breakout rooms. The breakout room lifecycle is
|
||||
// driven by the parent meeting.
|
||||
val (newState, expireReason) = ExpiryTrackerHelper.processMeetingExpiryAudit(outGW, eventBus, liveMeeting, state)
|
||||
state = newState
|
||||
expireReason foreach (reason => log.info("Meeting {} expired with reason {}", props.meetingProp.intId, reason))
|
||||
}
|
||||
|
||||
sendRttTraceTest()
|
||||
setRecordingChapterBreak()
|
||||
|
@ -75,11 +75,13 @@ class MeetingActorAudit(
|
||||
// Trigger updating users of time remaining on meeting.
|
||||
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendTimeRemainingAuditInternalMsg(props.meetingProp.intId)))
|
||||
|
||||
// This is a breakout room. Update the main meeting with list of users in this breakout room.
|
||||
eventBus.publish(BigBlueButtonEvent(
|
||||
props.meetingProp.intId,
|
||||
SendBreakoutUsersAuditInternalMsg(props.breakoutProps.parentId, props.meetingProp.intId)
|
||||
))
|
||||
if (props.meetingProp.isBreakout) {
|
||||
// This is a breakout room. Update the main meeting with list of users in this breakout room.
|
||||
eventBus.publish(BigBlueButtonEvent(
|
||||
props.meetingProp.intId,
|
||||
SendBreakoutUsersAuditInternalMsg(props.breakoutProps.parentId, props.meetingProp.intId)
|
||||
))
|
||||
}
|
||||
|
||||
// Trigger recording timer, only for meeting allowing recording
|
||||
if (props.recordProp.record) {
|
||||
|
@ -20,7 +20,7 @@ trait MeetingExpiryTrackerHelper extends HandlerHelpers {
|
||||
for {
|
||||
expireReason <- reason
|
||||
} yield {
|
||||
endAllBreakoutRooms(eventBus, liveMeeting, state)
|
||||
endAllBreakoutRooms(eventBus, liveMeeting, state, expireReason)
|
||||
sendEndMeetingDueToExpiry(expireReason, eventBus, outGW, liveMeeting, "system")
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,10 @@ import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.common2.util.JsonUtil
|
||||
|
||||
object AnalyticsActor {
|
||||
def props(): Props = Props(classOf[AnalyticsActor])
|
||||
def props(includeChat: Boolean): Props = Props(classOf[AnalyticsActor], includeChat)
|
||||
}
|
||||
|
||||
class AnalyticsActor extends Actor with ActorLogging {
|
||||
class AnalyticsActor(val includeChat: Boolean) extends Actor with ActorLogging {
|
||||
|
||||
val TAG = "-- analytics -- "
|
||||
|
||||
@ -22,6 +22,12 @@ class AnalyticsActor extends Actor with ActorLogging {
|
||||
log.info(TAG + json)
|
||||
}
|
||||
|
||||
def logChatMessage(msg: BbbCommonEnvCoreMsg): Unit = {
|
||||
if (includeChat) {
|
||||
logMessage(msg)
|
||||
}
|
||||
}
|
||||
|
||||
def traceMessage(msg: BbbCommonEnvCoreMsg): Unit = {
|
||||
val json = JsonUtil.toJson(msg)
|
||||
log.info(" -- trace -- " + json)
|
||||
@ -120,16 +126,18 @@ class AnalyticsActor extends Actor with ActorLogging {
|
||||
case m: PresentationConversionUpdateEvtMsgBody => logMessage(msg)
|
||||
case m: PresentationPageCountErrorSysPubMsg => logMessage(msg)
|
||||
case m: PresentationPageCountErrorEvtMsg => logMessage(msg)
|
||||
case m: PresentationUploadedFileTooLargeErrorSysPubMsg => logMessage(msg)
|
||||
case m: PresentationUploadedFileTooLargeErrorEvtMsg => logMessage(msg)
|
||||
|
||||
// Group Chats
|
||||
case m: SendGroupChatMessageMsg => logMessage(msg)
|
||||
case m: GroupChatMessageBroadcastEvtMsg => logMessage(msg)
|
||||
case m: GetGroupChatMsgsReqMsg => logMessage(msg)
|
||||
case m: GetGroupChatMsgsRespMsg => logMessage(msg)
|
||||
case m: CreateGroupChatReqMsg => logMessage(msg)
|
||||
case m: GroupChatCreatedEvtMsg => logMessage(msg)
|
||||
case m: GetGroupChatsReqMsg => logMessage(msg)
|
||||
case m: GetGroupChatsRespMsg => logMessage(msg)
|
||||
case m: SendGroupChatMessageMsg => logChatMessage(msg)
|
||||
case m: GroupChatMessageBroadcastEvtMsg => logChatMessage(msg)
|
||||
case m: GetGroupChatMsgsReqMsg => logChatMessage(msg)
|
||||
case m: GetGroupChatMsgsRespMsg => logChatMessage(msg)
|
||||
case m: CreateGroupChatReqMsg => logChatMessage(msg)
|
||||
case m: GroupChatCreatedEvtMsg => logChatMessage(msg)
|
||||
case m: GetGroupChatsReqMsg => logChatMessage(msg)
|
||||
case m: GetGroupChatsRespMsg => logChatMessage(msg)
|
||||
|
||||
// Guest Management
|
||||
case m: GuestsWaitingApprovedMsg => logMessage(msg)
|
||||
|
@ -1,30 +1,41 @@
|
||||
package org.bigbluebutton.core2.message.handlers
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.api.SendTimeRemainingAuditInternalMsg
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||
import org.bigbluebutton.core.api.{ EndBreakoutRoomInternalMsg, SendBreakoutTimeRemainingInternalMsg, SendTimeRemainingAuditInternalMsg }
|
||||
import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
||||
import org.bigbluebutton.core.domain.{ MeetingEndReason, MeetingState2x }
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
|
||||
import org.bigbluebutton.core.util.TimeUtil
|
||||
|
||||
trait SendBreakoutTimeRemainingMsgHdlr {
|
||||
this: MeetingActor =>
|
||||
|
||||
val liveMeeting: LiveMeeting
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
def handleSendBreakoutTimeRemainingMsg(msg: SendTimeRemainingAuditInternalMsg, state: MeetingState2x): MeetingState2x = {
|
||||
|
||||
for {
|
||||
model <- state.breakout
|
||||
startedOn <- model.startedOn
|
||||
} yield {
|
||||
val endMeetingTime = TimeUtil.millisToSeconds(startedOn) + TimeUtil.minutesToSeconds(model.durationInMinutes)
|
||||
val timeRemaining = endMeetingTime - TimeUtil.millisToSeconds(System.currentTimeMillis())
|
||||
|
||||
if (!liveMeeting.props.meetingProp.isBreakout) {
|
||||
|
||||
val endMeetingTime = TimeUtil.millisToSeconds(startedOn) + TimeUtil.minutesToSeconds(model.durationInMinutes)
|
||||
val timeRemaining = endMeetingTime - TimeUtil.millisToSeconds(System.currentTimeMillis())
|
||||
|
||||
// Notify parent meeting users of breakout rooms time remaining
|
||||
val event = buildBreakoutRoomsTimeRemainingUpdateEvtMsg(liveMeeting.props.meetingProp.intId, timeRemaining.toInt)
|
||||
|
||||
outGW.send(event)
|
||||
}
|
||||
|
||||
// Tell all breakout rooms of time remaining so they can notify their users.
|
||||
// This syncs all rooms about time remaining.
|
||||
model.rooms.values.foreach { room =>
|
||||
eventBus.publish(BigBlueButtonEvent(room.id, SendBreakoutTimeRemainingInternalMsg(props.breakoutProps.parentId, timeRemaining.toInt)))
|
||||
}
|
||||
|
||||
if (timeRemaining < 0) {
|
||||
endAllBreakoutRooms(eventBus, liveMeeting, state, MeetingEndReason.BREAKOUT_ENDED_EXCEEDING_DURATION)
|
||||
}
|
||||
}
|
||||
|
||||
state
|
||||
|
@ -1,10 +1,10 @@
|
||||
package org.bigbluebutton.core2.message.handlers
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.api.SendTimeRemainingAuditInternalMsg
|
||||
import org.bigbluebutton.core.domain.{ MeetingState2x }
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting, OutMsgRouter }
|
||||
import org.bigbluebutton.core.util.TimeUtil
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
|
||||
trait SendTimeRemainingUpdateHdlr {
|
||||
this: BaseMeetingActor =>
|
||||
@ -13,26 +13,14 @@ trait SendTimeRemainingUpdateHdlr {
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
def handleSendTimeRemainingUpdate(msg: SendTimeRemainingAuditInternalMsg, state: MeetingState2x): MeetingState2x = {
|
||||
|
||||
if (state.expiryTracker.durationInMs > 0) {
|
||||
val endMeetingTime = state.expiryTracker.endMeetingTime()
|
||||
val timeRemaining = TimeUtil.millisToSeconds(endMeetingTime - TimeUtil.timeNowInMs())
|
||||
|
||||
def buildMeetingTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
|
||||
val envelope = BbbCoreEnvelope(MeetingTimeRemainingUpdateEvtMsg.NAME, routing)
|
||||
val body = MeetingTimeRemainingUpdateEvtMsgBody(timeLeftInSec)
|
||||
val header = BbbClientMsgHeader(MeetingTimeRemainingUpdateEvtMsg.NAME, meetingId, "not-used")
|
||||
val event = MeetingTimeRemainingUpdateEvtMsg(header, body)
|
||||
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
if (timeRemaining > 0) {
|
||||
val event = buildMeetingTimeRemainingUpdateEvtMsg(liveMeeting.props.meetingProp.intId, timeRemaining.toInt)
|
||||
val event = MsgBuilder.buildMeetingTimeRemainingUpdateEvtMsg(liveMeeting.props.meetingProp.intId, timeRemaining.toInt)
|
||||
outGW.send(event)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
state
|
||||
|
@ -13,7 +13,7 @@ trait EndMeetingSysCmdMsgHdlr extends HandlerHelpers {
|
||||
val eventBus: InternalEventBus
|
||||
|
||||
def handleEndMeeting(msg: EndMeetingSysCmdMsg, state: MeetingState2x): Unit = {
|
||||
endAllBreakoutRooms(eventBus, liveMeeting, state)
|
||||
endAllBreakoutRooms(eventBus, liveMeeting, state, MeetingEndReason.ENDED_FROM_API)
|
||||
log.info("Meeting {} ended by from API.", msg.body.meetingId)
|
||||
sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_FROM_API, eventBus, outGW, liveMeeting, "system")
|
||||
}
|
||||
|
@ -494,4 +494,14 @@ object MsgBuilder {
|
||||
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
def buildMeetingTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
|
||||
val envelope = BbbCoreEnvelope(MeetingTimeRemainingUpdateEvtMsg.NAME, routing)
|
||||
val body = MeetingTimeRemainingUpdateEvtMsgBody(timeLeftInSec)
|
||||
val header = BbbClientMsgHeader(MeetingTimeRemainingUpdateEvtMsg.NAME, meetingId, "not-used")
|
||||
val event = MeetingTimeRemainingUpdateEvtMsg(header, body)
|
||||
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
}
|
||||
|
@ -60,19 +60,21 @@ object FakeUserGenerator {
|
||||
}
|
||||
|
||||
def createFakeVoiceUser(user: RegisteredUser, callingWith: String, muted: Boolean, talking: Boolean,
|
||||
listenOnly: Boolean): VoiceUserState = {
|
||||
listenOnly: Boolean, floor: Boolean = false): VoiceUserState = {
|
||||
val voiceUserId = RandomStringGenerator.randomAlphanumericString(8)
|
||||
val lastFloorTime = System.currentTimeMillis().toString();
|
||||
VoiceUserState(intId = user.id, voiceUserId = voiceUserId, callingWith, callerName = user.name,
|
||||
callerNum = user.name, muted, talking, listenOnly, "freeswitch", System.currentTimeMillis())
|
||||
callerNum = user.name, muted, talking, listenOnly, "freeswitch", System.currentTimeMillis(), floor, lastFloorTime)
|
||||
}
|
||||
|
||||
def createFakeVoiceOnlyUser(callingWith: String, muted: Boolean, talking: Boolean,
|
||||
listenOnly: Boolean): VoiceUserState = {
|
||||
listenOnly: Boolean, floor: Boolean = false): VoiceUserState = {
|
||||
val voiceUserId = RandomStringGenerator.randomAlphanumericString(8)
|
||||
val intId = "v_" + RandomStringGenerator.randomAlphanumericString(16)
|
||||
val name = getRandomElement(firstNames, random) + " " + getRandomElement(lastNames, random)
|
||||
val lastFloorTime = System.currentTimeMillis().toString();
|
||||
VoiceUserState(intId, voiceUserId = voiceUserId, callingWith, callerName = name,
|
||||
callerNum = name, muted, talking, listenOnly, "freeswitch", System.currentTimeMillis())
|
||||
callerNum = name, muted, talking, listenOnly, "freeswitch", System.currentTimeMillis(), floor, lastFloorTime)
|
||||
}
|
||||
|
||||
def createFakeWebcamStreamFor(userId: String, viewers: Set[String]): WebcamStream = {
|
||||
|
@ -78,6 +78,10 @@ apps {
|
||||
endMeetingWhenNoMoreAuthedUsersAfterMinutes = 2
|
||||
}
|
||||
|
||||
analytics {
|
||||
includeChat = true
|
||||
}
|
||||
|
||||
voiceConf {
|
||||
recordPath = "/var/freeswitch/meetings"
|
||||
# Use ogg instead of wav to get smaller audio files.
|
||||
|
@ -1,73 +0,0 @@
|
||||
<!-- http://wiki.freeswitch.org/wiki/Mod_conference -->
|
||||
<!-- None of these paths are real if you want any of these options you need to really set them up -->
|
||||
<configuration name="conference.conf" description="Audio Conference">
|
||||
<!-- Advertise certain presence on startup . -->
|
||||
<advertise>
|
||||
<room name="3001@$${domain}" status="FreeSWITCH"/>
|
||||
</advertise>
|
||||
|
||||
<!-- These are the default keys that map when you do not specify a caller control group -->
|
||||
<!-- Note: none and default are reserved names for group names. Disabled if dist-dtmf member flag is set. -->
|
||||
<caller-controls>
|
||||
<group name="default">
|
||||
<control action="mute" digits="0"/>
|
||||
<control action="deaf mute" digits="*"/>
|
||||
<control action="energy up" digits="9"/>
|
||||
<control action="energy equ" digits="8"/>
|
||||
<control action="energy dn" digits="7"/>
|
||||
<control action="vol talk up" digits="3"/>
|
||||
<control action="vol talk zero" digits="2"/>
|
||||
<control action="vol talk dn" digits="1"/>
|
||||
<control action="vol listen up" digits="6"/>
|
||||
<control action="vol listen zero" digits="5"/>
|
||||
<control action="vol listen dn" digits="4"/>
|
||||
<control action="hangup" digits="#"/>
|
||||
</group>
|
||||
</caller-controls>
|
||||
|
||||
<!-- Profiles are collections of settings you can reference by name. -->
|
||||
<profiles>
|
||||
|
||||
<!-- profile used for WebRTC Desktop Sharing -->
|
||||
<profile name="video-mcu-stereo">
|
||||
<param name="domain" value="$${domain}"/>
|
||||
<param name="rate" value="48000"/>
|
||||
<param name="channels" value="2"/>
|
||||
<param name="interval" value="20"/>
|
||||
<param name="energy-level" value="200"/>
|
||||
<!-- <param name="tts-engine" value="flite"/> -->
|
||||
<!-- <param name="tts-voice" value="kal16"/> -->
|
||||
|
||||
<!--remove audio for when user is alone since we hit this case every time-->
|
||||
<!--with -DESKSHARE conference. It has a single user only by default-->
|
||||
|
||||
<!--<param name="muted-sound" value="conference/conf-muted.wav"/>-->
|
||||
<!--<param name="unmuted-sound" value="conference/conf-unmuted.wav"/>-->
|
||||
<!-- <param name="alone-sound" value="conference/conf-alone.wav"/> -->
|
||||
<!-- <param name="moh-sound" value="local_stream://stereo"/> -->
|
||||
<!--<param name="enter-sound" value="tone_stream://%(200,0,500,600,700)"/>-->
|
||||
<!--<param name="exit-sound" value="tone_stream://%(500,0,300,200,100,50,25)"/>-->
|
||||
<!--<param name="kicked-sound" value="conference/conf-kicked.wav"/>-->
|
||||
<!--<param name="locked-sound" value="conference/conf-locked.wav"/>-->
|
||||
<!--<param name="is-locked-sound" value="conference/conf-is-locked.wav"/>-->
|
||||
<!--<param name="is-unlocked-sound" value="conference/conf-is-unlocked.wav"/>-->
|
||||
<!--<param name="pin-sound" value="conference/conf-pin.wav"/>-->
|
||||
<!--<param name="bad-pin-sound" value="conference/conf-bad-pin.wav"/>-->
|
||||
|
||||
<param name="caller-id-name" value="$${outbound_caller_name}"/>
|
||||
<param name="caller-id-number" value="$${outbound_caller_id}"/>
|
||||
<param name="comfort-noise" value="false"/>
|
||||
<param name="conference-flags" value="video-floor-only|video-required-for-canvas|rfc-4579|livearray-sync|minimize-video-encoding"/>
|
||||
|
||||
<param name="video-mode" value="mux"/> <!-- other values for video-mode are transcode or passthrough -->
|
||||
<param name="video-layout-name" value="1x1"/> <!-- 1x1 since we only have 1 video stream -->
|
||||
<param name="video-layout-name" value="group:grid"/>
|
||||
<param name="video-canvas-size" value="1920x1080"/>
|
||||
<param name="video-canvas-bgcolor" value="#333333"/>
|
||||
<param name="video-layout-bgcolor" value="#000000"/>
|
||||
<param name="video-codec-bandwidth" value="1mb"/>
|
||||
<param name="video-fps" value="15"/>
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
</configuration>
|
@ -1,30 +0,0 @@
|
||||
<configuration name="verto.conf" description="HTML5 Verto Endpoint">
|
||||
|
||||
<settings>
|
||||
<param name="debug" value="10"/>
|
||||
</settings>
|
||||
|
||||
<profiles>
|
||||
<profile name="mine">
|
||||
<param name="bind-local" value="0.0.0.0:8081"/>
|
||||
<param name="bind-local" value="0.0.0.0:8082" secure="true"/>
|
||||
<param name="force-register-domain" value="$${domain}"/>
|
||||
<param name="secure-combined" value="$${certs_dir}/wss.pem"/>
|
||||
<param name="secure-chain" value="$${certs_dir}/wss.pem"/>
|
||||
<param name="userauth" value="true"/>
|
||||
<!-- setting this to true will allow anyone to register even with no account so use with care -->
|
||||
<param name="blind-reg" value="false"/>
|
||||
<param name="mcast-ip" value="224.1.1.1"/>
|
||||
<param name="mcast-port" value="1337"/>
|
||||
<param name="rtp-ip" value="$${local_ip_v4}"/>
|
||||
<!-- <param name="ext-rtp-ip" value=""/> -->
|
||||
<param name="local-network" value="localnet.auto"/>
|
||||
<param name="outbound-codec-string" value="opus,vp8"/>
|
||||
<param name="inbound-codec-string" value="opus,vp8"/>
|
||||
<param name="apply-candidate-acl" value="wan.auto"/>
|
||||
<param name="timer-name" value="soft"/>
|
||||
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</configuration>
|
@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
NOTICE:
|
||||
|
||||
This context is usually accessed via authenticated callers on the sip profile on port 5060
|
||||
or transfered callers from the public context which arrived via the sip profile on port 5080.
|
||||
|
||||
Authenticated users will use the user_context variable on the user to determine what context
|
||||
they can access. You can also add a user in the directory with the cidr= attribute acl.conf.xml
|
||||
will build the domains ACL using this value.
|
||||
-->
|
||||
<!-- http://wiki.freeswitch.org/wiki/Dialplan_XML -->
|
||||
<include>
|
||||
<context name="default">
|
||||
<extension name="public_extensions">
|
||||
<condition field="destination_number" expression="^\d{5}-DESKSHARE$">
|
||||
<action application="log" data="INFO AAAAAA transferring to $1 XML public!!"/>
|
||||
<action application="transfer" data="${destination_number} XML public"/>
|
||||
</condition>
|
||||
</extension>
|
||||
|
||||
<!-- other extensions -->
|
||||
|
||||
</context>
|
||||
</include>
|
||||
|
@ -1,32 +0,0 @@
|
||||
<!--
|
||||
NOTICE:
|
||||
|
||||
This context is usually accessed via the external sip profile listening on port 5080.
|
||||
|
||||
It is recommended to have separate inbound and outbound contexts. Not only for security
|
||||
but clearing up why you would need to do such a thing. You don't want outside un-authenticated
|
||||
callers hitting your default context which allows dialing calls thru your providers and results
|
||||
in Toll Fraud.
|
||||
-->
|
||||
|
||||
<!-- http://wiki.freeswitch.org/wiki/Dialplan_XML -->
|
||||
<include>
|
||||
<context name="public">
|
||||
|
||||
<!-- other extensions -->
|
||||
|
||||
<extension name="public_extensions">
|
||||
<condition field="destination_number" expression="^\d{5}-DESKSHARE$">
|
||||
<action application="log" data="INFO ************WEBRTC DESKSHARE EXT***********" />
|
||||
<action application="answer"/>
|
||||
<action application="conference" data="${destination_number}@video-mcu-stereo"/>
|
||||
</condition>
|
||||
</extension>
|
||||
|
||||
<!--
|
||||
You can place files in the public directory to get included.
|
||||
-->
|
||||
<X-PRE-PROCESS cmd="include" data="public/*.xml"/>
|
||||
|
||||
</context>
|
||||
</include>
|
@ -2,5 +2,4 @@
|
||||
|
||||
rm -rf src/main/resources
|
||||
cp -R src/universal/conf src/main/resources
|
||||
sbt run
|
||||
|
||||
exec sbt run
|
||||
|
@ -3,4 +3,4 @@
|
||||
sbt clean stage
|
||||
sudo service bbb-fsesl-akka stop
|
||||
cd target/universal/stage
|
||||
./bin/bbb-fsesl-akka
|
||||
exec ./bin/bbb-fsesl-akka
|
||||
|
@ -89,6 +89,14 @@ public class FreeswitchConferenceEventListener implements ConferenceEventListene
|
||||
vcs.deskShareRTMPBroadcastStopped(evt.getRoom(), evt.getBroadcastingStreamUrl(),
|
||||
evt.getVideoWidth(), evt.getVideoHeight(), evt.getTimestamp());
|
||||
}
|
||||
} else if (event instanceof AudioFloorChangedEvent) {
|
||||
AudioFloorChangedEvent evt = (AudioFloorChangedEvent) event;
|
||||
vcs.audioFloorChanged(
|
||||
evt.getRoom(),
|
||||
evt.getVoiceUserId(),
|
||||
evt.getOldVoiceUserId(),
|
||||
evt.getFloorTimestamp()
|
||||
);
|
||||
} else if (event instanceof VoiceConfRunningAndRecordingEvent) {
|
||||
VoiceConfRunningAndRecordingEvent evt = (VoiceConfRunningAndRecordingEvent) event;
|
||||
if (evt.running && ! evt.recording) {
|
||||
|
@ -64,6 +64,11 @@ public interface IVoiceConferenceService {
|
||||
Integer videoHeight,
|
||||
String timestamp);
|
||||
|
||||
void audioFloorChanged(String room,
|
||||
String voiceUserId,
|
||||
String oldVoiceUserId,
|
||||
String floorTimestamp);
|
||||
|
||||
void voiceConfRunningAndRecording(String room,
|
||||
Boolean isRunning,
|
||||
Boolean isRecording,
|
||||
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along
|
||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.bigbluebutton.freeswitch.voice.events;
|
||||
|
||||
public class AudioFloorChangedEvent extends VoiceConferenceEvent {
|
||||
|
||||
private final String voiceUserId;
|
||||
private final String oldVoiceUserId;
|
||||
private final String floorTimestamp;
|
||||
|
||||
public AudioFloorChangedEvent(
|
||||
String room,
|
||||
String voiceUserId,
|
||||
String oldVoiceUserId,
|
||||
String floorTimestamp
|
||||
) {
|
||||
super(room);
|
||||
this.voiceUserId = voiceUserId;
|
||||
this.oldVoiceUserId = oldVoiceUserId;
|
||||
this.floorTimestamp = floorTimestamp;
|
||||
}
|
||||
|
||||
public String getVoiceUserId() {
|
||||
return voiceUserId;
|
||||
}
|
||||
|
||||
public String getOldVoiceUserId() {
|
||||
return oldVoiceUserId;
|
||||
}
|
||||
|
||||
public String getFloorTimestamp() {
|
||||
return floorTimestamp;
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ public class ESLEventListener implements IEslEventListener {
|
||||
private static final String STOP_RECORDING_EVENT = "stop-recording";
|
||||
private static final String CONFERENCE_CREATED_EVENT = "conference-create";
|
||||
private static final String CONFERENCE_DESTROYED_EVENT = "conference-destroy";
|
||||
private static final String FLOOR_CHANGE_EVENT = "video-floor-change";
|
||||
|
||||
private static final String SCREENSHARE_CONFERENCE_NAME_SUFFIX = "-SCREENSHARE";
|
||||
|
||||
@ -197,6 +198,12 @@ public class ESLEventListener implements IEslEventListener {
|
||||
} else if (action.equals(CONFERENCE_DESTROYED_EVENT)) {
|
||||
VoiceConfRunningEvent pt = new VoiceConfRunningEvent(confName, false);
|
||||
conferenceEventListener.handleConferenceEvent(pt);
|
||||
} else if (action.equals(FLOOR_CHANGE_EVENT)) {
|
||||
String holderMemberId = this.getNewFloorHolderMemberIdFromEvent(event);
|
||||
String oldHolderMemberId = this.getOldFloorHolderMemberIdFromEvent(event);
|
||||
String floorTimestamp = event.getEventHeaders().get("Event-Date-Timestamp");
|
||||
AudioFloorChangedEvent vFloor= new AudioFloorChangedEvent(confName, holderMemberId, oldHolderMemberId, floorTimestamp);
|
||||
conferenceEventListener.handleConferenceEvent(vFloor);
|
||||
} else {
|
||||
log.warn("Unknown conference Action [" + action + "]");
|
||||
}
|
||||
@ -507,6 +514,22 @@ public class ESLEventListener implements IEslEventListener {
|
||||
return e.getEventHeaders().get("Path");
|
||||
}
|
||||
|
||||
private String getOldFloorHolderMemberIdFromEvent(EslEvent e) {
|
||||
String oldFloorHolder = e.getEventHeaders().get("Old-ID");
|
||||
if(oldFloorHolder == null || oldFloorHolder.equalsIgnoreCase("none")) {
|
||||
oldFloorHolder= "";
|
||||
}
|
||||
return oldFloorHolder;
|
||||
}
|
||||
|
||||
private String getNewFloorHolderMemberIdFromEvent(EslEvent e) {
|
||||
String newHolder = e.getEventHeaders().get("New-ID");
|
||||
if(newHolder == null || newHolder.equalsIgnoreCase("none")) {
|
||||
newHolder = "";
|
||||
}
|
||||
return newHolder;
|
||||
}
|
||||
|
||||
// Distinguish between recording to a file:
|
||||
// /path/to/a/file.mp4
|
||||
// and broadcasting a stream:
|
||||
|
@ -276,6 +276,28 @@ class VoiceConferenceService(healthz: HealthzService,
|
||||
sender.publish(fromVoiceConfRedisChannel, json)
|
||||
}
|
||||
|
||||
def audioFloorChanged(
|
||||
voiceConfId: String,
|
||||
voiceUserId: String,
|
||||
oldVoiceUserId: String,
|
||||
floorTimestamp: String
|
||||
) {
|
||||
val header = BbbCoreVoiceConfHeader(AudioFloorChangedVoiceConfEvtMsg.NAME, voiceConfId)
|
||||
val body = AudioFloorChangedVoiceConfEvtMsgBody(
|
||||
voiceConfId,
|
||||
voiceUserId,
|
||||
oldVoiceUserId,
|
||||
floorTimestamp
|
||||
);
|
||||
val envelope = BbbCoreEnvelope(AudioFloorChangedVoiceConfEvtMsg.NAME, Map("voiceConf" -> voiceConfId))
|
||||
|
||||
val msg = new AudioFloorChangedVoiceConfEvtMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, msg)
|
||||
|
||||
val json = JsonUtil.toJson(msgEvent)
|
||||
sender.publish(fromVoiceConfRedisChannel, json)
|
||||
}
|
||||
|
||||
def voiceCallStateEvent(
|
||||
conf: String,
|
||||
callSession: String,
|
||||
|
@ -22,7 +22,6 @@ package org.bigbluebutton.common2.redis;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sun.org.apache.xpath.internal.operations.Bool;
|
||||
import io.lettuce.core.api.sync.BaseRedisCommands;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -147,6 +147,21 @@ case class PresentationConversionEndedSysMsgBody(
|
||||
presName: String
|
||||
)
|
||||
|
||||
object PresentationUploadedFileTooLargeErrorSysPubMsg { val NAME = "PresentationUploadedFileTooLargeErrorSysPubMsg" }
|
||||
case class PresentationUploadedFileTooLargeErrorSysPubMsg(
|
||||
header: BbbClientMsgHeader,
|
||||
body: PresentationUploadedFileTooLargeErrorSysPubMsgBody
|
||||
) extends StandardMsg
|
||||
case class PresentationUploadedFileTooLargeErrorSysPubMsgBody(
|
||||
podId: String,
|
||||
messageKey: String,
|
||||
code: String,
|
||||
presentationName: String,
|
||||
presentationToken: String,
|
||||
fileSize: Int,
|
||||
maxFileSize: Int
|
||||
)
|
||||
|
||||
// ------------ bbb-common-web to akka-apps ------------
|
||||
|
||||
// ------------ akka-apps to client ------------
|
||||
@ -198,6 +213,10 @@ case class PresentationPageConvertedEventMsgBody(
|
||||
page: PresentationPageVO
|
||||
)
|
||||
|
||||
object PresentationUploadedFileTooLargeErrorEvtMsg { val NAME = "PresentationUploadedFileTooLargeErrorEvtMsg" }
|
||||
case class PresentationUploadedFileTooLargeErrorEvtMsg(header: BbbClientMsgHeader, body: PresentationUploadedFileTooLargeErrorEvtMsgBody) extends BbbCoreMsg
|
||||
case class PresentationUploadedFileTooLargeErrorEvtMsgBody(podId: String, messageKey: String, code: String, presentationName: String, presentationToken: String, fileSize: Int, maxFileSize: Int)
|
||||
|
||||
object PresentationConversionRequestReceivedEventMsg { val NAME = "PresentationConversionRequestReceivedEventMsg" }
|
||||
case class PresentationConversionRequestReceivedEventMsg(
|
||||
header: BbbClientMsgHeader,
|
||||
@ -281,5 +300,5 @@ case class SyncGetPresentationPodsRespMsgBody(pods: Vector[PresentationPodVO])
|
||||
// ------------ akka-apps to bbb-common-web ------------
|
||||
object PresentationUploadTokenSysPubMsg { val NAME = "PresentationUploadTokenSysPubMsg" }
|
||||
case class PresentationUploadTokenSysPubMsg(header: BbbClientMsgHeader, body: PresentationUploadTokenSysPubMsgBody) extends BbbCoreMsg
|
||||
case class PresentationUploadTokenSysPubMsgBody(podId: String, authzToken: String, filename: String)
|
||||
case class PresentationUploadTokenSysPubMsgBody(podId: String, authzToken: String, filename: String, meetingId: String)
|
||||
// ------------ akka-apps to bbb-common-web ------------
|
||||
|
@ -408,4 +408,4 @@ case class SelectRandomViewerReqMsgBody(requestedBy: String)
|
||||
*/
|
||||
object SelectRandomViewerRespMsg { val NAME = "SelectRandomViewerRespMsg" }
|
||||
case class SelectRandomViewerRespMsg(header: BbbClientMsgHeader, body: SelectRandomViewerRespMsgBody) extends StandardMsg
|
||||
case class SelectRandomViewerRespMsgBody(requestedBy: String, selectedUserId: String)
|
||||
case class SelectRandomViewerRespMsgBody(requestedBy: String, userIds: Vector[String], choice: Integer)
|
||||
|
@ -495,6 +495,24 @@ object SyncGetVoiceUsersRespMsg { val NAME = "SyncGetVoiceUsersRespMsg" }
|
||||
case class SyncGetVoiceUsersRespMsg(header: BbbClientMsgHeader, body: SyncGetVoiceUsersRespMsgBody) extends BbbCoreMsg
|
||||
case class SyncGetVoiceUsersRespMsgBody(voiceUsers: Vector[VoiceConfUser])
|
||||
|
||||
/**
|
||||
* Received from FS that a user has become a floor holder
|
||||
*/
|
||||
object AudioFloorChangedVoiceConfEvtMsg { val NAME = "AudioFloorChangedVoiceConfEvtMsg" }
|
||||
case class AudioFloorChangedVoiceConfEvtMsg(
|
||||
header: BbbCoreVoiceConfHeader,
|
||||
body: AudioFloorChangedVoiceConfEvtMsgBody
|
||||
) extends VoiceStandardMsg
|
||||
case class AudioFloorChangedVoiceConfEvtMsgBody(voiceConf: String, voiceUserId: String, oldVoiceUserId: String, floorTimestamp: String)
|
||||
|
||||
/**
|
||||
* Sent to a client that an user has become a floor holder
|
||||
*/
|
||||
|
||||
object AudioFloorChangedEvtMsg { val NAME = "AudioFloorChangedEvtMsg" }
|
||||
case class AudioFloorChangedEvtMsg(header: BbbClientMsgHeader, body: AudioFloorChangedEvtMsgBody) extends BbbCoreMsg
|
||||
case class AudioFloorChangedEvtMsgBody(voiceConf: String, intId: String, voiceUserId: String, floor: Boolean, lastFloorTime: String)
|
||||
|
||||
/**
|
||||
* Received from FS call state events.
|
||||
*/
|
||||
|
@ -86,6 +86,7 @@ import org.bigbluebutton.api2.IBbbWebApiGWApp;
|
||||
import org.bigbluebutton.api2.domain.UploadedTrack;
|
||||
import org.bigbluebutton.common2.redis.RedisStorageService;
|
||||
import org.bigbluebutton.presentation.PresentationUrlDownloadService;
|
||||
import org.bigbluebutton.presentation.imp.SwfSlidesGenerationProgressNotifier;
|
||||
import org.bigbluebutton.web.services.WaitingGuestCleanupTimerTask;
|
||||
import org.bigbluebutton.web.services.UserCleanupTimerTask;
|
||||
import org.bigbluebutton.web.services.EnteredUserCleanupTimerTask;
|
||||
@ -120,6 +121,7 @@ public class MeetingService implements MessageListener {
|
||||
private RedisStorageService storeService;
|
||||
private CallbackUrlService callbackUrlService;
|
||||
private HTML5LoadBalancingService html5LoadBalancingService;
|
||||
private SwfSlidesGenerationProgressNotifier notifier;
|
||||
|
||||
private long usersTimeout;
|
||||
private long enteredUsersTimeout;
|
||||
@ -314,6 +316,18 @@ public class MeetingService implements MessageListener {
|
||||
return valid;
|
||||
}
|
||||
|
||||
public PresentationUploadToken getPresentationUploadToken(String authzToken) {
|
||||
if(uploadAuthzTokens.containsKey(authzToken)) {
|
||||
return uploadAuthzTokens.get(authzToken);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPresentationUploadMaxFilesizeMessage(PresentationUploadToken presUploadToken, int uploadedFileSize, int maxUploadFileSize) {
|
||||
notifier.sendUploadFileTooLargeMessage(presUploadToken, uploadedFileSize, maxUploadFileSize);
|
||||
}
|
||||
|
||||
private void removeUserSessions(String meetingId) {
|
||||
Iterator<Map.Entry<String, UserSession>> iterator = sessions.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
@ -1241,4 +1255,9 @@ public class MeetingService implements MessageListener {
|
||||
public void setEnteredUsersTimeout(long value) {
|
||||
enteredUsersTimeout = value;
|
||||
}
|
||||
|
||||
public void setSwfSlidesGenerationProgressNotifier(SwfSlidesGenerationProgressNotifier notifier) {
|
||||
this.notifier = notifier;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,10 +4,12 @@ public class PresentationUploadToken implements IMessage {
|
||||
public final String podId;
|
||||
public final String authzToken;
|
||||
public final String filename;
|
||||
public final String meetingId;
|
||||
|
||||
public PresentationUploadToken(String podId, String authzToken, String filename) {
|
||||
public PresentationUploadToken(String podId, String authzToken, String filename, String meetingId) {
|
||||
this.podId = podId;
|
||||
this.authzToken = authzToken;
|
||||
this.filename = filename;
|
||||
this.meetingId = meetingId;
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ public class ConversionMessageConstants {
|
||||
public static final String OFFICE_DOC_CONVERSION_SUCCESS_KEY = "OFFICE_DOC_CONVERSION_SUCCESS";
|
||||
public static final String OFFICE_DOC_CONVERSION_FAILED_KEY = "OFFICE_DOC_CONVERSION_FAILED";
|
||||
public static final String OFFICE_DOC_CONVERSION_INVALID_KEY = "OFFICE_DOC_CONVERSION_INVALID";
|
||||
public static final String FILE_TOO_LARGE = "FILE_TOO_LARGE";
|
||||
public static final String SUPPORTED_DOCUMENT_KEY = "SUPPORTED_DOCUMENT";
|
||||
public static final String UNSUPPORTED_DOCUMENT_KEY = "UNSUPPORTED_DOCUMENT";
|
||||
public static final String PAGE_COUNT_FAILED_KEY = "PAGE_COUNT_FAILED";
|
||||
|
@ -89,4 +89,9 @@ public abstract class AbstractCommandHandler extends
|
||||
public Boolean isCommandSuccessful() {
|
||||
return !exitedWithError();
|
||||
}
|
||||
|
||||
public Boolean isCommandTimeout() {
|
||||
return exitCode == 124;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -59,18 +59,22 @@ public abstract class Office2PdfPageConverter {
|
||||
|
||||
log.info(String.format("Calling conversion script %s.", presOfficeConversionExec));
|
||||
|
||||
NuProcessBuilder officeConverterExec = new NuProcessBuilder(Arrays.asList(presOfficeConversionExec, presentationFile.getAbsolutePath(), output.getAbsolutePath()));
|
||||
NuProcessBuilder officeConverterExec = new NuProcessBuilder(Arrays.asList("timeout", conversionTimeout + "s", "/bin/sh", "-c",
|
||||
"\""+presOfficeConversionExec + "\" \"" + presentationFile.getAbsolutePath() + "\" \"" + output.getAbsolutePath()+"\""));
|
||||
Office2PdfConverterHandler office2PdfConverterHandler = new Office2PdfConverterHandler();
|
||||
|
||||
officeConverterExec.setProcessListener(office2PdfConverterHandler);
|
||||
|
||||
NuProcess process = officeConverterExec.start();
|
||||
try {
|
||||
process.waitFor(conversionTimeout, TimeUnit.SECONDS);
|
||||
process.waitFor(conversionTimeout + 1, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("InterruptedException while counting PDF pages {}", presentationFile.getName(), e);
|
||||
}
|
||||
|
||||
if(office2PdfConverterHandler.isCommandTimeout()) {
|
||||
log.error("Command execution ({}) exceeded the {} secs timeout for {}.",presOfficeConversionExec, conversionTimeout, presentationFile.getName());
|
||||
}
|
||||
|
||||
if(!office2PdfConverterHandler.isCommandSuccessful()) {
|
||||
throw new Exception(String.format("Error while executing conversion script %s.", presOfficeConversionExec));
|
||||
}
|
||||
|
@ -29,8 +29,10 @@ public class SvgImageCreatorImp implements SvgImageCreator {
|
||||
private SwfSlidesGenerationProgressNotifier notifier;
|
||||
private long imageTagThreshold;
|
||||
private long pathsThreshold;
|
||||
private String convTimeout = "60s";
|
||||
private int WAIT_FOR_SEC = 60;
|
||||
private int convPdfToSvgTimeout = 60;
|
||||
private int svgResolutionPpi = 300;
|
||||
private boolean forceRasterizeSlides = false;
|
||||
private int pngWidthRasterizedSlides = 2048;
|
||||
private String BLANK_SVG;
|
||||
|
||||
@Override
|
||||
@ -64,20 +66,24 @@ public class SvgImageCreatorImp implements SvgImageCreator {
|
||||
dest = imagePresentationDir.getAbsolutePath() + File.separator + "slide-1.pdf";
|
||||
|
||||
NuProcessBuilder convertImgToSvg = new NuProcessBuilder(
|
||||
Arrays.asList("timeout", convTimeout, "convert", source, "-auto-orient", dest));
|
||||
Arrays.asList("timeout", convPdfToSvgTimeout + "s", "convert", source, "-auto-orient", dest));
|
||||
|
||||
Png2SvgConversionHandler pHandler = new Png2SvgConversionHandler();
|
||||
convertImgToSvg.setProcessListener(pHandler);
|
||||
|
||||
NuProcess process = convertImgToSvg.start();
|
||||
try {
|
||||
process.waitFor(WAIT_FOR_SEC, TimeUnit.SECONDS);
|
||||
process.waitFor(convPdfToSvgTimeout + 1, TimeUnit.SECONDS);
|
||||
done = true;
|
||||
} catch (InterruptedException e) {
|
||||
done = false;
|
||||
log.error("InterruptedException while converting to SVG {}", dest, e);
|
||||
}
|
||||
|
||||
if(pHandler.isCommandTimeout()) {
|
||||
log.error("Command execution (convertImgToSvg) exceeded the {} secs timeout for {} page {}.", convPdfToSvgTimeout, pres.getName(), page);
|
||||
}
|
||||
|
||||
// Use the intermediate PDF file as source
|
||||
source = dest;
|
||||
}
|
||||
@ -89,26 +95,37 @@ public class SvgImageCreatorImp implements SvgImageCreator {
|
||||
|
||||
File destsvg = new File(imagePresentationDir.getAbsolutePath() + File.separatorChar + "slide" + page + ".svg");
|
||||
|
||||
NuProcessBuilder convertPdfToSvg = createConversionProcess("-svg", page, source, destsvg.getAbsolutePath(),
|
||||
SvgConversionHandler pHandler = new SvgConversionHandler();
|
||||
|
||||
if(this.forceRasterizeSlides == false) {
|
||||
NuProcessBuilder convertPdfToSvg = createConversionProcess("-svg", page, source, destsvg.getAbsolutePath(),
|
||||
true);
|
||||
|
||||
SvgConversionHandler pHandler = new SvgConversionHandler();
|
||||
convertPdfToSvg.setProcessListener(pHandler);
|
||||
convertPdfToSvg.setProcessListener(pHandler);
|
||||
|
||||
NuProcess process = convertPdfToSvg.start();
|
||||
try {
|
||||
process.waitFor(WAIT_FOR_SEC, TimeUnit.SECONDS);
|
||||
done = true;
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted Exception while generating SVG slides {}", pres.getName(), e);
|
||||
NuProcess process = convertPdfToSvg.start();
|
||||
try {
|
||||
process.waitFor(convPdfToSvgTimeout + 1, TimeUnit.SECONDS);
|
||||
done = true;
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted Exception while generating SVG slides {}", pres.getName(), e);
|
||||
}
|
||||
|
||||
if(pHandler.isCommandTimeout()) {
|
||||
log.error("Command execution (convertPdfToSvg) exceeded the {} secs timeout for {} page {}.", convPdfToSvgTimeout, pres.getName(), page);
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
return done;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
return done;
|
||||
}
|
||||
|
||||
if (destsvg.length() == 0 || pHandler.numberOfImageTags() > imageTagThreshold
|
||||
|| pHandler.numberOfPaths() > pathsThreshold) {
|
||||
if (destsvg.length() == 0 ||
|
||||
pHandler.numberOfImageTags() > imageTagThreshold ||
|
||||
pHandler.numberOfPaths() > pathsThreshold ||
|
||||
this.forceRasterizeSlides) {
|
||||
|
||||
// We need t delete the destination file as we are starting a
|
||||
// new conversion process
|
||||
if (destsvg.exists()) {
|
||||
@ -117,21 +134,24 @@ public class SvgImageCreatorImp implements SvgImageCreator {
|
||||
|
||||
done = false;
|
||||
|
||||
Map<String, Object> logData = new HashMap<String, Object>();
|
||||
logData.put("meetingId", pres.getMeetingId());
|
||||
logData.put("presId", pres.getId());
|
||||
logData.put("filename", pres.getName());
|
||||
logData.put("page", page);
|
||||
logData.put("convertSuccess", pHandler.isCommandSuccessful());
|
||||
logData.put("fileExists", destsvg.exists());
|
||||
logData.put("numberOfImages", pHandler.numberOfImageTags());
|
||||
logData.put("numberOfPaths", pHandler.numberOfPaths());
|
||||
logData.put("logCode", "potential_problem_with_svg");
|
||||
logData.put("message", "Potential problem with generated SVG");
|
||||
Gson gson = new Gson();
|
||||
String logStr = gson.toJson(logData);
|
||||
if(!this.forceRasterizeSlides) {
|
||||
Map<String, Object> logData = new HashMap<String, Object>();
|
||||
logData.put("meetingId", pres.getMeetingId());
|
||||
logData.put("presId", pres.getId());
|
||||
logData.put("filename", pres.getName());
|
||||
logData.put("page", page);
|
||||
logData.put("convertSuccess", pHandler.isCommandSuccessful());
|
||||
logData.put("fileExists", destsvg.exists());
|
||||
logData.put("numberOfImages", pHandler.numberOfImageTags());
|
||||
logData.put("numberOfPaths", pHandler.numberOfPaths());
|
||||
logData.put("logCode", "potential_problem_with_svg");
|
||||
logData.put("message", "Potential problem with generated SVG");
|
||||
Gson gson = new Gson();
|
||||
String logStr = gson.toJson(logData);
|
||||
|
||||
log.warn(" --analytics-- data={}", logStr);
|
||||
}
|
||||
|
||||
log.warn(" --analytics-- data={}", logStr);
|
||||
|
||||
File tempPng = null;
|
||||
String basePresentationame = UUID.randomUUID().toString();
|
||||
@ -140,14 +160,15 @@ public class SvgImageCreatorImp implements SvgImageCreator {
|
||||
} catch (IOException ioException) {
|
||||
// We should never fall into this if the server is correctly
|
||||
// configured
|
||||
Map<String, Object> logData = new HashMap<String, Object>();
|
||||
logData = new HashMap<String, Object>();
|
||||
logData.put("meetingId", pres.getMeetingId());
|
||||
logData.put("presId", pres.getId());
|
||||
logData.put("filename", pres.getName());
|
||||
logData.put("logCode", "problem_with_creating_svg");
|
||||
logData.put("message", "Unable to create temporary files");
|
||||
gson = new Gson();
|
||||
logStr = gson.toJson(logData);
|
||||
Gson gson = new Gson();
|
||||
String logStr = gson.toJson(logData);
|
||||
log.error(" --analytics-- data={}", logStr, ioException);
|
||||
}
|
||||
|
||||
@ -159,47 +180,66 @@ public class SvgImageCreatorImp implements SvgImageCreator {
|
||||
convertPdfToPng.setProcessListener(pngHandler);
|
||||
NuProcess pngProcess = convertPdfToPng.start();
|
||||
try {
|
||||
pngProcess.waitFor(WAIT_FOR_SEC, TimeUnit.SECONDS);
|
||||
pngProcess.waitFor(convPdfToSvgTimeout + 1, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted Exception while generating PNG image {}", pres.getName(), e);
|
||||
}
|
||||
|
||||
// Step 2: Convert a PNG image to SVG
|
||||
NuProcessBuilder convertPngToSvg = new NuProcessBuilder(Arrays.asList("timeout", convTimeout, "convert",
|
||||
tempPng.getAbsolutePath(), destsvg.getAbsolutePath()));
|
||||
|
||||
Png2SvgConversionHandler svgHandler = new Png2SvgConversionHandler();
|
||||
convertPngToSvg.setProcessListener(svgHandler);
|
||||
NuProcess svgProcess = convertPngToSvg.start();
|
||||
try {
|
||||
svgProcess.waitFor(WAIT_FOR_SEC, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted Exception while generating SVG image {}", pres.getName(), e);
|
||||
if(pngHandler.isCommandTimeout()) {
|
||||
log.error("Command execution (convertPdfToPng) exceeded the {} secs timeout for {} page {}.", convPdfToSvgTimeout, pres.getName(), page);
|
||||
}
|
||||
|
||||
done = svgHandler.isCommandSuccessful();
|
||||
if(tempPng.length() > 0) {
|
||||
// Step 2: Convert a PNG image to SVG
|
||||
NuProcessBuilder convertPngToSvg = new NuProcessBuilder(Arrays.asList("timeout", convPdfToSvgTimeout + "s", "convert",
|
||||
tempPng.getAbsolutePath(), destsvg.getAbsolutePath()));
|
||||
|
||||
Png2SvgConversionHandler svgHandler = new Png2SvgConversionHandler();
|
||||
convertPngToSvg.setProcessListener(svgHandler);
|
||||
NuProcess svgProcess = convertPngToSvg.start();
|
||||
try {
|
||||
svgProcess.waitFor(convPdfToSvgTimeout + 1, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted Exception while generating SVG image {}", pres.getName(), e);
|
||||
}
|
||||
|
||||
if(svgHandler.isCommandTimeout()) {
|
||||
log.error("Command execution (convertPngToSvg) exceeded the {} secs timeout for {} page {}.", convPdfToSvgTimeout, pres.getName(), page);
|
||||
}
|
||||
|
||||
done = svgHandler.isCommandSuccessful();
|
||||
|
||||
if(destsvg.length() > 0) {
|
||||
// Step 3: Add SVG namespace to the destionation file
|
||||
// Check : https://phabricator.wikimedia.org/T43174
|
||||
NuProcessBuilder addNameSpaceToSVG = new NuProcessBuilder(Arrays.asList("timeout", convPdfToSvgTimeout + "s",
|
||||
"/bin/sh", "-c",
|
||||
"sed -i "
|
||||
+ "'4s|>| xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.2\">|' "
|
||||
+ destsvg.getAbsolutePath()));
|
||||
|
||||
AddNamespaceToSvgHandler namespaceHandler = new AddNamespaceToSvgHandler();
|
||||
addNameSpaceToSVG.setProcessListener(namespaceHandler);
|
||||
NuProcess namespaceProcess = addNameSpaceToSVG.start();
|
||||
try {
|
||||
namespaceProcess.waitFor(convPdfToSvgTimeout + 1, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted Exception while adding SVG namespace {}", pres.getName(), e);
|
||||
}
|
||||
|
||||
if (namespaceHandler.isCommandTimeout()) {
|
||||
log.error("Command execution (addNameSpaceToSVG) exceeded the {} secs timeout for {} page {}.", convPdfToSvgTimeout, pres.getName(), page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the temporary PNG after finishing the image conversion
|
||||
tempPng.delete();
|
||||
|
||||
// Step 3: Add SVG namespace to the destionation file
|
||||
// Check : https://phabricator.wikimedia.org/T43174
|
||||
NuProcessBuilder addNameSpaceToSVG = new NuProcessBuilder(Arrays.asList("timeout", convTimeout,
|
||||
"/bin/sh", "-c",
|
||||
"sed -i "
|
||||
+ "'4s|>| xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.2\">|' "
|
||||
+ destsvg.getAbsolutePath()));
|
||||
|
||||
AddNamespaceToSvgHandler namespaceHandler = new AddNamespaceToSvgHandler();
|
||||
addNameSpaceToSVG.setProcessListener(namespaceHandler);
|
||||
NuProcess namespaceProcess = addNameSpaceToSVG.start();
|
||||
try {
|
||||
namespaceProcess.waitFor(WAIT_FOR_SEC, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted Exception while adding SVG namespace {}", pres.getName(), e);
|
||||
if(tempPng.exists()) {
|
||||
tempPng.delete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
long endConv = System.currentTimeMillis();
|
||||
|
||||
//System.out.println("******** CREATING SVG page " + page + " " + (endConv - startConv));
|
||||
@ -226,13 +266,20 @@ public class SvgImageCreatorImp implements SvgImageCreator {
|
||||
|
||||
private NuProcessBuilder createConversionProcess(String format, int page, String source, String destFile,
|
||||
boolean analyze) {
|
||||
String rawCommand = "pdftocairo -r " + (analyze ? " 300 " : " 150 ") + format + (analyze ? "" : " -singlefile")
|
||||
+ " -q -f " + String.valueOf(page) + " -l " + String.valueOf(page) + " " + source + " " + destFile;
|
||||
String rawCommand = "pdftocairo -r " + this.svgResolutionPpi + " " + format + (analyze ? "" : " -singlefile");
|
||||
|
||||
//Resize png resolution to avoid too large files
|
||||
if(format.equals("-png") && this.pngWidthRasterizedSlides != 0) {
|
||||
rawCommand += " -scale-to-x " + this.pngWidthRasterizedSlides + " -scale-to-y -1 ";
|
||||
}
|
||||
|
||||
rawCommand += " -q -f " + String.valueOf(page) + " -l " + String.valueOf(page) + " " + source + " " + destFile;
|
||||
if (analyze) {
|
||||
rawCommand += " && cat " + destFile;
|
||||
rawCommand += " | egrep 'data:image/png;base64|<path' | sed 's/ / /g' | cut -d' ' -f 1 | sort | uniq -cw 2";
|
||||
}
|
||||
return new NuProcessBuilder(Arrays.asList("timeout", convTimeout, "/bin/sh", "-c", rawCommand));
|
||||
|
||||
return new NuProcessBuilder(Arrays.asList("timeout", convPdfToSvgTimeout + "s", "/bin/sh", "-c", rawCommand));
|
||||
}
|
||||
|
||||
private File determineSvgImagesDirectory(File presentationFile) {
|
||||
@ -278,4 +325,20 @@ public class SvgImageCreatorImp implements SvgImageCreator {
|
||||
SwfSlidesGenerationProgressNotifier notifier) {
|
||||
this.notifier = notifier;
|
||||
}
|
||||
|
||||
public void setConvPdfToSvgTimeout(int convPdfToSvgTimeout) {
|
||||
this.convPdfToSvgTimeout = convPdfToSvgTimeout;
|
||||
}
|
||||
|
||||
public void setSvgResolutionPpi(int svgResolutionPpi) {
|
||||
this.svgResolutionPpi = svgResolutionPpi;
|
||||
}
|
||||
|
||||
public void setForceRasterizeSlides(boolean forceRasterizeSlides) {
|
||||
this.forceRasterizeSlides = forceRasterizeSlides;
|
||||
}
|
||||
|
||||
public void setPngWidthRasterizedSlides(int pngWidthRasterizedSlides) {
|
||||
this.pngWidthRasterizedSlides = pngWidthRasterizedSlides;
|
||||
}
|
||||
}
|
||||
|
@ -18,14 +18,12 @@
|
||||
|
||||
package org.bigbluebutton.presentation.imp;
|
||||
|
||||
import org.bigbluebutton.api.messaging.messages.PresentationUploadToken;
|
||||
import org.bigbluebutton.api2.IBbbWebApiGWApp;
|
||||
import org.bigbluebutton.presentation.ConversionMessageConstants;
|
||||
import org.bigbluebutton.presentation.GeneratedSlidesInfoHelper;
|
||||
import org.bigbluebutton.presentation.UploadedPresentation;
|
||||
import org.bigbluebutton.presentation.messages.DocPageCompletedProgress;
|
||||
import org.bigbluebutton.presentation.messages.DocPageGeneratedProgress;
|
||||
import org.bigbluebutton.presentation.messages.IDocConversionMsg;
|
||||
import org.bigbluebutton.presentation.messages.OfficeDocConversionProgress;
|
||||
import org.bigbluebutton.presentation.messages.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -41,6 +39,17 @@ public class SwfSlidesGenerationProgressNotifier {
|
||||
messagingService.sendDocConversionMsg(msg);
|
||||
}
|
||||
|
||||
public void sendUploadFileTooLargeMessage(PresentationUploadToken pres, int uploadedFileSize, int maxUploadFileSize) {
|
||||
UploadFileTooLargeMessage progress = new UploadFileTooLargeMessage(
|
||||
pres.podId,
|
||||
pres.meetingId,
|
||||
pres.filename,
|
||||
pres.authzToken,
|
||||
ConversionMessageConstants.FILE_TOO_LARGE,
|
||||
uploadedFileSize,
|
||||
maxUploadFileSize);
|
||||
messagingService.sendDocConversionMsg(progress);
|
||||
}
|
||||
|
||||
public void sendConversionUpdateMessage(int slidesCompleted, UploadedPresentation pres, int pageGenerated) {
|
||||
DocPageGeneratedProgress progress = new DocPageGeneratedProgress(pres.getPodId(),
|
||||
|
@ -0,0 +1,27 @@
|
||||
package org.bigbluebutton.presentation.messages;
|
||||
|
||||
public class UploadFileTooLargeMessage implements IDocConversionMsg {
|
||||
public final String podId;
|
||||
public final String meetingId;
|
||||
public final String filename;
|
||||
public final String authzToken;
|
||||
public final String key;
|
||||
public final Integer uploadedFileSize;
|
||||
public final Integer maxUploadFileSize;
|
||||
|
||||
public UploadFileTooLargeMessage(String podId,
|
||||
String meetingId,
|
||||
String filename,
|
||||
String authzToken,
|
||||
String key,
|
||||
Integer uploadedFileSize,
|
||||
Integer maxUploadFileSize) {
|
||||
this.podId = podId;
|
||||
this.meetingId = meetingId;
|
||||
this.filename = filename;
|
||||
this.authzToken = authzToken;
|
||||
this.key = key;
|
||||
this.uploadedFileSize = uploadedFileSize;
|
||||
this.maxUploadFileSize = maxUploadFileSize;
|
||||
}
|
||||
}
|
@ -312,6 +312,9 @@ class BbbWebApiGWApp(
|
||||
} else if (msg.isInstanceOf[DocPageConversionStarted]) {
|
||||
val event = MsgBuilder.buildPresentationPageConversionStartedSysMsg(msg.asInstanceOf[DocPageConversionStarted])
|
||||
msgToAkkaAppsEventBus.publish(MsgToAkkaApps(toAkkaAppsChannel, event))
|
||||
} else if (msg.isInstanceOf[UploadFileTooLargeMessage]) {
|
||||
val event = MsgBuilder.buildPresentationUploadedFileTooLargeErrorSysMsg(msg.asInstanceOf[UploadFileTooLargeMessage])
|
||||
msgToAkkaAppsEventBus.publish(MsgToAkkaApps(toAkkaAppsChannel, event))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,4 +280,17 @@ object MsgBuilder {
|
||||
val req = DeletedRecordingSysMsg(header, body)
|
||||
BbbCommonEnvCoreMsg(envelope, req)
|
||||
}
|
||||
|
||||
def buildPresentationUploadedFileTooLargeErrorSysMsg(msg: UploadFileTooLargeMessage): BbbCommonEnvCoreMsg = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-web")
|
||||
val envelope = BbbCoreEnvelope(PresentationUploadedFileTooLargeErrorSysPubMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(PresentationUploadedFileTooLargeErrorSysPubMsg.NAME, msg.meetingId, msg.authzToken)
|
||||
|
||||
val body = PresentationUploadedFileTooLargeErrorSysPubMsgBody(podId = msg.podId, messageKey = msg.key,
|
||||
code = msg.key, presentationName = msg.filename, presentationToken = msg.authzToken, fileSize = msg.uploadedFileSize.intValue(), maxFileSize = msg.maxUploadFileSize)
|
||||
|
||||
val req = PresentationUploadedFileTooLargeErrorSysPubMsg(header, body)
|
||||
BbbCommonEnvCoreMsg(envelope, req)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ class OldMeetingMsgHdlrActor(val olgMsgGW: OldMessageReceivedGW)
|
||||
}
|
||||
|
||||
def handlePresentationUploadTokenSysPubMsg(msg: PresentationUploadTokenSysPubMsg): Unit = {
|
||||
olgMsgGW.handle(new PresentationUploadToken(msg.body.podId, msg.body.authzToken, msg.body.filename))
|
||||
olgMsgGW.handle(new PresentationUploadToken(msg.body.podId, msg.body.authzToken, msg.body.filename, msg.body.meetingId))
|
||||
}
|
||||
|
||||
def handleGuestsWaitingApprovedEvtMsg(msg: GuestsWaitingApprovedEvtMsg): Unit = {
|
||||
|
@ -519,6 +519,12 @@ public class Client
|
||||
} else if (eventFunc.equals("conference_loop_input")) {
|
||||
listener.conferenceEventAction(uniqueId, confName, confSize, eventAction, event);
|
||||
return;
|
||||
} else if (eventFunc.equals("conference_member_set_floor_holder")) {
|
||||
listener.conferenceEventAction(uniqueId, confName, confSize, eventAction, event);
|
||||
return;
|
||||
} else if (eventFunc.equals("conference_video_set_floor_holder")) {
|
||||
listener.conferenceEventAction(uniqueId, confName, confSize, eventAction, event);
|
||||
return;
|
||||
} else if (eventFunc.equals("stop_talking_handler")) {
|
||||
listener.conferenceEventAction(uniqueId, confName, confSize, eventAction, event);
|
||||
return;
|
||||
|
@ -1,19 +1,44 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
set -u
|
||||
PATH="/bin/:/usr/bin/"
|
||||
|
||||
# Conversion of office files to Pdf using local docker bbb-soffice
|
||||
|
||||
# This script receives two params
|
||||
# This script receives three params
|
||||
# Param 1: Input office file path (e.g. "/tmp/test.odt")
|
||||
# Param 2: Output pdf file path (e.g. "/tmp/test.pdf")
|
||||
# Param 3: Output format (pdf default)
|
||||
|
||||
while [ -z "$randomDirectoryName" -o -d "/tmp/bbb-libreoffice-conversion/$randomDirectoryName" ]; do
|
||||
randomDirectoryName=$(shuf -i 100000000-999999999 -n 1)
|
||||
done
|
||||
if (( $# == 0 )); then
|
||||
echo "Missing parameter 1 (Input office file path)";
|
||||
exit 1
|
||||
elif (( $# == 1 )); then
|
||||
echo "Missing parameter 2 (Output pdf file path)";
|
||||
exit 1
|
||||
fi;
|
||||
|
||||
mkdir -p "/tmp/bbb-libreoffice-conversion/"
|
||||
mkdir "/tmp/bbb-libreoffice-conversion/$randomDirectoryName/"
|
||||
cp "$1" "/tmp/bbb-libreoffice-conversion/$randomDirectoryName/file"
|
||||
sudo /usr/bin/docker run --rm --network none --env="HOME=/tmp/" -w /tmp/ --user=$(printf %05d `id -u`) -v "/tmp/bbb-libreoffice-conversion/$randomDirectoryName/":/data/ --rm bbb-soffice sh -c "/usr/bin/soffice -env:UserInstallation=file:///tmp/ --convert-to pdf --outdir /data /data/file"
|
||||
cp "/tmp/bbb-libreoffice-conversion/$randomDirectoryName/file.pdf" "$2"
|
||||
rm -r "/tmp/bbb-libreoffice-conversion/$randomDirectoryName/"
|
||||
|
||||
#Create tmp dir for conversion
|
||||
mkdir -p "/tmp/bbb-soffice-$(whoami)/"
|
||||
tempDir="$(mktemp -d -p /tmp/bbb-soffice-$(whoami)/)"
|
||||
|
||||
source="$1"
|
||||
dest="$2"
|
||||
|
||||
#If output format is missing, define PDF
|
||||
convertTo="${3:-pdf}"
|
||||
convertToParam="--convert-to $convertTo"
|
||||
|
||||
#If output is html, include param --writer to avoid blank page
|
||||
if [ ${1: -5} == ".html" ]
|
||||
then
|
||||
convertToParam="$convertToParam --writer"
|
||||
fi
|
||||
|
||||
cp "${source}" "$tempDir/file"
|
||||
sudo /usr/bin/docker run --rm --network none --env="HOME=/tmp/" -w /tmp/ --user=$(printf %05d `id -u`) -v "$tempDir/":/data/ -v /usr/share/fonts/:/usr/share/fonts/:ro --rm bbb-soffice sh -c "/usr/bin/soffice -env:UserInstallation=file:///tmp/ $convertToParam --outdir /data /data/file"
|
||||
cp "$tempDir/file.$convertTo" "${dest}"
|
||||
rm -r "$tempDir/"
|
||||
|
||||
exit 0
|
||||
|
@ -1,12 +1,32 @@
|
||||
#/bin/bash
|
||||
#!/bin/bash
|
||||
set -e
|
||||
set -u
|
||||
PATH="/bin/:/usr/bin/"
|
||||
|
||||
# This is a sample script - adjust it per your need
|
||||
# 1 - setup a server with JOD-CONVERTER-REST ( docker run --memory 512m --rm -p 8080:8080 eugenmayer/jodconverter:rest )
|
||||
# 2 - replace the HOST information in below command with your server host
|
||||
|
||||
# This script receives two params
|
||||
# This script receives three params
|
||||
# Param 1: Input office file path (e.g. "/tmp/test.odt")
|
||||
# Param 2: Output pdf file path (e.g. "/tmp/test.pdf")
|
||||
# Param 3: Destination Format (pdf default)
|
||||
|
||||
curl -X POST "http://127.0.0.1:8080/lool/convert-to/pdf" -H "accept: application/octet-stream" -H "Content-Type: multipart/form-data" -F "data=@$1" > $2
|
||||
if (( $# == 0 )); then
|
||||
echo "Missing parameter 1 (Input office file path)";
|
||||
exit 1
|
||||
elif (( $# == 1 )); then
|
||||
echo "Missing parameter 2 (Output pdf file path)";
|
||||
exit 1
|
||||
fi;
|
||||
|
||||
|
||||
source="$1"
|
||||
dest="$2"
|
||||
|
||||
#If output format is missing, define PDF
|
||||
convertTo="${3:-pdf}"
|
||||
|
||||
curl -X POST "http://127.0.0.1:8080/lool/convert-to/$convertTo" -H "accept: application/octet-stream" -H "Content-Type: multipart/form-data" -F "data=@${source}" > "${dest}"
|
||||
|
||||
exit 0
|
||||
|
9
bbb-libreoffice/assets/etherpad-export.sh
Normal file
9
bbb-libreoffice/assets/etherpad-export.sh
Normal file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
#This script is used to enable Etherpad to export to PDF/ODT
|
||||
# 1- Edit /usr/share/etherpad-lite/settings.json
|
||||
# 2- Set "soffice" config to this script path (default "/usr/share/bbb-libreoffice-conversion/etherpad-export.sh")
|
||||
|
||||
/usr/share/bbb-libreoffice-conversion/convert.sh "$8" "$(echo $8 | sed -E -e 's/html|odt/'$7'/')" $7
|
||||
|
||||
exit 0
|
@ -1 +1,4 @@
|
||||
bigbluebutton ALL=(ALL) NOPASSWD: /usr/bin/docker run --rm --network none --env=HOME=/tmp/ -w /tmp/ --user=[0-9][0-9][0-9][0-9][0-9] -v /tmp/bbb-libreoffice-conversion/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]/\:/data/ --rm bbb-soffice sh -c /usr/bin/soffice -env\:UserInstallation=file\:///tmp/ --convert-to pdf --outdir /data /data/file
|
||||
bigbluebutton ALL=(ALL) NOPASSWD: /usr/bin/docker run --rm --network none --env=HOME=/tmp/ -w /tmp/ --user=[0-9][0-9][0-9][0-9][0-9] -v /tmp/bbb-soffice-bigbluebutton/tmp.[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]/\:/data/ -v /usr/share/fonts/\:/usr/share/fonts/\:ro --rm bbb-soffice sh -c /usr/bin/soffice -env\:UserInstallation=file\:///tmp/ --convert-to pdf --outdir /data /data/file
|
||||
etherpad ALL=(ALL) NOPASSWD: /usr/bin/docker run --rm --network none --env=HOME=/tmp/ -w /tmp/ --user=[0-9][0-9][0-9][0-9][0-9] -v /tmp/bbb-soffice-etherpad/tmp.[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]/\:/data/ -v /usr/share/fonts/\:/usr/share/fonts/\:ro --rm bbb-soffice sh -c /usr/bin/soffice -env\:UserInstallation=file\:///tmp/ --convert-to pdf --writer --outdir /data /data/file
|
||||
etherpad ALL=(ALL) NOPASSWD: /usr/bin/docker run --rm --network none --env=HOME=/tmp/ -w /tmp/ --user=[0-9][0-9][0-9][0-9][0-9] -v /tmp/bbb-soffice-etherpad/tmp.[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]/\:/data/ -v /usr/share/fonts/\:/usr/share/fonts/\:ro --rm bbb-soffice sh -c /usr/bin/soffice -env\:UserInstallation=file\:///tmp/ --convert-to odt --writer --outdir /data /data/file
|
||||
etherpad ALL=(ALL) NOPASSWD: /usr/bin/docker run --rm --network none --env=HOME=/tmp/ -w /tmp/ --user=[0-9][0-9][0-9][0-9][0-9] -v /tmp/bbb-soffice-etherpad/tmp.[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]/\:/data/ -v /usr/share/fonts/\:/usr/share/fonts/\:ro --rm bbb-soffice sh -c /usr/bin/soffice -env\:UserInstallation=file\:///tmp/ --convert-to doc --outdir /data /data/file
|
||||
|
@ -2,12 +2,12 @@ FROM openjdk:11-jre
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
|
||||
#Required to install Libreoffice 7
|
||||
RUN echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
|
||||
|
||||
RUN apt update
|
||||
|
||||
RUN apt -y install locales-all fontconfig libxt6 libxrender1
|
||||
RUN apt -y install --no-install-recommends libreoffice fonts-crosextra-carlito fonts-crosextra-caladea fonts-noto fonts-noto-cjk
|
||||
|
||||
|
||||
RUN dpkg-reconfigure fontconfig && fc-cache -f -s -v
|
||||
|
||||
RUN apt install -y -t buster-backports libreoffice
|
||||
|
||||
|
@ -37,6 +37,8 @@ if [ "$FOLDER_CHECK" = "0" ]; then
|
||||
mkdir -m 755 /usr/share/bbb-libreoffice-conversion/
|
||||
cp assets/convert-local.sh /usr/share/bbb-libreoffice-conversion/convert.sh
|
||||
chmod 755 /usr/share/bbb-libreoffice-conversion/convert.sh
|
||||
cp assets/etherpad-export.sh /usr/share/bbb-libreoffice-conversion/etherpad-export.sh
|
||||
chmod 755 /usr/share/bbb-libreoffice-conversion/etherpad-export.sh
|
||||
chown -R root /usr/share/bbb-libreoffice-conversion/
|
||||
else
|
||||
echo "Install folder already exists"
|
||||
@ -50,3 +52,20 @@ else
|
||||
echo "Sudoers file already exists"
|
||||
fi;
|
||||
|
||||
aptInstalledList=$(apt list --installed 2>&1)
|
||||
fontInstalled=0
|
||||
|
||||
for font in fonts-arkpandora fonts-crosextra-carlito fonts-crosextra-caladea fonts-noto fonts-noto-cjk fonts-liberation fonts-arkpandora
|
||||
do
|
||||
if [[ $(echo $aptInstalledList | grep $font | wc -l) = "0" ]]; then
|
||||
echo "Font $font doesn't exists, installing"
|
||||
apt-get install -y --no-install-recommends $font
|
||||
fontInstalled=1
|
||||
else
|
||||
echo "Font $font already installed"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $fontInstalled = "1" ]; then
|
||||
dpkg-reconfigure fontconfig && fc-cache -f -s -v
|
||||
fi
|
||||
|
@ -12,6 +12,8 @@ if [ "$FOLDER_CHECK" = "0" ]; then
|
||||
mkdir -m 755 /usr/share/bbb-libreoffice-conversion/
|
||||
cp assets/convert-remote.sh /usr/share/bbb-libreoffice-conversion/convert.sh
|
||||
chmod 755 /usr/share/bbb-libreoffice-conversion/convert.sh
|
||||
cp assets/etherpad-export.sh /usr/share/bbb-libreoffice-conversion/etherpad-export.sh
|
||||
chmod 755 /usr/share/bbb-libreoffice-conversion/etherpad-export.sh
|
||||
chown -R root /usr/share/bbb-libreoffice-conversion/
|
||||
else
|
||||
echo "Install folder already exists"
|
||||
|
@ -9,5 +9,4 @@ if [ -f webapps/lti.war ]; then
|
||||
rm webapps/lti.war
|
||||
fi
|
||||
|
||||
catalina.sh run
|
||||
|
||||
exec catalina.sh run
|
||||
|
@ -2,4 +2,4 @@
|
||||
rm -rf libs
|
||||
grails clean
|
||||
grails compile
|
||||
grails prod run-app --port 8181
|
||||
exec grails prod run-app --port 8181
|
||||
|
@ -1,5 +0,0 @@
|
||||
<configuration name="abstraction.conf" description="Abstraction">
|
||||
<apis>
|
||||
<api name="user_name" description="Return Name for extension" syntax="<exten>" parse="(.*)" destination="user_data" argument="$1@default var effective_caller_id_name"/>
|
||||
</apis>
|
||||
</configuration>
|
@ -1,12 +0,0 @@
|
||||
<configuration name="alsa.conf" description="Soundcard Endpoint">
|
||||
<settings>
|
||||
<!--Default dialplan and caller-id info -->
|
||||
<param name="dialplan" value="XML"/>
|
||||
<param name="cid-name" value="N800 Alsa"/>
|
||||
<param name="cid-num" value="5555551212"/>
|
||||
|
||||
<!--audio sample rate and interval -->
|
||||
<param name="sample-rate" value="8000"/>
|
||||
<param name="codec-ms" value="20"/>
|
||||
</settings>
|
||||
</configuration>
|
@ -1,87 +0,0 @@
|
||||
<configuration name="amqp.conf" description="mod_amqp">
|
||||
<producers>
|
||||
<profile name="default">
|
||||
<connections>
|
||||
<connection name="primary">
|
||||
<param name="hostname" value="localhost"/>
|
||||
<param name="virtualhost" value="/"/>
|
||||
<param name="username" value="guest"/>
|
||||
<param name="password" value="guest"/>
|
||||
<param name="port" value="5673"/>
|
||||
<param name="heartbeat" value="0"/>
|
||||
</connection>
|
||||
<connection name="secondary">
|
||||
<param name="hostname" value="localhost"/>
|
||||
<param name="virtualhost" value="/"/>
|
||||
<param name="username" value="guest"/>
|
||||
<param name="password" value="guest"/>
|
||||
<param name="port" value="5672"/>
|
||||
<param name="heartbeat" value="0"/>
|
||||
</connection>
|
||||
</connections>
|
||||
<params>
|
||||
<param name="exchange-name" value="TAP.Events"/>
|
||||
<param name="exchange-type" value="topic"/>
|
||||
<param name="circuit_breaker_ms" value="10000"/>
|
||||
<param name="reconnect_interval_ms" value="1000"/>
|
||||
<param name="send_queue_size" value="5000"/>
|
||||
<param name="enable_fallback_format_fields" value="1"/>
|
||||
|
||||
<!-- The routing key is made from the format string, using the header values in the event specified in the format_fields.-->
|
||||
<!-- Fields that are prefixed with a # are treated as literals rather than doing a header lookup -->
|
||||
<param name="format_fields" value="#FreeSWITCH,FreeSWITCH-Hostname,Event-Name,Event-Subclass,Unique-ID"/>
|
||||
|
||||
<!-- If enable_fallback_format_fields is enabled, then you can | separate event headers, and if the first does not exist
|
||||
then the system will check additional configured header values.
|
||||
-->
|
||||
<!-- <param name="format_fields" value="#FreeSWITCH,FreeSWITCH-Hostname|#Unknown,Event-Name,Event-Subclass,Unique-ID"/> -->
|
||||
|
||||
<!-- <param name="event_filter" value="SWITCH_EVENT_ALL"/> -->
|
||||
<param name="event_filter" value="SWITCH_EVENT_CHANNEL_CREATE,SWITCH_EVENT_CHANNEL_DESTROY,SWITCH_EVENT_HEARTBEAT,SWITCH_EVENT_DTMF"/>
|
||||
</params>
|
||||
</profile>
|
||||
</producers>
|
||||
<commands>
|
||||
<profile name="default">
|
||||
<connections>
|
||||
<connection name="primary">
|
||||
<param name="hostname" value="localhost"/>
|
||||
<param name="virtualhost" value="/"/>
|
||||
<param name="username" value="guest"/>
|
||||
<param name="password" value="guest"/>
|
||||
<param name="port" value="5672"/>
|
||||
<param name="heartbeat" value="0"/>
|
||||
</connection>
|
||||
</connections>
|
||||
<params>
|
||||
<param name="exchange-name" value="TAP.Commands"/>
|
||||
<param name="binding_key" value="commandBindingKey"/>
|
||||
<param name="reconnect_interval_ms" value="1000"/>
|
||||
<param name="queue-passive" value="false"/>
|
||||
<param name="queue-durable" value="false"/>
|
||||
<param name="queue-exclusive" value="false"/>
|
||||
<param name="queue-auto-delete" value="true"/>
|
||||
</params>
|
||||
</profile>
|
||||
</commands>
|
||||
<logging>
|
||||
<profile name="default">
|
||||
<connections>
|
||||
<connection name="primary">
|
||||
<param name="hostname" value="localhost"/>
|
||||
<param name="virtualhost" value="/"/>
|
||||
<param name="username" value="guest"/>
|
||||
<param name="password" value="guest"/>
|
||||
<param name="port" value="5672"/>
|
||||
<param name="heartbeat" value="0"/>
|
||||
</connection>
|
||||
</connections>
|
||||
<params>
|
||||
<param name="exchange-name" value="TAP.Logging"/>
|
||||
<param name="send_queue_size" value="5000"/>
|
||||
<param name="reconnect_interval_ms" value="1000"/>
|
||||
<param name="log-levels" value="debug,info,notice,warning,err,crit,alert"/>
|
||||
</params>
|
||||
</profile>
|
||||
</logging>
|
||||
</configuration>
|
@ -1,19 +0,0 @@
|
||||
<configuration name="amr.conf">
|
||||
<settings>
|
||||
<!-- AMR modes (supported bitrates) :
|
||||
mode 0 AMR 4.75 kbps
|
||||
mode 1 AMR 5.15 kbps
|
||||
mode 2 AMR 5.9 kbps
|
||||
mode 3 AMR 6.7 kbps
|
||||
mode 4 AMR 7.4 kbps
|
||||
mode 5 AMR 7.95 kbps
|
||||
mode 6 AMR 10.2 kbps
|
||||
mode 7 AMR 12.2 kbps
|
||||
-->
|
||||
<param name="default-bitrate" value="7"/>
|
||||
<!-- Enable VoLTE specific FMTP -->
|
||||
<param name="volte" value="0"/>
|
||||
<!-- Enable automatic bitrate variation during the call based on RTCP feedback -->
|
||||
<param name="adjust-bitrate" value="0"/>
|
||||
</settings>
|
||||
</configuration>
|
@ -1,7 +0,0 @@
|
||||
<configuration name="amrwb.conf">
|
||||
<settings>
|
||||
<param name="default-bitrate" value="8"/>
|
||||
<param name="volte" value="1"/>
|
||||
<param name="adjust-bitrate" value="0"/>
|
||||
</settings>
|
||||
</configuration>
|
@ -1,167 +0,0 @@
|
||||
<configuration name="avcodec.conf" description="AVCodec Config">
|
||||
<settings>
|
||||
<!-- max bitrate the system support, truncate if over limit -->
|
||||
<!-- <param name="max-bitrate" value="5mb"/> -->
|
||||
|
||||
<!-- <param name="rtp-slice-size" value="1200"/> -->
|
||||
|
||||
<!-- minimum time to generate a new key frame in ms /> -->
|
||||
<!-- <param name="key-frame-min-freq" value="250"/> -->
|
||||
|
||||
<!-- integer of cpus, or 'auto', or 'cpu/<divisor>/<max> -->
|
||||
<param name="dec-threads" value="1"/>
|
||||
<param name="enc-threads" value="cpu/2/4"/>
|
||||
</settings>
|
||||
|
||||
<profiles>
|
||||
<profile name="H263">
|
||||
</profile>
|
||||
|
||||
<profile name="H263+">
|
||||
</profile>
|
||||
|
||||
<profile name="H264">
|
||||
<!-- <param name="dec-threads" value="1"/> -->
|
||||
<!-- <param name="enc-threads" value="cpu/2/4"/> -->
|
||||
|
||||
<!-- <param name="profile" value="baseline"/> -->
|
||||
<!-- <param name="level" value="41"/> -->
|
||||
<!-- <param name="timebase" value="1/90"/> -->
|
||||
|
||||
<!--
|
||||
|
||||
#define AV_CODEC_FLAG_UNALIGNED (1 << 0)
|
||||
#define AV_CODEC_FLAG_QSCALE (1 << 1)
|
||||
#define AV_CODEC_FLAG_4MV (1 << 2)
|
||||
#define AV_CODEC_FLAG_OUTPUT_CORRUPT (1 << 3)
|
||||
#define AV_CODEC_FLAG_QPEL (1 << 4)
|
||||
#define AV_CODEC_FLAG_PASS1 (1 << 9)
|
||||
#define AV_CODEC_FLAG_PASS2 (1 << 10)
|
||||
#define AV_CODEC_FLAG_LOOP_FILTER (1 << 11)
|
||||
#define AV_CODEC_FLAG_GRAY (1 << 13)
|
||||
#define AV_CODEC_FLAG_PSNR (1 << 15)
|
||||
#define AV_CODEC_FLAG_TRUNCATED (1 << 16)
|
||||
#define AV_CODEC_FLAG_INTERLACED_DCT (1 << 18)
|
||||
#define AV_CODEC_FLAG_LOW_DELAY (1 << 19)
|
||||
#define AV_CODEC_FLAG_GLOBAL_HEADER (1 << 22)
|
||||
#define AV_CODEC_FLAG_BITEXACT (1 << 23)
|
||||
#define AV_CODEC_FLAG_AC_PRED (1 << 24)
|
||||
#define AV_CODEC_FLAG_INTERLACED_ME (1 << 29)
|
||||
#define AV_CODEC_FLAG_CLOSED_GOP (1U << 31)
|
||||
|
||||
-->
|
||||
|
||||
<param name="flags" value="LOOP_FILTER|PSNR"/>
|
||||
|
||||
<!--
|
||||
#define FF_CMP_SAD 0
|
||||
#define FF_CMP_SSE 1
|
||||
#define FF_CMP_SATD 2
|
||||
#define FF_CMP_DCT 3
|
||||
#define FF_CMP_PSNR 4
|
||||
#define FF_CMP_BIT 5
|
||||
#define FF_CMP_RD 6
|
||||
#define FF_CMP_ZERO 7
|
||||
#define FF_CMP_VSAD 8
|
||||
#define FF_CMP_VSSE 9
|
||||
#define FF_CMP_NSSE 10
|
||||
#define FF_CMP_W53 11
|
||||
#define FF_CMP_W97 12
|
||||
#define FF_CMP_DCTMAX 13
|
||||
#define FF_CMP_DCT264 14
|
||||
#define FF_CMP_MEDIAN_SAD 15
|
||||
#define FF_CMP_CHROMA 256
|
||||
-->
|
||||
|
||||
<!-- <param name="me-cmp" value="1"/> -->
|
||||
<!-- <param name="me-range" value="16"/> -->
|
||||
<!-- <param name="max-b-frames" value="3"/> -->
|
||||
<!-- <param name="refs" value="3"/> -->
|
||||
<!-- <param name="gop-size" value="250"/> -->
|
||||
<!-- <param name="keyint-min" value="25"/> -->
|
||||
<!-- <param name="i-quant-factor" value="0.71"/> -->
|
||||
<!-- <param name="b-quant-factor" value="0.76923078"/> -->
|
||||
<!-- <param name="qcompress" value="0.6"/> -->
|
||||
<!-- <param name="qmin" value="10"/> -->
|
||||
<!-- <param name="qmax" value="51"/> -->
|
||||
<!-- <param name="max-qdiff" value="4"/> -->
|
||||
|
||||
<!--
|
||||
enum AVColorSpace {
|
||||
AVCOL_SPC_RGB = 0, ///< order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB)
|
||||
AVCOL_SPC_BT709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 xvYCC709 / SMPTE RP177 Annex B
|
||||
AVCOL_SPC_UNSPECIFIED = 2,
|
||||
AVCOL_SPC_RESERVED = 3,
|
||||
AVCOL_SPC_FCC = 4, ///< FCC Title 47 Code of Federal Regulations 73.682 (a)(20)
|
||||
AVCOL_SPC_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM / IEC 61966-2-4 xvYCC601
|
||||
AVCOL_SPC_SMPTE170M = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC
|
||||
AVCOL_SPC_SMPTE240M = 7, ///< functionally identical to above
|
||||
AVCOL_SPC_YCGCO = 8, ///< Used by Dirac / VC-2 and H.264 FRext, see ITU-T SG16
|
||||
AVCOL_SPC_YCOCG = AVCOL_SPC_YCGCO,
|
||||
AVCOL_SPC_BT2020_NCL = 9, ///< ITU-R BT2020 non-constant luminance system
|
||||
AVCOL_SPC_BT2020_CL = 10, ///< ITU-R BT2020 constant luminance system
|
||||
AVCOL_SPC_SMPTE2085 = 11, ///< SMPTE 2085, Y'D'zD'x
|
||||
AVCOL_SPC_CHROMA_DERIVED_NCL = 12, ///< Chromaticity-derived non-constant luminance system
|
||||
AVCOL_SPC_CHROMA_DERIVED_CL = 13, ///< Chromaticity-derived constant luminance system
|
||||
AVCOL_SPC_ICTCP = 14, ///< ITU-R BT.2100-0, ICtCp
|
||||
AVCOL_SPC_NB ///< Not part of ABI
|
||||
};
|
||||
-->
|
||||
<param name="colorspace" value="0"/>
|
||||
|
||||
<!--
|
||||
enum AVColorRange {
|
||||
AVCOL_RANGE_UNSPECIFIED = 0,
|
||||
AVCOL_RANGE_MPEG = 1, ///< the normal 219*2^(n-8) "MPEG" YUV ranges
|
||||
AVCOL_RANGE_JPEG = 2, ///< the normal 2^n-1 "JPEG" YUV ranges
|
||||
AVCOL_RANGE_NB ///< Not part of ABI
|
||||
};
|
||||
-->
|
||||
<param name="color-range" value="2"/>
|
||||
|
||||
<!-- x264 private options-->
|
||||
<options>
|
||||
<option name="preset" value="veryfast"/>
|
||||
<option name="intra_refresh" value="1"/>
|
||||
<option name="tune" value="animation+zerolatency"/>
|
||||
<option name="sc_threshold" value="40"/>
|
||||
<option name="b_strategy" value="1"/>
|
||||
<option name="crf" value="18"/>
|
||||
</options>
|
||||
</profile>
|
||||
|
||||
<profile name="H265">
|
||||
</profile>
|
||||
|
||||
<profile name="conference">
|
||||
<param name="dec-threads" value="1"/>
|
||||
<param name="enc-threads" value="cpu/2/4"/>
|
||||
|
||||
<codecs>
|
||||
<!-- profiles will be parsed at runtime
|
||||
to overwrite this profile params if codec matches -->
|
||||
<codec name="H263" profile="H263"/>
|
||||
<codec name="H264" profile="H264"/>
|
||||
<codec name="H264" profile="conference-H264"/>
|
||||
</codecs>
|
||||
</profile>
|
||||
|
||||
<profile name="conference-H264">
|
||||
<options>
|
||||
<option name="preset" value="veryfast"/>
|
||||
<option name="intra_refresh" value="1"/>
|
||||
<option name="tune" value="animation+zerolatency"/>
|
||||
<option name="sc_threshold" value="40"/>
|
||||
<option name="b_strategy" value="1"/>
|
||||
<option name="crf" value="10"/>
|
||||
</options>
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
</configuration>
|
||||
|
||||
<configuration name="avformat.conf" description="AVFormat Config">
|
||||
<settings>
|
||||
<param name="colorspace" value="1"/>
|
||||
</settings>
|
||||
</configuration>
|
@ -1,74 +0,0 @@
|
||||
<configuration name="avmd.conf" description="AVMD config">
|
||||
<settings>
|
||||
|
||||
<!-- Edit these settings to change default behaviour
|
||||
of each avmd session. Settings can be overwritten
|
||||
by values passed dynamically per each session -->
|
||||
|
||||
|
||||
<!-- Global settings -->
|
||||
|
||||
<!-- define/undefine this to enable/disable logging of avmd
|
||||
intermediate computations to log -->
|
||||
<param name="debug" value="0"/>
|
||||
|
||||
<!-- define/undef this to enable/disable verbose logging (and reporting to the console)
|
||||
of detection status and other diagnostics like parameters avmd session has been started with,
|
||||
change of configuration parameters, beep detection status after session ended
|
||||
(stop event is fired independently of this setting and beep status included there) -->
|
||||
<param name="report_status" value="1"/>
|
||||
|
||||
<!-- define/undefine this to enable/disable faster computation
|
||||
of arcus cosine - table will be created mapping floats
|
||||
to integers and returning arc cos values given these integer
|
||||
indices into table -->
|
||||
<param name="fast_math" value="0"/>
|
||||
<!-- Global settings end -->
|
||||
|
||||
|
||||
<!-- Per call (session) settings. These settings can be overwritten
|
||||
with custom/different values per each avmd session -->
|
||||
|
||||
<!-- define/undefine this to classify avmd beep detection as valid
|
||||
only when there is required number of consecutive elements
|
||||
in the SMA buffer without reset -->
|
||||
<param name="require_continuous_streak" value="1"/>
|
||||
|
||||
<!-- required number of consecutive elements in the SMA buffer
|
||||
without reset. This parameter helps to avoid false beeps, bigger this value is
|
||||
smaller the probability of getting false detection -->
|
||||
<param name="sample_n_continuous_streak" value="3"/>
|
||||
|
||||
<!-- define number of samples to skip starting from the beginning
|
||||
of the frame and/or after reset has happened. This serves the purpose of skipping first few
|
||||
estimations on each frame, as these estimations may be inaccurate. This parameter also helps
|
||||
to give more robust detections when it's value is increased (up to scertain limit of about 60). -->
|
||||
<param name="sample_n_to_skip" value="0"/>
|
||||
|
||||
<param name="require_continuous_streak_amp" value="1"/>
|
||||
<param name="sample_n_continuous_streak_amp" value="3"/>
|
||||
|
||||
<!-- define/undefine this to enable/disable simplified estimation
|
||||
of frequency based on approximation of sin(x) with (x)
|
||||
in the range x=[0,PI/2] -->
|
||||
<param name="simplified_estimation" value="1"/>
|
||||
|
||||
<!-- define/undefine to enable/disable avmd on internal channel -->
|
||||
<param name="inbound_channel" value="0"/>
|
||||
|
||||
<!-- define/undefine to enable/disable avmd on external channel -->
|
||||
<param name="outbound_channel" value="1"/>
|
||||
|
||||
<!-- determines the mode of detection, default is both amplitude and frequency -->
|
||||
<param name="detection_mode" value="2"/>
|
||||
|
||||
<!-- number of detection threads running per each avmd session -->
|
||||
<param name="detectors_n" value="36"/>
|
||||
|
||||
<!-- number of lagged detection threads running per each avmd session -->
|
||||
<param name="detectors_lagged_n" value="1"/>
|
||||
|
||||
<!-- Per call settings end -->
|
||||
</settings>
|
||||
</configuration>
|
||||
|
@ -1,11 +0,0 @@
|
||||
<configuration name="mod_blacklist.conf" description="Blacklist module">
|
||||
<lists>
|
||||
<!--
|
||||
Example blacklist, the referenced file contains blacklisted items, one entry per line
|
||||
|
||||
NOTE: make sure the file exists and is readable by FreeSWITCH.
|
||||
|
||||
<list name="example" filename="$${conf_dir}/blacklists/example.list"/>
|
||||
-->
|
||||
</lists>
|
||||
</configuration>
|
@ -1,39 +0,0 @@
|
||||
<configuration name="callcenter.conf" description="CallCenter">
|
||||
<settings>
|
||||
<!--<param name="odbc-dsn" value="dsn:user:pass"/>-->
|
||||
<!--<param name="dbname" value="/dev/shm/callcenter.db"/>-->
|
||||
<!--<param name="cc-instance-id" value="single_box"/>-->
|
||||
</settings>
|
||||
|
||||
<queues>
|
||||
|
||||
<queue name="support@default">
|
||||
<param name="strategy" value="longest-idle-agent"/>
|
||||
<param name="moh-sound" value="$${hold_music}"/>
|
||||
<!--<param name="record-template" value="$${recordings_dir}/${strftime(%Y-%m-%d-%H-%M-%S)}.${destination_number}.${caller_id_number}.${uuid}.wav"/>-->
|
||||
<param name="time-base-score" value="system"/>
|
||||
<param name="max-wait-time" value="0"/>
|
||||
<param name="max-wait-time-with-no-agent" value="0"/>
|
||||
<param name="max-wait-time-with-no-agent-time-reached" value="5"/>
|
||||
<param name="tier-rules-apply" value="false"/>
|
||||
<param name="tier-rule-wait-second" value="300"/>
|
||||
<param name="tier-rule-wait-multiply-level" value="true"/>
|
||||
<param name="tier-rule-no-agent-no-wait" value="false"/>
|
||||
<param name="discard-abandoned-after" value="60"/>
|
||||
<param name="abandoned-resume-allowed" value="false"/>
|
||||
</queue>
|
||||
|
||||
</queues>
|
||||
|
||||
<!-- WARNING: Configuration of XML Agents will be updated into the DB upon restart. -->
|
||||
<!-- WARNING: Configuration of XML Tiers will reset the level and position if those were supplied. -->
|
||||
<!-- WARNING: Agents and Tiers XML config shouldn't be used in a multi FS shared DB setup (Not currently supported anyway) -->
|
||||
<agents>
|
||||
<!--<agent name="1000@default" type="callback" contact="[leg_timeout=10]user/1000@default" status="Available" max-no-answer="3" wrap-up-time="10" reject-delay-time="10" busy-delay-time="60" />-->
|
||||
</agents>
|
||||
<tiers>
|
||||
<!-- If no level or position is provided, they will default to 1. You should do this to keep db value on restart. -->
|
||||
<!-- <tier agent="1000@default" queue="support@default" level="1" position="1"/> -->
|
||||
</tiers>
|
||||
|
||||
</configuration>
|
@ -1,23 +0,0 @@
|
||||
<configuration name="cdr_csv.conf" description="CDR CSV Format">
|
||||
<settings>
|
||||
<!-- 'cdr-csv' will always be appended to log-base -->
|
||||
<!--<param name="log-base" value="/var/log"/>-->
|
||||
<param name="default-template" value="example"/>
|
||||
<!-- This is like the info app but after the call is hung up -->
|
||||
<!--<param name="debug" value="true"/>-->
|
||||
<param name="rotate-on-hup" value="true"/>
|
||||
<!-- may be a b or ab -->
|
||||
<param name="legs" value="a"/>
|
||||
<!-- Only log in Master.csv -->
|
||||
<!-- <param name="master-file-only" value="true"/> -->
|
||||
</settings>
|
||||
<templates>
|
||||
<template name="sql">INSERT INTO cdr VALUES ("${caller_id_name}","${caller_id_number}","${destination_number}","${context}","${start_stamp}","${answer_stamp}","${end_stamp}","${duration}","${billsec}","${hangup_cause}","${uuid}","${bleg_uuid}", "${accountcode}");</template>
|
||||
<template name="example">"${caller_id_name}","${caller_id_number}","${destination_number}","${context}","${start_stamp}","${answer_stamp}","${end_stamp}","${duration}","${billsec}","${hangup_cause}","${uuid}","${bleg_uuid}","${accountcode}","${read_codec}","${write_codec}"</template>
|
||||
<template name="snom">"${caller_id_name}","${caller_id_number}","${destination_number}","${context}","${start_stamp}","${answer_stamp}","${end_stamp}","${duration}","${billsec}","${hangup_cause}","${uuid}","${bleg_uuid}", "${accountcode}","${read_codec}","${write_codec}","${sip_user_agent}","${call_clientcode}","${sip_rtp_rxstat}","${sip_rtp_txstat}","${sofia_record_file}"</template>
|
||||
<template name="linksys">"${caller_id_name}","${caller_id_number}","${destination_number}","${context}","${start_stamp}","${answer_stamp}","${end_stamp}","${duration}","${billsec}","${hangup_cause}","${uuid}","${bleg_uuid}","${accountcode}","${read_codec}","${write_codec}","${sip_user_agent}","${sip_p_rtp_stat}"</template>
|
||||
<template name="asterisk">"${accountcode}","${caller_id_number}","${destination_number}","${context}","${caller_id}","${channel_name}","${bridge_channel}","${last_app}","${last_arg}","${start_stamp}","${answer_stamp}","${end_stamp}","${duration}","${billsec}","${hangup_cause}","${amaflags}","${uuid}","${userfield}"</template>
|
||||
<template name="opencdrrate">"${uuid}","${signal_bond}","${direction}","${ani}","${destination_number}","${answer_stamp}","${end_stamp}","${billsec}","${accountcode}","${userfield}","${network_addr}","${regex('${original_caller_id_name}'|^.)}","${sip_gateway_name}"</template>
|
||||
</templates>
|
||||
</configuration>
|
||||
|
@ -1,13 +0,0 @@
|
||||
<configuration name="cdr_mongodb.conf" description="MongoDB CDR logger">
|
||||
<settings>
|
||||
<!-- Hostnames and IPv6 addrs not supported (yet) -->
|
||||
<param name="host" value="127.0.0.1"/>
|
||||
<param name="port" value="27017"/>
|
||||
|
||||
<!-- Namespace format is database.collection -->
|
||||
<param name="namespace" value="test.cdr"/>
|
||||
|
||||
<!-- If true, create CDR for B-leg of call (default: true) -->
|
||||
<param name="log-b-leg" value="false"/>
|
||||
</settings>
|
||||
</configuration>
|
@ -1,40 +0,0 @@
|
||||
<configuration name="cdr_pg_csv.conf" description="CDR PG CSV Format">
|
||||
<settings>
|
||||
<!-- See parameters for PQconnectdb() at http://www.postgresql.org/docs/8.4/static/libpq-connect.html -->
|
||||
<param name="db-info" value="host=localhost dbname=cdr connect_timeout=10" />
|
||||
<!-- CDR table name -->
|
||||
<!--<param name="db-table" value="cdr"/>-->
|
||||
|
||||
<!-- Log a-leg (a), b-leg (b) or both (ab) -->
|
||||
<param name="legs" value="a"/>
|
||||
|
||||
<!-- Directory in which to spool failed SQL inserts -->
|
||||
<!-- <param name="spool-dir" value="$${log_dir}/cdr-pg-csv"/> -->
|
||||
<!-- Disk spool format if DB connection/insert fails - csv (default) or sql -->
|
||||
<param name="spool-format" value="csv"/>
|
||||
<param name="rotate-on-hup" value="true"/>
|
||||
|
||||
<!-- This is like the info app but after the call is hung up -->
|
||||
<!--<param name="debug" value="true"/>-->
|
||||
</settings>
|
||||
<schema>
|
||||
<field var="local_ip_v4"/>
|
||||
<field var="caller_id_name"/>
|
||||
<field var="caller_id_number"/>
|
||||
<field var="destination_number"/>
|
||||
<field var="context"/>
|
||||
<field var="start_stamp"/>
|
||||
<field var="answer_stamp"/>
|
||||
<field var="end_stamp"/>
|
||||
<field var="duration" quote="false"/>
|
||||
<field var="billsec" quote="false"/>
|
||||
<field var="hangup_cause"/>
|
||||
<field var="uuid"/>
|
||||
<field var="bleg_uuid"/>
|
||||
<field var="accountcode"/>
|
||||
<field var="read_codec"/>
|
||||
<field var="write_codec"/>
|
||||
<!-- <field var="sip_hangup_disposition"/> -->
|
||||
<!-- <field var="ani"/> -->
|
||||
</schema>
|
||||
</configuration>
|
@ -1,18 +0,0 @@
|
||||
<configuration name="cdr_sqlite.conf" description="SQLite CDR">
|
||||
<settings>
|
||||
<!-- SQLite database name (.db suffix will be automatically appended) -->
|
||||
<!-- <param name="db-name" value="cdr"/> -->
|
||||
<!-- CDR table name -->
|
||||
<!-- <param name="db-table" value="cdr"/> -->
|
||||
<!-- Log a-leg (a), b-leg (b) or both (ab) -->
|
||||
<param name="legs" value="a"/>
|
||||
<!-- Default template to use when inserting records -->
|
||||
<param name="default-template" value="example"/>
|
||||
<!-- This is like the info app but after the call is hung up -->
|
||||
<!--<param name="debug" value="true"/>-->
|
||||
</settings>
|
||||
<templates>
|
||||
<!-- Note that field order must match SQL table schema, otherwise insert will fail -->
|
||||
<template name="example">"${caller_id_name}","${caller_id_number}","${destination_number}","${context}","${start_stamp}","${answer_stamp}","${end_stamp}",${duration},${billsec},"${hangup_cause}","${uuid}","${bleg_uuid}","${accountcode}"</template>
|
||||
</templates>
|
||||
</configuration>
|
@ -1,12 +0,0 @@
|
||||
<configuration name="cepstral.conf" description="Cepstral TTS configuration">
|
||||
<settings>
|
||||
<!--
|
||||
Possible encodings:
|
||||
* utf-8
|
||||
* us-ascii
|
||||
* iso8859-1 (default)
|
||||
* iso8859-15
|
||||
-->
|
||||
<param name="encoding" value="utf-8"/>
|
||||
</settings>
|
||||
</configuration>
|
@ -1,33 +0,0 @@
|
||||
<configuration name="cidlookup.conf" description="cidlookup Configuration">
|
||||
<settings>
|
||||
<!-- comment out url to not setup a url based lookup -->
|
||||
<param name="url" value="http://query.voipcnam.com/query.php?api_key=MYAPIKEY&number=${caller_id_number}"/>
|
||||
|
||||
<!-- comment out whitepages-apikey to not use whitepages.com, you must
|
||||
get an API key from http://developer.whitepages.com/ -->
|
||||
<param name="whitepages-apikey" value="MYAPIKEY"/>
|
||||
|
||||
<!-- set to false to not cache (in memcache) results from the url query -->
|
||||
<param name="cache" value="true"/>
|
||||
<!-- expire is in seconds -->
|
||||
<param name="cache-expire" value="86400"/>
|
||||
|
||||
<param name="odbc-dsn" value="phone:phone:phone"/>
|
||||
|
||||
<!-- comment out sql to not setup a database (directory) lookup -->
|
||||
<param name="sql" value="
|
||||
SELECT name||' ('||type||')' AS name
|
||||
FROM phonebook p JOIN numbers n ON p.id = n.phonebook_id
|
||||
WHERE n.number='${caller_id_number}'
|
||||
LIMIT 1
|
||||
"/>
|
||||
<!-- comment out citystate-sql to not setup a database (city/state)
|
||||
lookup -->
|
||||
<param name="citystate-sql" value="
|
||||
SELECT ratecenter||' '||state as name
|
||||
FROM npa_nxx_company_ocn
|
||||
WHERE npa = ${caller_id_number:1:3} AND nxx = ${caller_id_number:4:3}
|
||||
LIMIT 1
|
||||
"/>
|
||||
</settings>
|
||||
</configuration>
|
@ -216,6 +216,7 @@
|
||||
<param name="caller-id-number" value="$${outbound_caller_id}"/>
|
||||
<!-- param name="comfort-noise" value="true"/ -->
|
||||
<param name="comfort-noise" value="1400"/>
|
||||
<param name="video-auto-floor-msec" value="2000"/>
|
||||
|
||||
<!-- <param name="conference-flags" value="video-floor-only|rfc-4579|livearray-sync|auto-3d-position|minimize-video-encoding"/> -->
|
||||
|
||||
|
@ -1,393 +0,0 @@
|
||||
<configuration name="conference_layouts.conf" description="Audio Conference">
|
||||
<layout-settings>
|
||||
<layouts>
|
||||
<layout name="1x1">
|
||||
<image x="0" y="0" scale="360" floor="true"/>
|
||||
</layout>
|
||||
<layout name="1x2" auto-3d-position="true">
|
||||
<image x="90" y="0" scale="180"/>
|
||||
<image x="90" y="180" scale="180"/>
|
||||
</layout>
|
||||
<layout name="2x1" auto-3d-position="true">
|
||||
<image x="0" y="90" scale="180"/>
|
||||
<image x="180" y="90" scale="180"/>
|
||||
</layout>
|
||||
|
||||
<layout name="2x1-zoom" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="180" hscale="360" zoom="true"/>
|
||||
<image x="180" y="0" scale="180" hscale="360" zoom="true"/>
|
||||
</layout>
|
||||
<layout name="3x1-zoom" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="120" hscale="360" zoom="true"/>
|
||||
<image x="120" y="0" scale="120" hscale="360" zoom="true"/>
|
||||
<image x="240" y="0" scale="120" hscale="360" zoom="true"/>
|
||||
</layout>
|
||||
<layout name="5-grid-zoom" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="180"/>
|
||||
<image x="180" y="0" scale="180"/>
|
||||
<image x="0" y="180" scale="120" hscale="180" zoom="true"/>
|
||||
<image x="120" y="180" scale="120" hscale="180" zoom="true"/>
|
||||
<image x="240" y="180" scale="120" hscale="180" zoom="true"/>
|
||||
</layout>
|
||||
<layout name="3x2-zoom" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="120" hscale="180" zoom="true"/>
|
||||
<image x="120" y="0" scale="120" hscale="180" zoom="true"/>
|
||||
<image x="240" y="0" scale="120" hscale="180" zoom="true"/>
|
||||
<image x="0" y="180" scale="120" hscale="180" zoom="true"/>
|
||||
<image x="120" y="180" scale="120" hscale="180" zoom="true"/>
|
||||
<image x="240" y="180" scale="120" hscale="180" zoom="true"/>
|
||||
</layout>
|
||||
<layout name="7-grid-zoom" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="120" hscale="180" zoom="true"/>
|
||||
<image x="120" y="0" scale="120" hscale="180" zoom="true"/>
|
||||
<image x="240" y="0" scale="120" hscale="180" zoom="true"/>
|
||||
<image x="0" y="180" scale="90" hscale="180" zoom="true"/>
|
||||
<image x="90" y="180" scale="90" hscale="180" zoom="true"/>
|
||||
<image x="180" y="180" scale="90" hscale="180" zoom="true"/>
|
||||
<image x="270" y="180" scale="90" hscale="180" zoom="true"/>
|
||||
</layout>
|
||||
<layout name="4x2-zoom" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="90" hscale="180" zoom="true"/>
|
||||
<image x="90" y="0" scale="90" hscale="180" zoom="true"/>
|
||||
<image x="180" y="0" scale="90" hscale="180" zoom="true"/>
|
||||
<image x="270" y="0" scale="90" hscale="180" zoom="true"/>
|
||||
<image x="0" y="180" scale="90" hscale="180" zoom="true"/>
|
||||
<image x="90" y="180" scale="90" hscale="180" zoom="true"/>
|
||||
<image x="180" y="180" scale="90" hscale="180" zoom="true"/>
|
||||
<image x="270" y="180" scale="90" hscale="180" zoom="true"/>
|
||||
</layout>
|
||||
<layout name="1x1+2x1" auto-3d-position="true">
|
||||
<image x="90" y="0" scale="180"/>
|
||||
<image x="0" y="180" scale="180"/>
|
||||
<image x="180" y="180" scale="180"/>
|
||||
</layout>
|
||||
<layout name="2x2" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="180"/>
|
||||
<image x="180" y="0" scale="180"/>
|
||||
<image x="0" y="180" scale="180"/>
|
||||
<image x="180" y="180" scale="180"/>
|
||||
</layout>
|
||||
<layout name="3x3" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="120"/>
|
||||
<image x="120" y="0" scale="120"/>
|
||||
<image x="240" y="0" scale="120"/>
|
||||
<image x="0" y="120" scale="120"/>
|
||||
<image x="120" y="120" scale="120"/>
|
||||
<image x="240" y="120" scale="120"/>
|
||||
<image x="0" y="240" scale="120"/>
|
||||
<image x="120" y="240" scale="120"/>
|
||||
<image x="240" y="240" scale="120"/>
|
||||
</layout>
|
||||
<layout name="4x4" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="90"/>
|
||||
<image x="90" y="0" scale="90"/>
|
||||
<image x="180" y="0" scale="90"/>
|
||||
<image x="270" y="0" scale="90"/>
|
||||
<image x="0" y="90" scale="90"/>
|
||||
<image x="90" y="90" scale="90"/>
|
||||
<image x="180" y="90" scale="90"/>
|
||||
<image x="270" y="90" scale="90"/>
|
||||
<image x="0" y="180" scale="90"/>
|
||||
<image x="90" y="180" scale="90"/>
|
||||
<image x="180" y="180" scale="90"/>
|
||||
<image x="270" y="180" scale="90"/>
|
||||
<image x="0" y="270" scale="90"/>
|
||||
<image x="90" y="270" scale="90"/>
|
||||
<image x="180" y="270" scale="90"/>
|
||||
<image x="270" y="270" scale="90"/>
|
||||
</layout>
|
||||
<layout name="5x5" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="72"/>
|
||||
<image x="72" y="0" scale="72"/>
|
||||
<image x="144" y="0" scale="72"/>
|
||||
<image x="216" y="0" scale="72"/>
|
||||
<image x="288" y="0" scale="72"/>
|
||||
<image x="0" y="72" scale="72"/>
|
||||
<image x="72" y="72" scale="72"/>
|
||||
<image x="144" y="72" scale="72"/>
|
||||
<image x="216" y="72" scale="72"/>
|
||||
<image x="288" y="72" scale="72"/>
|
||||
<image x="0" y="144" scale="72"/>
|
||||
<image x="72" y="144" scale="72"/>
|
||||
<image x="144" y="144" scale="72"/>
|
||||
<image x="216" y="144" scale="72"/>
|
||||
<image x="288" y="144" scale="72"/>
|
||||
<image x="0" y="216" scale="72"/>
|
||||
<image x="72" y="216" scale="72"/>
|
||||
<image x="144" y="216" scale="72"/>
|
||||
<image x="216" y="216" scale="72"/>
|
||||
<image x="288" y="216" scale="72"/>
|
||||
<image x="0" y="288" scale="72"/>
|
||||
<image x="72" y="288" scale="72"/>
|
||||
<image x="144" y="288" scale="72"/>
|
||||
<image x="216" y="288" scale="72"/>
|
||||
<image x="288" y="288" scale="72"/>
|
||||
</layout>
|
||||
<layout name="6x6" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="60"/>
|
||||
<image x="60" y="0" scale="60"/>
|
||||
<image x="120" y="0" scale="60"/>
|
||||
<image x="180" y="0" scale="60"/>
|
||||
<image x="240" y="0" scale="60"/>
|
||||
<image x="300" y="0" scale="60"/>
|
||||
<image x="0" y="60" scale="60"/>
|
||||
<image x="60" y="60" scale="60"/>
|
||||
<image x="120" y="60" scale="60"/>
|
||||
<image x="180" y="60" scale="60"/>
|
||||
<image x="240" y="60" scale="60"/>
|
||||
<image x="300" y="60" scale="60"/>
|
||||
<image x="0" y="120" scale="60"/>
|
||||
<image x="60" y="120" scale="60"/>
|
||||
<image x="120" y="120" scale="60"/>
|
||||
<image x="180" y="120" scale="60"/>
|
||||
<image x="240" y="120" scale="60"/>
|
||||
<image x="300" y="120" scale="60"/>
|
||||
<image x="0" y="180" scale="60"/>
|
||||
<image x="60" y="180" scale="60"/>
|
||||
<image x="120" y="180" scale="60"/>
|
||||
<image x="180" y="180" scale="60"/>
|
||||
<image x="240" y="180" scale="60"/>
|
||||
<image x="300" y="180" scale="60"/>
|
||||
<image x="0" y="240" scale="60"/>
|
||||
<image x="60" y="240" scale="60"/>
|
||||
<image x="120" y="240" scale="60"/>
|
||||
<image x="180" y="240" scale="60"/>
|
||||
<image x="240" y="240" scale="60"/>
|
||||
<image x="300" y="240" scale="60"/>
|
||||
<image x="0" y="300" scale="60"/>
|
||||
<image x="60" y="300" scale="60"/>
|
||||
<image x="120" y="300" scale="60"/>
|
||||
<image x="180" y="300" scale="60"/>
|
||||
<image x="240" y="300" scale="60"/>
|
||||
<image x="300" y="300" scale="60"/>
|
||||
</layout>
|
||||
<layout name="8x8" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="45"/>
|
||||
<image x="45" y="0" scale="45"/>
|
||||
<image x="90" y="0" scale="45"/>
|
||||
<image x="135" y="0" scale="45"/>
|
||||
<image x="180" y="0" scale="45"/>
|
||||
<image x="225" y="0" scale="45"/>
|
||||
<image x="270" y="0" scale="45"/>
|
||||
<image x="315" y="0" scale="45"/>
|
||||
<image x="0" y="45" scale="45"/>
|
||||
<image x="45" y="45" scale="45"/>
|
||||
<image x="90" y="45" scale="45"/>
|
||||
<image x="135" y="45" scale="45"/>
|
||||
<image x="180" y="45" scale="45"/>
|
||||
<image x="225" y="45" scale="45"/>
|
||||
<image x="270" y="45" scale="45"/>
|
||||
<image x="315" y="45" scale="45"/>
|
||||
<image x="0" y="90" scale="45"/>
|
||||
<image x="45" y="90" scale="45"/>
|
||||
<image x="90" y="90" scale="45"/>
|
||||
<image x="135" y="90" scale="45"/>
|
||||
<image x="180" y="90" scale="45"/>
|
||||
<image x="225" y="90" scale="45"/>
|
||||
<image x="270" y="90" scale="45"/>
|
||||
<image x="315" y="90" scale="45"/>
|
||||
<image x="0" y="135" scale="45"/>
|
||||
<image x="45" y="135" scale="45"/>
|
||||
<image x="90" y="135" scale="45"/>
|
||||
<image x="135" y="135" scale="45"/>
|
||||
<image x="180" y="135" scale="45"/>
|
||||
<image x="225" y="135" scale="45"/>
|
||||
<image x="270" y="135" scale="45"/>
|
||||
<image x="315" y="135" scale="45"/>
|
||||
<image x="0" y="180" scale="45"/>
|
||||
<image x="45" y="180" scale="45"/>
|
||||
<image x="90" y="180" scale="45"/>
|
||||
<image x="135" y="180" scale="45"/>
|
||||
<image x="180" y="180" scale="45"/>
|
||||
<image x="225" y="180" scale="45"/>
|
||||
<image x="270" y="180" scale="45"/>
|
||||
<image x="315" y="180" scale="45"/>
|
||||
<image x="0" y="225" scale="45"/>
|
||||
<image x="45" y="225" scale="45"/>
|
||||
<image x="90" y="225" scale="45"/>
|
||||
<image x="135" y="225" scale="45"/>
|
||||
<image x="180" y="225" scale="45"/>
|
||||
<image x="225" y="225" scale="45"/>
|
||||
<image x="270" y="225" scale="45"/>
|
||||
<image x="315" y="225" scale="45"/>
|
||||
<image x="0" y="270" scale="45"/>
|
||||
<image x="45" y="270" scale="45"/>
|
||||
<image x="90" y="270" scale="45"/>
|
||||
<image x="135" y="270" scale="45"/>
|
||||
<image x="180" y="270" scale="45"/>
|
||||
<image x="225" y="270" scale="45"/>
|
||||
<image x="270" y="270" scale="45"/>
|
||||
<image x="315" y="270" scale="45"/>
|
||||
<image x="0" y="315" scale="45"/>
|
||||
<image x="45" y="315" scale="45"/>
|
||||
<image x="90" y="315" scale="45"/>
|
||||
<image x="135" y="315" scale="45"/>
|
||||
<image x="180" y="315" scale="45"/>
|
||||
<image x="225" y="315" scale="45"/>
|
||||
<image x="270" y="315" scale="45"/>
|
||||
<image x="315" y="315" scale="45"/>
|
||||
</layout>
|
||||
<layout name="1up_top_left+5" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="240" floor="true"/>
|
||||
<image x="240" y="0" scale="120"/>
|
||||
<image x="240" y="120" scale="120"/>
|
||||
<image x="0" y="240" scale="120"/>
|
||||
<image x="120" y="240" scale="120"/>
|
||||
<image x="240" y="240" scale="120"/>
|
||||
</layout>
|
||||
<layout name="1up_top_left+7" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="270" floor="true"/>
|
||||
<image x="270" y="0" scale="90"/>
|
||||
<image x="270" y="90" scale="90"/>
|
||||
<image x="270" y="180" scale="90"/>
|
||||
<image x="0" y="270" scale="90"/>
|
||||
<image x="90" y="270" scale="90"/>
|
||||
<image x="180" y="270" scale="90"/>
|
||||
<image x="270" y="270" scale="90"/>
|
||||
</layout>
|
||||
<layout name="1up_top_left+9" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="288" floor="true"/>
|
||||
<image x="288" y="0" scale="72"/>
|
||||
<image x="288" y="72" scale="72"/>
|
||||
<image x="288" y="144" scale="72"/>
|
||||
<image x="288" y="216" scale="72"/>
|
||||
<image x="0" y="288" scale="72"/>
|
||||
<image x="72" y="288" scale="72"/>
|
||||
<image x="144" y="288" scale="72"/>
|
||||
<image x="216" y="288" scale="72"/>
|
||||
<image x="288" y="288" scale="72"/>
|
||||
</layout>
|
||||
<layout name="2up_top+8" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="180" floor="true"/>
|
||||
<image x="180" y="0" scale="180" reservation_id="secondary"/>
|
||||
<image x="0" y="180" scale="90"/>
|
||||
<image x="90" y="180" scale="90"/>
|
||||
<image x="180" y="180" scale="90"/>
|
||||
<image x="270" y="180" scale="90"/>
|
||||
<image x="0" y="270" scale="90"/>
|
||||
<image x="90" y="270" scale="90"/>
|
||||
<image x="180" y="270" scale="90"/>
|
||||
<image x="270" y="270" scale="90"/>
|
||||
</layout>
|
||||
<layout name="2up_middle+8" auto-3d-position="true">
|
||||
<image x="0" y="90" scale="180" floor="true"/>
|
||||
<image x="180" y="90" scale="180" reservation_id="secondary"/>
|
||||
<image x="0" y="0" scale="90"/>
|
||||
<image x="90" y="0" scale="90"/>
|
||||
<image x="180" y="0" scale="90"/>
|
||||
<image x="270" y="0" scale="90"/>
|
||||
<image x="0" y="270" scale="90"/>
|
||||
<image x="90" y="270" scale="90"/>
|
||||
<image x="180" y="270" scale="90"/>
|
||||
<image x="270" y="270" scale="90"/>
|
||||
</layout>
|
||||
<layout name="2up_bottom+8" auto-3d-position="true">
|
||||
<image x="0" y="180" scale="180" floor="true"/>
|
||||
<image x="180" y="180" scale="180" reservation_id="secondary"/>
|
||||
<image x="0" y="0" scale="90"/>
|
||||
<image x="90" y="0" scale="90"/>
|
||||
<image x="180" y="0" scale="90"/>
|
||||
<image x="270" y="0" scale="90"/>
|
||||
<image x="0" y="90" scale="90"/>
|
||||
<image x="90" y="90" scale="90"/>
|
||||
<image x="180" y="90" scale="90"/>
|
||||
<image x="270" y="90" scale="90"/>
|
||||
</layout>
|
||||
<layout name="3up+4" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="180" floor="true"/>
|
||||
<image x="180" y="0" scale="180" reservation_id="secondary"/>
|
||||
<image x="0" y="180" scale="180" reservation_id="third"/>
|
||||
<image x="180" y="180" scale="90"/>
|
||||
<image x="270" y="180" scale="90"/>
|
||||
<image x="180" y="270" scale="90"/>
|
||||
<image x="270" y="270" scale="90"/>
|
||||
</layout>
|
||||
<layout name="3up+9" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="180" floor="true"/>
|
||||
<image x="180" y="0" scale="180" reservation_id="secondary"/>
|
||||
<image x="0" y="180" scale="180" reservation_id="third"/>
|
||||
<image x="180" y="180" scale="60"/>
|
||||
<image x="240" y="180" scale="60"/>
|
||||
<image x="300" y="180" scale="60"/>
|
||||
<image x="180" y="240" scale="60"/>
|
||||
<image x="240" y="240" scale="60"/>
|
||||
<image x="300" y="240" scale="60"/>
|
||||
<image x="180" y="300" scale="60"/>
|
||||
<image x="240" y="300" scale="60"/>
|
||||
<image x="300" y="300" scale="60"/>
|
||||
</layout>
|
||||
<layout name="2x1-presenter-zoom" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="180" hscale="360" zoom="true" floor="true"/>
|
||||
<image x="180" y="0" scale="180" hscale="360" zoom="true" reservation_id="presenter"/>
|
||||
</layout>
|
||||
<layout name="presenter-dual-vertical">
|
||||
<image x="90" y="0" scale="180" floor-only="true"/>
|
||||
<image x="90" y="180" scale="180" reservation_id="presenter"/>
|
||||
</layout>
|
||||
<layout name="presenter-dual-horizontal">
|
||||
<image x="0" y="90" scale="180" floor-only="true"/>
|
||||
<image x="180" y="90" scale="180" reservation_id="presenter"/>
|
||||
</layout>
|
||||
<layout name="presenter-overlap-small-top-right">
|
||||
<image x="0" y="0" scale="360" floor-only="true"/>
|
||||
<image x="300" y="0" scale="60" overlap="true" reservation_id="presenter"/>
|
||||
</layout>
|
||||
<layout name="presenter-overlap-small-bot-right">
|
||||
<image x="0" y="0" scale="360" floor-only="true"/>
|
||||
<image x="300" y="300" scale="60" overlap="true" reservation_id="presenter"/>
|
||||
</layout>
|
||||
<layout name="presenter-overlap-large-top-right">
|
||||
<image x="0" y="0" scale="360" floor-only="true"/>
|
||||
<image x="180" y="0" scale="180" overlap="true" reservation_id="presenter"/>
|
||||
</layout>
|
||||
<layout name="presenter-overlap-large-bot-right">
|
||||
<image x="0" y="0" scale="360" floor-only="true"/>
|
||||
<image x="180" y="180" scale="180" overlap="true" reservation_id="presenter"/>
|
||||
</layout>
|
||||
<layout name="overlaps" auto-3d-position="true">
|
||||
<image x="0" y="0" scale="360" floor-only="true"/>
|
||||
<image x="300" y="300" scale="60" overlap="true"/>
|
||||
<image x="240" y="300" scale="60" overlap="true"/>
|
||||
<image x="180" y="300" scale="60" overlap="true"/>
|
||||
<image x="120" y="300" scale="60" overlap="true"/>
|
||||
<image x="60" y="300" scale="60" overlap="true"/>
|
||||
<image x="0" y="300" scale="60" overlap="true"/>
|
||||
</layout>
|
||||
|
||||
|
||||
</layouts>
|
||||
<groups>
|
||||
<group name="grid">
|
||||
<layout>1x1</layout>
|
||||
<layout>2x1</layout>
|
||||
<layout>1x1+2x1</layout>
|
||||
<layout>2x2</layout>
|
||||
<layout>3x3</layout>
|
||||
<layout>4x4</layout>
|
||||
<layout>5x5</layout>
|
||||
<layout>6x6</layout>
|
||||
<layout>8x8</layout>
|
||||
</group>
|
||||
<group name="grid-zoom">
|
||||
<layout>1x1</layout>
|
||||
<layout>2x1-zoom</layout>
|
||||
<layout>3x1-zoom</layout>
|
||||
<layout>2x2</layout>
|
||||
<layout>5-grid-zoom</layout>
|
||||
<layout>3x2-zoom</layout>
|
||||
<layout>7-grid-zoom</layout>
|
||||
<layout>4x2-zoom</layout>
|
||||
<layout>3x3</layout>
|
||||
</group>
|
||||
<group name="1up_top_left_plus">
|
||||
<layout>1up_top_left+5</layout>
|
||||
<layout>1up_top_left+7</layout>
|
||||
<layout>1up_top_left+9</layout>
|
||||
</group>
|
||||
<group name="3up_plus">
|
||||
<layout>3up+4</layout>
|
||||
<layout>3up+9</layout>
|
||||
</group>
|
||||
</groups>
|
||||
</layout-settings>
|
||||
</configuration>
|
@ -1,5 +0,0 @@
|
||||
<configuration name="curl.conf" description="cURL module">
|
||||
<settings>
|
||||
<param name="max-bytes" value="64000"/>
|
||||
</settings>
|
||||
</configuration>
|
@ -1,5 +0,0 @@
|
||||
<configuration name="db.conf" description="LIMIT DB Configuration">
|
||||
<settings>
|
||||
<!--<param name="odbc-dsn" value="dsn:user:pass"/>-->
|
||||
</settings>
|
||||
</configuration>
|
@ -1,9 +0,0 @@
|
||||
<configuration name="dialplan_directory.conf" description="Dialplan Directory">
|
||||
<settings>
|
||||
<param name="directory-name" value="ldap"/>
|
||||
<param name="host" value="ldap.freeswitch.org"/>
|
||||
<param name="dn" value="cn=Manager,dc=freeswitch,dc=org"/>
|
||||
<param name="pass" value="test"/>
|
||||
<param name="base" value="dc=freeswitch,dc=org"/>
|
||||
</settings>
|
||||
</configuration>
|
@ -1,9 +0,0 @@
|
||||
<configuration name="dingaling.conf" description="XMPP Jingle Endpoint">
|
||||
<settings>
|
||||
<param name="debug" value="0"/>
|
||||
<param name="codec-prefs" value="H264,PCMU"/>
|
||||
</settings>
|
||||
|
||||
<X-PRE-PROCESS cmd="include" data="../jingle_profiles/*.xml"/>
|
||||
|
||||
</configuration>
|
@ -1,21 +0,0 @@
|
||||
<configuration name="directory.conf" description="Directory">
|
||||
<settings>
|
||||
<!--<param name="odbc-dsn" value="dsn:user:pass"/>-->
|
||||
<!--<param name="dbname" value="directory"/>-->
|
||||
</settings>
|
||||
<profiles>
|
||||
<profile name="default">
|
||||
<param name="max-menu-attempts" value="3"/>
|
||||
<param name="min-search-digits" value="3"/>
|
||||
<param name="terminator-key" value="#"/>
|
||||
<param name="digit-timeout" value="3000"/>
|
||||
<param name="max-result" value="5"/>
|
||||
<param name="next-key" value="6"/>
|
||||
<param name="prev-key" value="4"/>
|
||||
<param name="switch-order-key" value="*"/>
|
||||
<param name="select-name-key" value="1"/>
|
||||
<param name="new-search-key" value="3"/>
|
||||
<param name="search-order" value="last_name"/>
|
||||
</profile>
|
||||
</profiles>
|
||||
</configuration>
|
@ -1,10 +0,0 @@
|
||||
<configuration name="distributor.conf" description="Distributor Configuration">
|
||||
<lists>
|
||||
<!-- every 10 calls to test you will get foo1 once and foo2 9 times...yes NINE TIMES! -->
|
||||
<!-- this is not the same as 100 with 10 and 90 that would do foo1 10 times in a row then foo2 90 times in a row -->
|
||||
<list name="test">
|
||||
<node name="foo1" weight="1"/>
|
||||
<node name="foo2" weight="9"/>
|
||||
</list>
|
||||
</lists>
|
||||
</configuration>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user