diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingState2x.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingState2x.scala
index ecd2c9e3ec..c21a58d0e3 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingState2x.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingState2x.scala
@@ -32,4 +32,5 @@ object MeetingEndReason {
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"
+ val ENDED_DUE_TO_NO_MODERATOR = "ENDED_DUE_TO_NO_MODERATOR"
}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingTrackers.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingTrackers.scala
index 5b1c906870..01b82a8721 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingTrackers.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingTrackers.scala
@@ -3,14 +3,18 @@ package org.bigbluebutton.core.domain
case class MeetingExpiryTracker(
startedOnInMs: Long,
userHasJoined: Boolean,
+ moderatorHasJoined: Boolean,
isBreakout: Boolean,
lastUserLeftOnInMs: Option[Long],
+ lastModeratorLeftOnInMs: Long,
durationInMs: Long,
meetingExpireIfNoUserJoinedInMs: Long,
meetingExpireWhenLastUserLeftInMs: Long,
userInactivityInspectTimerInMs: Long,
userInactivityThresholdInMs: Long,
- userActivitySignResponseDelayInMs: Long
+ userActivitySignResponseDelayInMs: Long,
+ endWhenNoModerator: Boolean,
+ endWhenNoModeratorDelayInMs: Long
) {
def setUserHasJoined(): MeetingExpiryTracker = {
if (!userHasJoined) {
@@ -24,6 +28,18 @@ case class MeetingExpiryTracker(
copy(lastUserLeftOnInMs = Some(timestampInMs))
}
+ def setModeratorHasJoined(): MeetingExpiryTracker = {
+ if (!moderatorHasJoined) {
+ copy(moderatorHasJoined = true, lastModeratorLeftOnInMs = 0)
+ } else {
+ copy(lastModeratorLeftOnInMs = 0)
+ }
+ }
+
+ def setLastModeratorLeftOn(timestampInMs: Long): MeetingExpiryTracker = {
+ copy(lastModeratorLeftOnInMs = timestampInMs)
+ }
+
def hasMeetingExpiredAfterLastUserLeft(timestampInMs: Long): Boolean = {
val expire = for {
lastUserLeftOn <- lastUserLeftOnInMs
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala
index 79cae79e00..2c131fb1c7 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Users2x.scala
@@ -55,6 +55,10 @@ object Users2x {
users.toVector.length
}
+ def numActiveModerators(users: Users2x): Int = {
+ users.toVector.filter(u => u.role == Roles.MODERATOR_ROLE && !u.userLeftFlag.left).length
+ }
+
def findNotPresenters(users: Users2x): Vector[UserState] = {
users.toVector.filter(u => !u.presenter)
}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala
index a8e052e5e3..3a125c5e1f 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala
@@ -135,14 +135,18 @@ class MeetingActor(
val expiryTracker = new MeetingExpiryTracker(
startedOnInMs = TimeUtil.timeNowInMs(),
userHasJoined = false,
+ moderatorHasJoined = false,
isBreakout = props.meetingProp.isBreakout,
lastUserLeftOnInMs = None,
+ lastModeratorLeftOnInMs = 0,
durationInMs = TimeUtil.minutesToMillis(props.durationProps.duration),
meetingExpireIfNoUserJoinedInMs = TimeUtil.minutesToMillis(props.durationProps.meetingExpireIfNoUserJoinedInMinutes),
meetingExpireWhenLastUserLeftInMs = TimeUtil.minutesToMillis(props.durationProps.meetingExpireWhenLastUserLeftInMinutes),
userInactivityInspectTimerInMs = TimeUtil.minutesToMillis(props.durationProps.userInactivityInspectTimerInMinutes),
userInactivityThresholdInMs = TimeUtil.minutesToMillis(props.durationProps.userInactivityThresholdInMinutes),
- userActivitySignResponseDelayInMs = TimeUtil.minutesToMillis(props.durationProps.userActivitySignResponseDelayInMinutes)
+ userActivitySignResponseDelayInMs = TimeUtil.minutesToMillis(props.durationProps.userActivitySignResponseDelayInMinutes),
+ endWhenNoModerator = props.durationProps.endWhenNoModerator,
+ endWhenNoModeratorDelayInMs = TimeUtil.minutesToMillis(props.durationProps.endWhenNoModeratorDelayInMinutes)
)
val recordingTracker = new MeetingRecordingTracker(startedOnInMs = 0L, previousDurationInMs = 0L, currentDurationInMs = 0L)
@@ -298,6 +302,23 @@ class MeetingActor(
}
}
+ private def updateModeratorsPresence() {
+ if (Users2x.numActiveModerators(liveMeeting.users2x) > 0) {
+ if (state.expiryTracker.moderatorHasJoined == false ||
+ state.expiryTracker.lastModeratorLeftOnInMs != 0) {
+ log.info("A moderator has joined. Setting setModeratorHasJoined(). meetingId=" + props.meetingProp.intId)
+ val tracker = state.expiryTracker.setModeratorHasJoined()
+ state = state.update(tracker)
+ }
+ } else {
+ if (state.expiryTracker.moderatorHasJoined == true) {
+ log.info("All moderators have left. Setting setLastModeratorLeftOn(). meetingId=" + props.meetingProp.intId)
+ val tracker = state.expiryTracker.setLastModeratorLeftOn(TimeUtil.timeNowInMs())
+ state = state.update(tracker)
+ }
+ }
+ }
+
private def updateUserLastInactivityInspect(userId: String) {
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, userId)
@@ -317,20 +338,26 @@ class MeetingActor(
private def handleMessageThatAffectsInactivity(msg: BbbCommonEnvCoreMsg): Unit = {
msg.core match {
- case m: EndMeetingSysCmdMsg => handleEndMeeting(m, state)
+ case m: EndMeetingSysCmdMsg => handleEndMeeting(m, state)
// Users
- case m: ValidateAuthTokenReqMsg => state = usersApp.handleValidateAuthTokenReqMsg(m, state)
- case m: UserJoinMeetingReqMsg => state = handleUserJoinMeetingReqMsg(m, state)
- case m: UserJoinMeetingAfterReconnectReqMsg => state = handleUserJoinMeetingAfterReconnectReqMsg(m, state)
- case m: UserLeaveReqMsg => state = handleUserLeaveReqMsg(m, state)
- case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m)
- case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m)
- case m: GetCamBroadcastPermissionReqMsg => handleGetCamBroadcastPermissionReqMsg(m)
- case m: GetCamSubscribePermissionReqMsg => handleGetCamSubscribePermissionReqMsg(m)
+ case m: ValidateAuthTokenReqMsg => state = usersApp.handleValidateAuthTokenReqMsg(m, state)
+ case m: UserJoinMeetingReqMsg =>
+ state = handleUserJoinMeetingReqMsg(m, state)
+ updateModeratorsPresence()
+ case m: UserJoinMeetingAfterReconnectReqMsg =>
+ state = handleUserJoinMeetingAfterReconnectReqMsg(m, state)
+ updateModeratorsPresence()
+ case m: UserLeaveReqMsg =>
+ state = handleUserLeaveReqMsg(m, state)
+ updateModeratorsPresence()
+ case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m)
+ case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m)
+ case m: GetCamBroadcastPermissionReqMsg => handleGetCamBroadcastPermissionReqMsg(m)
+ case m: GetCamSubscribePermissionReqMsg => handleGetCamSubscribePermissionReqMsg(m)
- case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m)
- case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m, state)
+ case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m)
+ case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m, state)
case m: SetRecordingStatusCmdMsg =>
state = usersApp.handleSetRecordingStatusCmdMsg(m, state)
updateUserLastActivity(m.body.setBy)
@@ -354,6 +381,7 @@ class MeetingActor(
case m: ChangeUserRoleCmdMsg =>
usersApp.handleChangeUserRoleCmdMsg(m)
updateUserLastActivity(m.body.changedBy)
+ updateModeratorsPresence()
// Whiteboard
case m: SendCursorPositionPubMsg => wbApp.handle(m, liveMeeting, msgBus)
@@ -596,7 +624,8 @@ class MeetingActor(
processUserInactivityAudit()
flagRegisteredUsersWhoHasNotJoined()
- checkIfNeetToEndMeetingWhenNoAuthedUsers(liveMeeting)
+ checkIfNeedToEndMeetingWhenNoAuthedUsers(liveMeeting)
+ checkIfNeedToEndMeetingWhenNoNoModerators(liveMeeting)
}
def checkVoiceConfUsersStatus(): Unit = {
@@ -663,7 +692,7 @@ class MeetingActor(
}
- private def checkIfNeetToEndMeetingWhenNoAuthedUsers(liveMeeting: LiveMeeting): Unit = {
+ private def checkIfNeedToEndMeetingWhenNoAuthedUsers(liveMeeting: LiveMeeting): Unit = {
val authUserJoined = MeetingStatus2x.hasAuthedUserJoined(liveMeeting.status)
if (endMeetingWhenNoMoreAuthedUsers &&
@@ -684,6 +713,28 @@ class MeetingActor(
}
}
+ private def checkIfNeedToEndMeetingWhenNoNoModerators(liveMeeting: LiveMeeting): Unit = {
+ if (state.expiryTracker.endWhenNoModerator &&
+ !liveMeeting.props.meetingProp.isBreakout &&
+ state.expiryTracker.moderatorHasJoined &&
+ state.expiryTracker.lastModeratorLeftOnInMs != 0 &&
+ //Check if has moderator with leftFlag
+ Users2x.findModerator(liveMeeting.users2x).toVector.length == 0) {
+ val hasModeratorLeftRecently = (TimeUtil.timeNowInMs() - state.expiryTracker.endWhenNoModeratorDelayInMs) < state.expiryTracker.lastModeratorLeftOnInMs
+ if (!hasModeratorLeftRecently) {
+ log.info("Meeting will end due option endWhenNoModerator is enabled and all moderators have left the meeting. meetingId=" + props.meetingProp.intId)
+ sendEndMeetingDueToExpiry(
+ MeetingEndReason.ENDED_DUE_TO_NO_MODERATOR,
+ eventBus, outGW, liveMeeting,
+ "system"
+ )
+ } else {
+ val msToEndMeeting = state.expiryTracker.lastModeratorLeftOnInMs - (TimeUtil.timeNowInMs() - state.expiryTracker.endWhenNoModeratorDelayInMs)
+ log.info("All moderators have left. Meeting will end in " + TimeUtil.millisToSeconds(msToEndMeeting) + " seconds. meetingId=" + props.meetingProp.intId)
+ }
+ }
+ }
+
def handleExtendMeetingDuration(msg: ExtendMeetingDuration) {
}
diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/domain/Meeting2x.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/domain/Meeting2x.scala
index 67f842e615..cbc449393e 100755
--- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/domain/Meeting2x.scala
+++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/domain/Meeting2x.scala
@@ -4,7 +4,9 @@ case class ConfigProps(defaultConfigToken: String, config: String)
case class DurationProps(duration: Int, createdTime: Long, createdDate: String,
meetingExpireIfNoUserJoinedInMinutes: Int, meetingExpireWhenLastUserLeftInMinutes: Int,
- userInactivityInspectTimerInMinutes: Int, userInactivityThresholdInMinutes: Int, userActivitySignResponseDelayInMinutes: Int)
+ userInactivityInspectTimerInMinutes: Int, userInactivityThresholdInMinutes: Int,
+ userActivitySignResponseDelayInMinutes: Int,
+ endWhenNoModerator: Boolean, endWhenNoModeratorDelayInMinutes: Int)
case class MeetingProp(name: String, extId: String, intId: String, isBreakout: Boolean)
diff --git a/bbb-common-message/src/test/scala/org/bigbluebutton/common2/TestFixtures.scala b/bbb-common-message/src/test/scala/org/bigbluebutton/common2/TestFixtures.scala
index 127f0e6cb6..0ddf252dcf 100755
--- a/bbb-common-message/src/test/scala/org/bigbluebutton/common2/TestFixtures.scala
+++ b/bbb-common-message/src/test/scala/org/bigbluebutton/common2/TestFixtures.scala
@@ -17,6 +17,8 @@ trait TestFixtures {
val userInactivityInspectTimerInMinutes = 60
val userInactivityThresholdInMinutes = 10
val userActivitySignResponseDelayInMinutes = 5
+ val endWhenNoModerator = false
+ val endWhenNoModeratorDelayInMinutes = 1
val autoStartRecording = false
val allowStartStopRecording = false
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java
index 3adb52b1ad..75ac2b6a0d 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java
@@ -432,7 +432,8 @@ public class MeetingService implements MessageListener {
m.getDialNumber(), m.getMaxUsers(),
m.getMeetingExpireIfNoUserJoinedInMinutes(), m.getmeetingExpireWhenLastUserLeftInMinutes(),
m.getUserInactivityInspectTimerInMinutes(), m.getUserInactivityThresholdInMinutes(),
- m.getUserActivitySignResponseDelayInMinutes(), m.getMuteOnStart(), m.getAllowModsToUnmuteUsers(), m.getMeetingKeepEvents(),
+ m.getUserActivitySignResponseDelayInMinutes(), m.getEndWhenNoModerator(), m.getEndWhenNoModeratorDelayInMinutes(),
+ m.getMuteOnStart(), m.getAllowModsToUnmuteUsers(), m.getMeetingKeepEvents(),
m.breakoutRoomsParams,
m.lockSettingsParams, m.getHtml5InstanceId());
}
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java
index 741d4099e0..a114cd9ee8 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/ParamsProcessorUtil.java
@@ -115,6 +115,7 @@ public class ParamsProcessorUtil {
private Integer userActivitySignResponseDelayInMinutes = 5;
private Boolean defaultAllowDuplicateExtUserid = true;
private Boolean defaultEndWhenNoModerator = false;
+ private Integer defaultEndWhenNoModeratorDelayInMinutes = 1;
private Integer defaultHtml5InstanceId = 1;
private String formatConfNum(String s) {
@@ -523,6 +524,8 @@ public class ParamsProcessorUtil {
meeting.setUserActivitySignResponseDelayInMinutes(userActivitySignResponseDelayInMinutes);
meeting.setUserInactivityThresholdInMinutes(userInactivityThresholdInMinutes);
// meeting.setHtml5InstanceId(html5InstanceId);
+ meeting.setEndWhenNoModerator(defaultEndWhenNoModerator);
+ meeting.setEndWhenNoModeratorDelayInMinutes(defaultEndWhenNoModeratorDelayInMinutes);
// Add extra parameters for breakout room
if (isBreakout) {
@@ -1156,5 +1159,8 @@ public class ParamsProcessorUtil {
this.defaultEndWhenNoModerator = val;
}
+ public void setEndWhenNoModeratorDelayInMinutes(Integer value) {
+ this.defaultEndWhenNoModeratorDelayInMinutes = value;
+ }
}
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java
index b17b07c7fb..b2eaefea9a 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java
@@ -91,6 +91,8 @@ public class Meeting {
private Integer userInactivityInspectTimerInMinutes = 120;
private Integer userInactivityThresholdInMinutes = 30;
private Integer userActivitySignResponseDelayInMinutes = 5;
+ private Boolean endWhenNoModerator = false;
+ private Integer endWhenNoModeratorDelayInMinutes = 1;
public final BreakoutRoomsParams breakoutRoomsParams;
public final LockSettingsParams lockSettingsParams;
@@ -99,8 +101,6 @@ public class Meeting {
private String meetingEndedCallbackURL = "";
- public final Boolean endWhenNoModerator;
-
private Integer html5InstanceId;
public Meeting(Meeting.Builder builder) {
@@ -134,6 +134,7 @@ public class Meeting {
lockSettingsParams = builder.lockSettingsParams;
allowDuplicateExtUserid = builder.allowDuplicateExtUserid;
endWhenNoModerator = builder.endWhenNoModerator;
+ endWhenNoModeratorDelayInMinutes = builder.endWhenNoModeratorDelayInMinutes;
html5InstanceId = builder.html5InstanceId;
/*
@@ -660,6 +661,22 @@ public class Meeting {
this.userActivitySignResponseDelayInMinutes = userActivitySignResponseDelayInMinutes;
}
+ public Boolean getEndWhenNoModerator() {
+ return endWhenNoModerator;
+ }
+
+ public void setEndWhenNoModerator(Boolean endWhenNoModerator) {
+ this.endWhenNoModerator = endWhenNoModerator;
+ }
+
+ public Integer getEndWhenNoModeratorDelayInMinutes() {
+ return endWhenNoModeratorDelayInMinutes;
+ }
+
+ public void setEndWhenNoModeratorDelayInMinutes(Integer endWhenNoModeratorDelayInMinutes) {
+ this.endWhenNoModeratorDelayInMinutes = endWhenNoModeratorDelayInMinutes;
+ }
+
public String getMeetingEndedCallbackURL() {
return meetingEndedCallbackURL;
}
@@ -742,6 +759,7 @@ public class Meeting {
private LockSettingsParams lockSettingsParams;
private Boolean allowDuplicateExtUserid;
private Boolean endWhenNoModerator;
+ private Integer endWhenNoModeratorDelayInMinutes;
private int html5InstanceId;
public Builder(String externalId, String internalId, long createTime) {
@@ -885,6 +903,11 @@ public class Meeting {
return this;
}
+ public Builder withEndWhenNoModeratorDelayInMinutes(Integer endWhenNoModeratorDelayInMinutes) {
+ this.endWhenNoModeratorDelayInMinutes = endWhenNoModeratorDelayInMinutes;
+ return this;
+ }
+
public Builder withHTML5InstanceId(int instanceId) {
html5InstanceId = instanceId;
return this;
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api2/IBbbWebApiGWApp.java b/bbb-common-web/src/main/java/org/bigbluebutton/api2/IBbbWebApiGWApp.java
index 66859a934f..9f3dd1cd2a 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/api2/IBbbWebApiGWApp.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/api2/IBbbWebApiGWApp.java
@@ -26,6 +26,8 @@ public interface IBbbWebApiGWApp {
Integer userInactivityInspectTimerInMinutes,
Integer userInactivityThresholdInMinutes,
Integer userActivitySignResponseDelayInMinutes,
+ Boolean endWhenNoModerator,
+ Integer endWhenNoModeratorDelayInMinutes,
Boolean muteOnStart,
Boolean allowModsToUnmuteUsers,
Boolean keepEvents,
diff --git a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/BbbWebApiGWApp.scala b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/BbbWebApiGWApp.scala
index 8d720d60dd..6ba6d28e33 100755
--- a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/BbbWebApiGWApp.scala
+++ b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/BbbWebApiGWApp.scala
@@ -135,6 +135,8 @@ class BbbWebApiGWApp(
userInactivityInspectTimerInMinutes: java.lang.Integer,
userInactivityThresholdInMinutes: java.lang.Integer,
userActivitySignResponseDelayInMinutes: java.lang.Integer,
+ endWhenNoModerator: java.lang.Boolean,
+ endWhenNoModeratorDelayInMinutes: java.lang.Integer,
muteOnStart: java.lang.Boolean,
allowModsToUnmuteUsers: java.lang.Boolean,
keepEvents: java.lang.Boolean,
@@ -151,7 +153,9 @@ class BbbWebApiGWApp(
meetingExpireWhenLastUserLeftInMinutes = meetingExpireWhenLastUserLeftInMinutes.intValue(),
userInactivityInspectTimerInMinutes = userInactivityInspectTimerInMinutes.intValue(),
userInactivityThresholdInMinutes = userInactivityThresholdInMinutes.intValue(),
- userActivitySignResponseDelayInMinutes = userActivitySignResponseDelayInMinutes.intValue()
+ userActivitySignResponseDelayInMinutes = userActivitySignResponseDelayInMinutes.intValue(),
+ endWhenNoModerator = endWhenNoModerator.booleanValue(),
+ endWhenNoModeratorDelayInMinutes.intValue()
)
val password = PasswordProp(moderatorPass = moderatorPass, viewerPass = viewerPass)
diff --git a/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js b/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js
index 9aaff67b77..a059fc3c00 100755
--- a/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js
@@ -47,6 +47,8 @@ export default function addMeeting(meeting) {
userInactivityInspectTimerInMinutes: Number,
userInactivityThresholdInMinutes: Number,
userActivitySignResponseDelayInMinutes: Number,
+ endWhenNoModerator: Boolean,
+ endWhenNoModeratorDelayInMinutes: Number,
timeRemaining: Number,
},
welcomeProp: {
diff --git a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties
index a07eeb7ecd..422db063a1 100755
--- a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties
+++ b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties
@@ -387,3 +387,6 @@ defaultTextTrackUrl=${bigbluebutton.web.serverURL}/bigbluebutton
# Needed for classes where teacher gets disconnected and can't get back in. Prevents
# students from running amok.
endWhenNoModerator=false
+
+# Number of minutes to wait for moderator rejoin before end meeting (if `endWhenNoModerator` enabled)
+endWhenNoModeratorDelayInMinutes=1
diff --git a/bigbluebutton-web/grails-app/conf/spring/resources.xml b/bigbluebutton-web/grails-app/conf/spring/resources.xml
index e8986bdc91..dcda4360f7 100755
--- a/bigbluebutton-web/grails-app/conf/spring/resources.xml
+++ b/bigbluebutton-web/grails-app/conf/spring/resources.xml
@@ -164,6 +164,7 @@ with BigBlueButton; if not, see .
+