diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ProctoringRoomConnection.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ProctoringRoomConnection.java index 8a8ac806..24bfc16f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ProctoringRoomConnection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ProctoringRoomConnection.java @@ -12,6 +12,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType; @JsonIgnoreProperties(ignoreUnknown = true) @@ -66,6 +67,9 @@ public class ProctoringRoomConnection { @JsonProperty(ATTR_USER_NAME) public final String userName; + @JsonProperty(Domain.REMOTE_PROCTORING_ROOM.ATTR_ROOM_DATA) + public final String additionalRoomData; + @JsonCreator public ProctoringRoomConnection( @JsonProperty(ProctoringServiceSettings.ATTR_SERVER_TYPE) final ProctoringServerType proctoringServerType, @@ -79,7 +83,8 @@ public class ProctoringRoomConnection { @JsonProperty(ATTR_ROOM_KEY) final CharSequence roomKey, @JsonProperty(ATTR_API_KEY) final CharSequence apiKey, @JsonProperty(ATTR_MEETING_ID) final String meetingId, - @JsonProperty(ATTR_USER_NAME) final String userName) { + @JsonProperty(ATTR_USER_NAME) final String userName, + @JsonProperty(Domain.REMOTE_PROCTORING_ROOM.ATTR_ROOM_DATA) final String additionalRoomData) { this.proctoringServerType = proctoringServerType; this.connectionToken = connectionToken; @@ -93,6 +98,7 @@ public class ProctoringRoomConnection { this.apiKey = apiKey; this.meetingId = meetingId; this.userName = userName; + this.additionalRoomData = additionalRoomData; } public ProctoringServerType getProctoringServerType() { @@ -143,6 +149,10 @@ public class ProctoringRoomConnection { return this.meetingId; } + public String getAdditionalRoomData() { + return this.additionalRoomData; + } + @Override public String toString() { final StringBuilder builder = new StringBuilder(); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java index 269a911d..aa61abd4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java @@ -282,7 +282,9 @@ public class MonitoringRunningExam implements TemplateComposer { return CONFIRM_CLOSE_TOWNHALL; } }) - .withExec(action -> this.monitoringProctoringService.toggleTownhallRoom(proctoringGUIService, + .withExec(action -> this.monitoringProctoringService.toggleTownhallRoom( + proctoringGUIService, + proctoringSettings, action)) .noEventPropagation() .publish(); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/MonitoringProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/MonitoringProctoringService.java index 93f80704..26f5d832 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/MonitoringProctoringService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/MonitoringProctoringService.java @@ -119,6 +119,7 @@ public class MonitoringProctoringService { public PageAction toggleTownhallRoom( final ProctoringGUIService proctoringGUIService, + final ProctoringServiceSettings proctoringSettings, final PageAction action) { if (isTownhallRoomActive(action.getEntityKey().modelId)) { @@ -135,7 +136,7 @@ public class MonitoringProctoringService { } } else { - if (openTownhallRoom(proctoringGUIService, action)) { + if (openTownhallRoom(proctoringGUIService, proctoringSettings, action)) { this.pageService.firePageEvent( new ActionActivationEvent( true, @@ -245,7 +246,7 @@ public class MonitoringProctoringService { }); joinURL = roomData.get("start_url"); } catch (final Exception e) { - + log.error("Failed to get proctoring start URL: ", e); } final PageContext pc = pageContext.copy() .clearAttributes() @@ -282,17 +283,17 @@ public class MonitoringProctoringService { String.valueOf(proctoringSettings.examId), proctoringConnectionData); + final String zoomStartLink = extractZoomStartLink(room.additionalRoomData); if (proctoringSettings.useZoomAppClientForCollectingRoom && - StringUtils.isNotBlank(extractZoomStartLink(room))) { + StringUtils.isNotBlank(zoomStartLink)) { - final String startLink = extractZoomStartLink(room); final String script = String.format( getOpenRoomScriptTemplate(), room.name, 800, 1200, room.name, - startLink, + zoomStartLink, ""); RWT.getClient() @@ -326,24 +327,11 @@ public class MonitoringProctoringService { .call() .onError(error -> log.error("Failed to notify proctoring room opened: ", error)); } - } return action; } - private String extractZoomStartLink(final RemoteProctoringRoom room) { - try { - final Map data = - this.jsonMapper.readValue(room.additionalRoomData, new TypeReference>() { - }); - return data.get("start_url"); - } catch (final Exception e) { - log.error("Failed to extract Zoom start link: ", e); - return null; - } - } - public PageAction openOneToOneRoom( final PageAction action, final ClientConnectionData connectionData, @@ -394,11 +382,14 @@ public class MonitoringProctoringService { private boolean openTownhallRoom( final ProctoringGUIService proctoringGUIService, + final ProctoringServiceSettings proctoringSettings, final PageAction action) { try { final EntityKey examId = action.getEntityKey(); + final String endpoint = this.remoteProctoringEndpoint; + final String joinURL = this.guiServiceInfo.getExternalServerURIBuilder().toUriString(); if (proctoringGUIService.getTownhallWindowName(examId.modelId) == null) { final ProctoringRoomConnection proctoringConnectionData = proctoringGUIService .openTownhallRoom( @@ -422,8 +413,8 @@ public class MonitoringProctoringService { 800, 1200, "Town-Hall", - this.guiServiceInfo.getExternalServerURIBuilder().toUriString(), - this.remoteProctoringEndpoint); + joinURL, + endpoint); javaScriptExecutor.execute(script); } catch (final Exception e) { @@ -520,4 +511,18 @@ public class MonitoringProctoringService { } } + private String extractZoomStartLink(final String additioalRoomAttributesJson) { + try { + final Map data = + this.jsonMapper.readValue( + additioalRoomAttributesJson, + new TypeReference>() { + }); + return data.get("start_url"); + } catch (final Exception e) { + log.error("Failed to extract Zoom start link: ", e); + return null; + } + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java index aa469d6a..1d4d052e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java @@ -379,7 +379,8 @@ public class JitsiProctoringService implements ExamProctoringService { null, null, null, - clientName); + clientName, + null); }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java index c5ffd950..12f77784 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java @@ -294,7 +294,8 @@ public class ZoomProctoringService implements ExamProctoringService { credentials.accessToken, credentials.clientId, String.valueOf(additionalZoomRoomData.meeting_id), - this.authorizationService.getUserService().getCurrentUser().getUsername()); + this.authorizationService.getUserService().getCurrentUser().getUsername(), + remoteProctoringRoom.additionalRoomData); }); } @@ -354,7 +355,8 @@ public class ZoomProctoringService implements ExamProctoringService { credentials.accessToken, credentials.clientId, String.valueOf(additionalZoomRoomData.meeting_id), - clientConnection.clientConnection.userSessionId); + clientConnection.clientConnection.userSessionId, + remoteProctoringRoom.additionalRoomData); }); } @@ -751,7 +753,6 @@ public class ZoomProctoringService implements ExamProctoringService { private long forExam(final ProctoringServiceSettings examProctoring) { - // TODO // NOTE: following is the original code that includes the exam end time but seems to make trouble for OLAT final long nowInSeconds = Utils.getSecondsNow(); final long nowPlus30MinInSeconds = nowInSeconds + Utils.toSeconds(30 * Constants.MINUTE_IN_MILLIS); @@ -874,15 +875,14 @@ public class ZoomProctoringService implements ExamProctoringService { .buildAndExpand(userId) .normalize() .toUriString(); - final String host = new URL(zoomServerUrl).getHost(); + final ApplyUserSettingsRequest applySettingsRequest = new ApplyUserSettingsRequest(); final String body = this.zoomProctoringService.jsonMapper.writeValueAsString(applySettingsRequest); final HttpHeaders headers = getHeaders(credentials); headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); - - return exchange(url, HttpMethod.PATCH, body, headers); - + final ResponseEntity exchange = exchange(url, HttpMethod.PATCH, body, headers); + return exchange; } catch (final Exception e) { log.error("Failed to apply user settings for Zoom user: {}", userId, e); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomRoomRequestResponse.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomRoomRequestResponse.java index 57a02181..d1a2378d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomRoomRequestResponse.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomRoomRequestResponse.java @@ -90,19 +90,43 @@ public interface ZoomRoomRequestResponse { @JsonIgnoreProperties(ignoreUnknown = true) static class ApplyUserSettingsRequest { @JsonProperty final InMeetingSettings in_meeting; + + // NOTE: This seems to need a special Zoom Account Plan to work + //@JsonProperty final FeaturesSettings feature; + public ApplyUserSettingsRequest() { - this.in_meeting = new InMeetingSettings(true, 1); + this.in_meeting = new InMeetingSettings(true, 3, 3); + //this.feature = new FeaturesSettings("Basic"); } - public ApplyUserSettingsRequest(final InMeetingSettings in_meeting) { + + public ApplyUserSettingsRequest( + final InMeetingSettings in_meeting, + final FeaturesSettings feature) { this.in_meeting = in_meeting; + //this.feature = feature; } static class InMeetingSettings { + @JsonProperty final boolean auto_saving_chat; @JsonProperty final int allow_users_save_chats; - public InMeetingSettings(boolean auto_saving_chat, int allow_users_save_chats) { + @JsonProperty final int allow_participants_chat_with; + + public InMeetingSettings( + final boolean auto_saving_chat, + final int allow_users_save_chats, + final int allow_participants_chat_with) { + this.auto_saving_chat = auto_saving_chat; this.allow_users_save_chats = allow_users_save_chats; + this.allow_participants_chat_with = allow_participants_chat_with; + } + } + + static class FeaturesSettings { + @JsonProperty final String concurrent_meeting; + public FeaturesSettings(final String concurrent_meeting) { + this.concurrent_meeting = concurrent_meeting; } } } diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/JitsiWindowScriptResolverTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/JitsiWindowScriptResolverTest.java index 749c24a4..1fd9539c 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/JitsiWindowScriptResolverTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/JitsiWindowScriptResolverTest.java @@ -41,7 +41,8 @@ public class JitsiWindowScriptResolverTest { "API_KEY", "ROOM_KEY", "MEETING_ID", - "USER_NAME")); + "USER_NAME", + null)); final ProctoringWindowData proctoringWindowDataOther = new ProctoringWindowData( "0", @@ -58,7 +59,8 @@ public class JitsiWindowScriptResolverTest { "API_KEY", "ROOM_KEY", "MEETING_ID", - "USER_NAME")); + "USER_NAME", + null)); assertFalse(jitsiWindowScriptResolver.applies(proctoringWindowDataOther)); assertTrue(jitsiWindowScriptResolver.applies(proctoringWindowData)); diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ZoomWindowScriptResolverTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ZoomWindowScriptResolverTest.java index 6c2d625d..72d66925 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ZoomWindowScriptResolverTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ZoomWindowScriptResolverTest.java @@ -41,7 +41,8 @@ public class ZoomWindowScriptResolverTest { "API_KEY", "ROOM_KEY", "MEETING_ID", - "USER_NAME")); + "USER_NAME", + null)); final ProctoringWindowData proctoringWindowDataOther = new ProctoringWindowData( "0", @@ -58,7 +59,8 @@ public class ZoomWindowScriptResolverTest { "API_KEY", "ROOM_KEY", "MEETING_ID", - "USER_NAME")); + "USER_NAME", + null)); assertFalse(zoomWindowScriptResolver.applies(proctoringWindowDataOther)); assertTrue(zoomWindowScriptResolver.applies(proctoringWindowDataZoom));