SEBSERV-262

This commit is contained in:
anhefti 2022-01-27 11:39:57 +01:00
parent 8c6ffee2a9
commit 0ab61c876d
8 changed files with 83 additions and 37 deletions

View file

@ -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();

View file

@ -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();

View file

@ -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<String, String> data =
this.jsonMapper.readValue(room.additionalRoomData, new TypeReference<Map<String, String>>() {
});
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<String, String> data =
this.jsonMapper.readValue(
additioalRoomAttributesJson,
new TypeReference<Map<String, String>>() {
});
return data.get("start_url");
} catch (final Exception e) {
log.error("Failed to extract Zoom start link: ", e);
return null;
}
}
}

View file

@ -379,7 +379,8 @@ public class JitsiProctoringService implements ExamProctoringService {
null,
null,
null,
clientName);
clientName,
null);
});
}

View file

@ -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<String> 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);

View file

@ -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;
}
}
}

View file

@ -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));

View file

@ -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));