diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutModel.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutModel.scala index cf83653d87..bba8d27930 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutModel.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutModel.scala @@ -18,8 +18,11 @@ object BreakoutModel { captureSlides: Boolean, captureNotesFilename: String, captureSlidesFilename: String, + allPages: Boolean, + presId: String, ): BreakoutRoom2x = { - new BreakoutRoom2x(id, externalId, name, parentId, sequence, shortName, isDefaultName, freeJoin, voiceConf, assignedUsers, Vector(), Vector(), None, false, captureNotes, captureSlides, captureNotesFilename, captureSlidesFilename) + new BreakoutRoom2x(id, externalId, name, parentId, sequence, shortName, isDefaultName, freeJoin, voiceConf, assignedUsers, Vector(), Vector(), None, false, + captureNotes, captureSlides, captureNotesFilename, captureSlidesFilename, allPages, presId) } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/breakout/CreateBreakoutRoomsCmdMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/breakout/CreateBreakoutRoomsCmdMsgHdlr.scala index 0b3c5f768d..d2d1f14e99 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/breakout/CreateBreakoutRoomsCmdMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/breakout/CreateBreakoutRoomsCmdMsgHdlr.scala @@ -40,26 +40,30 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait { } def processRequest(msg: CreateBreakoutRoomsCmdMsg, state: MeetingState2x): MeetingState2x = { - - val presId = getPresentationId(state) - val presSlide = getPresentationSlide(state) + val presId = getPresentationId(state) // The current presentation + val presSlide = getPresentationSlide(state) // The current slide val parentId = liveMeeting.props.meetingProp.intId var rooms = new collection.immutable.HashMap[String, BreakoutRoom2x] var i = 0 for (room <- msg.body.rooms) { + val roomPresId = if (room.presId.isEmpty) presId else room.presId; + i += 1 val (internalId, externalId) = BreakoutRoomsUtil.createMeetingIds(liveMeeting.props.meetingProp.intId, i) val voiceConf = BreakoutRoomsUtil.createVoiceConfId(liveMeeting.props.voiceProp.voiceConf, i) val breakout = BreakoutModel.create(parentId, internalId, externalId, room.name, room.sequence, room.shortName, room.isDefaultName, room.freeJoin, voiceConf, room.users, msg.body.captureNotes, - msg.body.captureSlides, room.captureNotesFilename, room.captureSlidesFilename) + msg.body.captureSlides, room.captureNotesFilename, room.captureSlidesFilename, + room.allPages, roomPresId) rooms = rooms + (breakout.id -> breakout) } for (breakout <- rooms.values.toVector) { + val roomSlides = if (breakout.allPages) -1 else presSlide; + val roomDetail = new BreakoutRoomDetail( breakout.id, breakout.name, liveMeeting.props.meetingProp.intId, @@ -72,7 +76,9 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait { msg.body.durationInMinutes * 60, liveMeeting.props.password.moderatorPass, liveMeeting.props.password.viewerPass, - presId, presSlide, msg.body.record, + breakout.presId, + roomSlides, + msg.body.record, liveMeeting.props.breakoutProps.privateChatEnabled, breakout.captureNotes, breakout.captureSlides, diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/BreakoutRoom2x.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/BreakoutRoom2x.scala index 009a2b018d..5fa3a84e07 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/BreakoutRoom2x.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/BreakoutRoom2x.scala @@ -19,6 +19,8 @@ case class BreakoutRoom2x( captureSlides: Boolean, captureNotesFilename: String, captureSlidesFilename: String, + allPages: Boolean, + presId: String, ) { } diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/BreakoutMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/BreakoutMsgs.scala index 7ac03144e3..6f0af8d0f9 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/BreakoutMsgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/BreakoutMsgs.scala @@ -71,7 +71,7 @@ case class BreakoutRoomDetail( object CreateBreakoutRoomsCmdMsg { val NAME = "CreateBreakoutRoomsCmdMsg" } case class CreateBreakoutRoomsCmdMsg(header: BbbClientMsgHeader, body: CreateBreakoutRoomsCmdMsgBody) extends StandardMsg case class CreateBreakoutRoomsCmdMsgBody(meetingId: String, durationInMinutes: Int, record: Boolean, captureNotes: Boolean, captureSlides: Boolean, rooms: Vector[BreakoutRoomMsgBody], sendInviteToModerators: Boolean) -case class BreakoutRoomMsgBody(name: String, sequence: Int, shortName: String, captureNotesFilename: String, captureSlidesFilename: String, isDefaultName: Boolean, freeJoin: Boolean, users: Vector[String]) +case class BreakoutRoomMsgBody(name: String, sequence: Int, shortName: String, captureNotesFilename: String, captureSlidesFilename: String, isDefaultName: Boolean, freeJoin: Boolean, users: Vector[String], allPages: Boolean, presId: String) // Sent by user to request ending all the breakout rooms object EndAllBreakoutRoomsMsg { val NAME = "EndAllBreakoutRoomsMsg" } diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java index 6fe5e1014f..9add5bb657 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java @@ -94,6 +94,7 @@ public class PresentationUrlDownloadService { }, 5, TimeUnit.SECONDS); } + // A negative presentationSlide indicates the entire presentation deck should be used. private void extractPage(final String sourceMeetingId, final String presentationId, final Integer presentationSlide, final String destinationMeetingId) { @@ -146,7 +147,7 @@ public class PresentationUrlDownloadService { + newFilename; File newPresentation = new File(newFilePath); - if (sourcePresentationFile.getName().toLowerCase().endsWith("pdf")) { + if (sourcePresentationFile.getName().toLowerCase().endsWith("pdf") && presentationSlide >= 0) { pageExtractor.extractPage(sourcePresentationFile, new File( newFilePath), presentationSlide); } else { diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/component.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/component.jsx index a7fb2ac68d..b46ac9a908 100644 --- a/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/component.jsx @@ -181,12 +181,17 @@ const intlMessages = defineMessages({ id: 'app.createBreakoutRoom.sendInvitationToMods', description: 'label for checkbox send invitation to moderators', }, + currentSlide: { + id: 'app.createBreakoutRoom.currentSlideLabel', + description: 'label for current slide', + }, }); const BREAKOUT_LIM = Meteor.settings.public.app.breakouts.breakoutRoomLimit; const MIN_BREAKOUT_ROOMS = 2; const MAX_BREAKOUT_ROOMS = BREAKOUT_LIM > MIN_BREAKOUT_ROOMS ? BREAKOUT_LIM : MIN_BREAKOUT_ROOMS; const MIN_BREAKOUT_TIME = 5; +const CURRENT_SLIDE_PREFIX = 'current-'; const propTypes = { intl: PropTypes.shape({ @@ -244,6 +249,7 @@ class BreakoutRoom extends PureComponent { this.removeRoomUsers = this.removeRoomUsers.bind(this); this.renderErrorMessages = this.renderErrorMessages.bind(this); this.onUpdateBreakouts = this.onUpdateBreakouts.bind(this); + this.getRoomPresentation = this.getRoomPresentation.bind(this); this.state = { numberOfRooms: MIN_BREAKOUT_ROOMS, @@ -266,6 +272,7 @@ class BreakoutRoom extends PureComponent { durationIsValid: true, breakoutJoinedUsers: null, enableUnassingUsers: null, + roomPresentations: [], }; this.btnLevelId = uniqueId('btn-set-level-'); @@ -503,6 +510,8 @@ class BreakoutRoom extends PureComponent { isDefaultName: !this.hasNameChanged(seq), freeJoin, sequence: seq, + allPages: !this.getRoomPresentation(seq).startsWith(CURRENT_SLIDE_PREFIX), + presId: this.getRoomPresentation(seq).replace(CURRENT_SLIDE_PREFIX, ''), })); createBreakoutRoom(rooms, durationTime, record, captureNotes, captureSlides, inviteMods); @@ -688,6 +697,15 @@ class BreakoutRoom extends PureComponent { }); } + getRoomPresentation(position) { + const { roomPresentations } = this.state; + const { presentations } = this.props; + + const currentPresentation = presentations.find((presentation) => presentation.current); + + return roomPresentations[position] || `${CURRENT_SLIDE_PREFIX}${currentPresentation?.id}`; + } + getFullName(position) { const { meetingName } = this.props; @@ -909,11 +927,12 @@ class BreakoutRoom extends PureComponent { } renderRoomsGrid() { - const { intl, isUpdate } = this.props; + const { intl, isUpdate, presentations } = this.props; const { leastOneUserIsValid, numberOfRooms, roomNamesChanged, + roomPresentations, } = this.state; const rooms = (numberOfRooms > MAX_BREAKOUT_ROOMS @@ -941,6 +960,17 @@ class BreakoutRoom extends PureComponent { }); }; + const changeRoomPresentation = (position) => (ev) => { + const newRoomsPresentations = [...roomPresentations]; + newRoomsPresentations[position] = ev.target.value; + + this.setState({ + roomPresentations: newRoomsPresentations, + }); + }; + + const currentPresentation = presentations.find((presentation) => presentation.current); + return ( { this.listOfUsers = r; }} data-test="roomGrid"> { @@ -963,6 +993,31 @@ class BreakoutRoom extends PureComponent { {intl.formatMessage(intlMessages.roomNameInputDesc)} + { presentations.length > 0 ? ( + + + { currentPresentation?.id ? ( + + ) : null } + { + presentations.map((presentation) => ( + + )) + } + + + ) : null } {this.renderUserItemByRoom(value)} diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/container.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/container.jsx index efeb81e60d..ef31af5d5a 100644 --- a/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/container.jsx @@ -3,6 +3,7 @@ import { withTracker } from 'meteor/react-meteor-data'; import ActionsBarService from '/imports/ui/components/actions-bar/service'; import BreakoutRoomService from '/imports/ui/components/breakout-room/service'; import CreateBreakoutRoomModal from './component'; +import Presentations from '/imports/api/presentations'; import { isImportSharedNotesFromBreakoutRoomsEnabled, isImportPresentationWithAnnotationsFromBreakoutRoomsEnabled } from '/imports/ui/services/features'; const METEOR_SETTINGS_APP = Meteor.settings.public.app; @@ -46,4 +47,5 @@ export default withTracker(() => ({ meetingName: ActionsBarService.meetingName(), amIModerator: ActionsBarService.amIModerator(), moveUser: ActionsBarService.moveUser, + presentations: Presentations.find({ 'conversion.done': true }).fetch(), }))(CreateBreakoutRoomContainer); diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/styles.js b/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/styles.js index 8b05b1eab8..956e15c58c 100644 --- a/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/styles.js +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/styles.js @@ -68,6 +68,15 @@ const FreeJoinLabel = styled.label` } `; +const BreakoutSlideLabel = styled.label` + font-size: ${fontSizeSmall}; + font-weight: bolder; + display: flex; + align-items: center; + font-size: ${fontSizeSmall}; + margin-bottom: 0.2rem; +` + const BreakoutNameInput = styled.input` width: 100%; text-align: center; @@ -377,4 +386,5 @@ export default { SubTitle, Content, ContentContainer, + BreakoutSlideLabel, }; diff --git a/bigbluebutton-html5/public/locales/en.json b/bigbluebutton-html5/public/locales/en.json index 3ae55d14e7..f6e4574b17 100755 --- a/bigbluebutton-html5/public/locales/en.json +++ b/bigbluebutton-html5/public/locales/en.json @@ -1249,6 +1249,7 @@ "app.createBreakoutRoom.setTimeHigherThanMeetingTimeError": "The breakout rooms duration can't exceed the meeting remaining time.", "app.createBreakoutRoom.roomNameInputDesc": "Updates breakout room name", "app.createBreakoutRoom.movedUserLabel": "Moved {0} to room {1}", + "app.createBreakoutRoom.currentSlideLabel": "Current slide", "app.updateBreakoutRoom.modalDesc": "To update or invite a user, simply drag them into the desired room.", "app.updateBreakoutRoom.cancelLabel": "Cancel", "app.updateBreakoutRoom.title": "Update Breakout Rooms",