SEBSERV-262
This commit is contained in:
parent
8c6ffee2a9
commit
0ab61c876d
8 changed files with 83 additions and 37 deletions
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -379,7 +379,8 @@ public class JitsiProctoringService implements ExamProctoringService {
|
|||
null,
|
||||
null,
|
||||
null,
|
||||
clientName);
|
||||
clientName,
|
||||
null);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue