SEBSERV-139 implementing join-in room in overall monitoring view
This commit is contained in:
parent
a91192ecbb
commit
3c0c8a4c41
22 changed files with 740 additions and 187 deletions
|
@ -13,8 +13,9 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class SEBClientProctoringConnectionData {
|
||||
public class SEBProctoringConnectionData {
|
||||
|
||||
public static final String ATTR_CONNECTION_TOKEN = "connectionToken";
|
||||
public static final String ATTR_SERVER_HOST = "serverHost";
|
||||
public static final String ATTR_SERVER_URL = "serverURL";
|
||||
public static final String ATTR_ROOM_NAME = "roomName";
|
||||
|
@ -22,6 +23,9 @@ public class SEBClientProctoringConnectionData {
|
|||
public static final String ATTR_ACCESS_TOKEN = "accessToken";
|
||||
public static final String ATTR_CONNECTION_URL = "connectionURL";
|
||||
|
||||
@JsonProperty(ATTR_CONNECTION_TOKEN)
|
||||
public final String connectionToken;
|
||||
|
||||
@JsonProperty(ATTR_SERVER_HOST)
|
||||
public final String serverHost;
|
||||
|
||||
|
@ -37,24 +41,25 @@ public class SEBClientProctoringConnectionData {
|
|||
@JsonProperty(ATTR_ACCESS_TOKEN)
|
||||
public final String accessToken;
|
||||
|
||||
@JsonProperty(ATTR_CONNECTION_URL)
|
||||
public final String connectionURL;
|
||||
|
||||
@JsonCreator
|
||||
public SEBClientProctoringConnectionData(
|
||||
public SEBProctoringConnectionData(
|
||||
@JsonProperty(ATTR_CONNECTION_TOKEN) final String connectionToken,
|
||||
@JsonProperty(ATTR_SERVER_HOST) final String serverHost,
|
||||
@JsonProperty(ATTR_SERVER_URL) final String serverURL,
|
||||
@JsonProperty(ATTR_ROOM_NAME) final String roomName,
|
||||
@JsonProperty(ATTR_SUBJECT) final String subject,
|
||||
@JsonProperty(ATTR_ACCESS_TOKEN) final String accessToken,
|
||||
@JsonProperty(ATTR_CONNECTION_URL) final String connectionURL) {
|
||||
@JsonProperty(ATTR_ACCESS_TOKEN) final String accessToken) {
|
||||
|
||||
this.connectionToken = connectionToken;
|
||||
this.serverHost = serverHost;
|
||||
this.serverURL = serverURL;
|
||||
this.roomName = roomName;
|
||||
this.subject = subject;
|
||||
this.accessToken = accessToken;
|
||||
this.connectionURL = connectionURL;
|
||||
}
|
||||
|
||||
public String getConnectionToken() {
|
||||
return this.connectionToken;
|
||||
}
|
||||
|
||||
public String getServerHost() {
|
||||
|
@ -69,10 +74,6 @@ public class SEBClientProctoringConnectionData {
|
|||
return this.serverURL;
|
||||
}
|
||||
|
||||
public String getConnectionURL() {
|
||||
return this.connectionURL;
|
||||
}
|
||||
|
||||
public String getRoomName() {
|
||||
return this.roomName;
|
||||
}
|
||||
|
@ -94,8 +95,6 @@ public class SEBClientProctoringConnectionData {
|
|||
builder.append(this.subject);
|
||||
builder.append(", accessToken=");
|
||||
builder.append(this.accessToken);
|
||||
builder.append(", connectionURL=");
|
||||
builder.append(this.connectionURL);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
|
@ -22,7 +22,7 @@ import org.springframework.stereotype.Component;
|
|||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.SEBServerAuthorizationContext;
|
||||
|
@ -51,8 +51,8 @@ public class ProctoringServlet extends HttpServlet {
|
|||
" const options = {\n" +
|
||||
" parentNode: document.querySelector('#proctoring'),\n" +
|
||||
" roomName: '%s',\n" +
|
||||
// " width: 600,\n" +
|
||||
" height: 400,\n" +
|
||||
" width: window.innerWidth,\n" +
|
||||
" height: window.innerHeight,\n" +
|
||||
" jwt: '%s',\n" +
|
||||
" configOverwrite: { startAudioOnly: false, startWithAudioMuted: true, startWithVideoMuted: true, disable1On1Mode: true },\n" +
|
||||
" interfaceConfigOverwrite: { " +
|
||||
|
@ -100,8 +100,8 @@ public class ProctoringServlet extends HttpServlet {
|
|||
return;
|
||||
}
|
||||
|
||||
final SEBClientProctoringConnectionData proctoringConnectionData =
|
||||
(SEBClientProctoringConnectionData) httpSession.getAttribute(SESSION_ATTR_PROCTORING_DATA);
|
||||
final SEBProctoringConnectionData proctoringConnectionData =
|
||||
(SEBProctoringConnectionData) httpSession.getAttribute(SESSION_ATTR_PROCTORING_DATA);
|
||||
|
||||
final String script = String.format(
|
||||
HTML,
|
||||
|
|
|
@ -27,7 +27,7 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
|||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
|
||||
|
@ -54,7 +54,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicator
|
|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionData;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetProctorDataForSEBClient;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetProctorRoomConnectionData;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.ClientConnectionDetails;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.InstructionProcessor;
|
||||
|
@ -277,10 +277,7 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
.withEntityKey(parentEntityKey)
|
||||
.withExec(action -> this.openProctorScreen(action, connectionToken))
|
||||
.noEventPropagation()
|
||||
.publishIf(() -> proctoringEnabled)
|
||||
|
||||
;
|
||||
|
||||
.publishIf(() -> proctoringEnabled);
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
|
@ -295,12 +292,12 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
// @formatter:on
|
||||
|
||||
private PageAction openProctorScreen(final PageAction action, final String connectionToken) {
|
||||
final SEBClientProctoringConnectionData proctoringConnectionData =
|
||||
this.pageService.getRestService().getBuilder(GetProctorDataForSEBClient.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId)
|
||||
.withURIVariable(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionToken)
|
||||
.call()
|
||||
.getOrThrow();
|
||||
final SEBProctoringConnectionData proctoringConnectionData = this.pageService.getRestService()
|
||||
.getBuilder(GetProctorRoomConnectionData.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId)
|
||||
.withQueryParam(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionToken)
|
||||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding();
|
||||
final String roomName = urlEncoder.encodeToString(Utils.toByteArray(connectionToken));
|
||||
|
@ -316,6 +313,9 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
this.guiServiceInfo.getExternalServerURIBuilder().toUriString(),
|
||||
roomName);
|
||||
javaScriptExecutor.execute(script);
|
||||
this.pageService.getCurrentUser()
|
||||
.getProctoringGUIService()
|
||||
.registerProctoringWindow(roomName);
|
||||
return action;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ import java.util.function.BooleanSupplier;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.eclipse.rap.rwt.RWT;
|
||||
import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
|
@ -31,6 +33,8 @@ import ch.ethz.seb.sebserver.gbl.model.Domain;
|
|||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||
|
@ -38,6 +42,8 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
|||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.gui.GuiServiceInfo;
|
||||
import ch.ethz.seb.sebserver.gui.ProctoringServlet;
|
||||
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
|
||||
import ch.ethz.seb.sebserver.gui.service.ResourceService;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
|
@ -53,10 +59,13 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionDataList;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetProctorRoomConnectionData;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.ClientConnectionTable;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.InstructionProcessor;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
|
@ -80,18 +89,21 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
private final PageService pageService;
|
||||
private final ResourceService resourceService;
|
||||
private final InstructionProcessor instructionProcessor;
|
||||
private final GuiServiceInfo guiServiceInfo;
|
||||
private final long pollInterval;
|
||||
|
||||
protected MonitoringRunningExam(
|
||||
final ServerPushService serverPushService,
|
||||
final PageService pageService,
|
||||
final InstructionProcessor instructionProcessor,
|
||||
final GuiServiceInfo guiServiceInfo,
|
||||
@Value("${sebserver.gui.webservice.poll-interval:1000}") final long pollInterval) {
|
||||
|
||||
this.serverPushService = serverPushService;
|
||||
this.pageService = pageService;
|
||||
this.resourceService = pageService.getResourceService();
|
||||
this.instructionProcessor = instructionProcessor;
|
||||
this.guiServiceInfo = guiServiceInfo;
|
||||
this.pollInterval = pollInterval;
|
||||
}
|
||||
|
||||
|
@ -145,7 +157,8 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
pageContext,
|
||||
ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION,
|
||||
ActionDefinition.MONITOR_EXAM_QUIT_SELECTED,
|
||||
ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION));
|
||||
ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION,
|
||||
ActionDefinition.MONITOR_EXAM_NEW_PROCTOR_ROOM));
|
||||
|
||||
this.serverPushService.runServerPush(
|
||||
new ServerPushContext(content, Utils.truePredicate()),
|
||||
|
@ -275,6 +288,136 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
final boolean proctoringEnabled = restService
|
||||
.getBuilder(GetProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||
.call()
|
||||
.map(ProctoringSettings::getEnableProctoring)
|
||||
.getOr(false);
|
||||
|
||||
if (proctoringEnabled) {
|
||||
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_NEW_PROCTOR_ROOM)
|
||||
.withEntityKey(entityKey)
|
||||
.withSelect(
|
||||
clientTable::getSelection,
|
||||
action -> newProctoringRoom(clientTable, action),
|
||||
EMPTY_SELECTION_TEXT_KEY)
|
||||
.noEventPropagation()
|
||||
.publishIf(privilege, false);
|
||||
|
||||
final ProctoringGUIService proctoringGUIService = this.pageService
|
||||
.getCurrentUser()
|
||||
.getProctoringGUIService();
|
||||
|
||||
proctoringGUIService.roomNames().forEach(roomName -> {
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_VIEW_PROCTOR_ROOM)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(a -> showProctoringRoom(roomName, clientTable, a))
|
||||
.withNameAttributes(roomName)
|
||||
.noEventPropagation()
|
||||
.publish();
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_CLOSE_PROCTOR_ROOM)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(a -> closeProctoringRoom(roomName, clientTable, a))
|
||||
.withNameAttributes(roomName)
|
||||
.publish();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
private static final String OPEN_SINGEL_ROOM_SCRIPT =
|
||||
"var existingWin = window.open('', '%s', 'height=800,width=1200,location=no,scrollbars=yes,status=no,menubar=yes,toolbar=yes,titlebar=yes');\n" +
|
||||
"if(existingWin.location.href === 'about:blank'){\n" +
|
||||
" existingWin.location.href = '%s/proctoring/%s';\n" +
|
||||
" existingWin.focus();\n" +
|
||||
"} else {\n" +
|
||||
" existingWin.focus();\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
|
||||
private PageAction closeProctoringRoom(
|
||||
final String roomName,
|
||||
final ClientConnectionTable clientTable,
|
||||
final PageAction action) {
|
||||
|
||||
final ProctoringGUIService proctoringGUIService = this.pageService
|
||||
.getCurrentUser()
|
||||
.getProctoringGUIService();
|
||||
|
||||
proctoringGUIService.closeRoom(roomName);
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
private PageAction newProctoringRoom(
|
||||
final ClientConnectionTable clientTable,
|
||||
final PageAction action) {
|
||||
|
||||
final ProctoringGUIService proctoringGUIService = this.pageService
|
||||
.getCurrentUser()
|
||||
.getProctoringGUIService();
|
||||
final String newRoomName = proctoringGUIService.createNewRoomName();
|
||||
final Set<String> connectionTokens = clientTable.getConnectionTokens(
|
||||
ClientConnection.getStatusPredicate(ConnectionStatus.ACTIVE),
|
||||
true);
|
||||
|
||||
proctoringGUIService.registerNewProcotringRoom(
|
||||
action.getEntityKey().modelId,
|
||||
newRoomName,
|
||||
connectionTokens);
|
||||
|
||||
this.pageService.pageActionBuilder(action.pageContext())
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_VIEW_PROCTOR_ROOM)
|
||||
.withEntityKey(action.getEntityKey())
|
||||
.withExec(a -> showProctoringRoom(newRoomName, clientTable, a))
|
||||
.withNameAttributes(newRoomName)
|
||||
.noEventPropagation()
|
||||
.publish();
|
||||
|
||||
this.pageService.pageActionBuilder(action.pageContext())
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_CLOSE_PROCTOR_ROOM)
|
||||
.withEntityKey(action.getEntityKey())
|
||||
.withExec(a -> closeProctoringRoom(newRoomName, clientTable, a))
|
||||
.withNameAttributes(newRoomName)
|
||||
.publish();
|
||||
|
||||
return showProctoringRoom(newRoomName, clientTable, action);
|
||||
}
|
||||
|
||||
private PageAction showProctoringRoom(
|
||||
final String roomName,
|
||||
final ClientConnectionTable clientTable,
|
||||
final PageAction action) {
|
||||
|
||||
final SEBProctoringConnectionData proctoringConnectionData = this.pageService.getRestService()
|
||||
.getBuilder(GetProctorRoomConnectionData.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId)
|
||||
.withQueryParam(SEBProctoringConnectionData.ATTR_ROOM_NAME, roomName)
|
||||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
RWT.getUISession().getHttpSession().setAttribute(
|
||||
ProctoringServlet.SESSION_ATTR_PROCTORING_DATA,
|
||||
proctoringConnectionData);
|
||||
|
||||
final String script = String.format(
|
||||
OPEN_SINGEL_ROOM_SCRIPT,
|
||||
roomName,
|
||||
this.guiServiceInfo.getExternalServerURIBuilder().toUriString(),
|
||||
roomName);
|
||||
|
||||
RWT.getClient()
|
||||
.getService(JavaScriptExecutor.class)
|
||||
.execute(script);
|
||||
|
||||
this.pageService.getCurrentUser()
|
||||
.getProctoringGUIService()
|
||||
.registerProctoringWindow(roomName);
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
private static Function<PageAction, PageAction> showStateViewAction(
|
||||
|
|
|
@ -29,7 +29,8 @@ public enum ActionCategory {
|
|||
LOGS_USER_ACTIVITY_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1),
|
||||
LOGS_SEB_CLIENT_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1),
|
||||
VARIA(new LocTextKey("sebserver.overall.action.category.varia"), 0),
|
||||
FILTER(new LocTextKey("sebserver.overall.action.category.filter"), 50);
|
||||
FILTER(new LocTextKey("sebserver.exam.monitoring.action.category.filter"), 50),
|
||||
PROCTORING(new LocTextKey("sebserver.exam.overall.action.category.proctoring"), 60);
|
||||
|
||||
public final LocTextKey title;
|
||||
public final int slotPosition;
|
||||
|
|
|
@ -684,6 +684,22 @@ public enum ActionDefinition {
|
|||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.FILTER),
|
||||
|
||||
MONITOR_EXAM_NEW_PROCTOR_ROOM(
|
||||
new LocTextKey("sebserver.monitoring.exam.action.newroom"),
|
||||
ImageIcon.VISIBILITY,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.PROCTORING),
|
||||
MONITOR_EXAM_VIEW_PROCTOR_ROOM(
|
||||
new LocTextKey("sebserver.monitoring.exam.action.viewroom"),
|
||||
ImageIcon.SHOW,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.PROCTORING),
|
||||
MONITOR_EXAM_CLOSE_PROCTOR_ROOM(
|
||||
new LocTextKey("sebserver.monitoring.exam.action.closeroom"),
|
||||
ImageIcon.DELETE,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.PROCTORING),
|
||||
|
||||
LOGS_USER_ACTIVITY_LIST(
|
||||
new LocTextKey("sebserver.logs.activity.userlogs"),
|
||||
PageStateDefinitionImpl.USER_ACTIVITY_LOGS),
|
||||
|
@ -745,4 +761,8 @@ public enum ActionDefinition {
|
|||
this.category = category;
|
||||
}
|
||||
|
||||
public LocTextKey getTitle(final Object... args) {
|
||||
return new LocTextKey(this.title.name, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ public class ActionPane implements TemplateComposer {
|
|||
final Tree treeForGroup = getTreeForGroup(actionTrees, parent, event.action.definition, true);
|
||||
final TreeItem actionItem = ActionPane.this.widgetFactory.treeItemLocalized(
|
||||
treeForGroup,
|
||||
event.action.definition.title);
|
||||
event.action.getTitle());
|
||||
|
||||
final Image image = event.active
|
||||
? event.action.definition.icon.getImage(parent.getDisplay())
|
||||
|
@ -114,6 +114,7 @@ public class ActionPane implements TemplateComposer {
|
|||
continue;
|
||||
}
|
||||
|
||||
final PageAction action = (PageAction) actionItem.getData(ACTION_EVENT_CALL_KEY);
|
||||
final Image image = event.activation
|
||||
? ad.icon.getImage(parent.getDisplay())
|
||||
: ad.icon.getGreyedImage(parent.getDisplay());
|
||||
|
@ -122,18 +123,21 @@ public class ActionPane implements TemplateComposer {
|
|||
actionItem.setForeground(null);
|
||||
} else {
|
||||
actionItem.setForeground(new Color(parent.getDisplay(), new RGBA(150, 150, 150, 50)));
|
||||
ActionPane.this.pageService.getPolyglotPageService().injectI18n(actionItem, ad.title);
|
||||
ActionPane.this.pageService.getPolyglotPageService().injectI18n(
|
||||
actionItem,
|
||||
(action != null) ? action.getTitle() : ad.title);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.decoration != null) {
|
||||
final TreeItem actionItemToDecorate = findAction(actionTrees, parent, event.decoration._1);
|
||||
final PageAction action = (PageAction) actionItemToDecorate.getData(ACTION_EVENT_CALL_KEY);
|
||||
if (actionItemToDecorate != null && event.decoration._2 != null) {
|
||||
actionItemToDecorate.setImage(0,
|
||||
event.decoration._2.icon.getImage(parent.getDisplay()));
|
||||
ActionPane.this.pageService.getPolyglotPageService().injectI18n(
|
||||
actionItemToDecorate,
|
||||
event.decoration._2.title);
|
||||
(action != null) ? action.getTitle() : event.decoration._2.title);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -250,7 +254,7 @@ public class ActionPane implements TemplateComposer {
|
|||
final PageAction switchAction = action.getSwitchAction();
|
||||
if (switchAction != null) {
|
||||
final PolyglotPageService polyglotPageService = this.pageService.getPolyglotPageService();
|
||||
polyglotPageService.injectI18n(treeItem, switchAction.definition.title);
|
||||
polyglotPageService.injectI18n(treeItem, switchAction.getTitle());
|
||||
treeItem.setImage(switchAction.definition.icon.getImage(treeItem.getDisplay()));
|
||||
treeItem.setData(ACTION_EVENT_CALL_KEY, switchAction);
|
||||
}
|
||||
|
|
|
@ -454,6 +454,7 @@ public interface PageService {
|
|||
private boolean fireActionEvent = true;
|
||||
private boolean ignoreMoveAwayFromEdit = false;
|
||||
private PageAction switchAction;
|
||||
private Object[] titleArgs;
|
||||
|
||||
protected PageActionBuilder(final PageService pageService, final PageContext pageContext) {
|
||||
this.pageService = pageService;
|
||||
|
@ -486,7 +487,8 @@ public interface PageService {
|
|||
exec,
|
||||
fireActionEvent,
|
||||
ignoreMoveAwayFromEdit,
|
||||
switchAction);
|
||||
switchAction,
|
||||
titleArgs);
|
||||
}
|
||||
|
||||
public PageActionBuilder publish() {
|
||||
|
@ -520,6 +522,11 @@ public interface PageService {
|
|||
return this;
|
||||
}
|
||||
|
||||
public PageActionBuilder withNameAttributes(final Object... attributes) {
|
||||
this.titleArgs = attributes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PageActionBuilder withSelectionSupplier(final Supplier<Set<EntityKey>> selectionSupplier) {
|
||||
this.selectionSupplier = selectionSupplier;
|
||||
return this;
|
||||
|
|
|
@ -43,6 +43,7 @@ public final class PageAction {
|
|||
final boolean fireActionEvent;
|
||||
final boolean ignoreMoveAwayFromEdit;
|
||||
private PageAction switchAction;
|
||||
final Object[] titleArgs;
|
||||
|
||||
final LocTextKey successMessage;
|
||||
|
||||
|
@ -56,7 +57,8 @@ public final class PageAction {
|
|||
final Function<PageAction, PageAction> exec,
|
||||
final boolean fireActionEvent,
|
||||
final boolean ignoreMoveAwayFromEdit,
|
||||
final PageAction switchAction) {
|
||||
final PageAction switchAction,
|
||||
final Object[] titleArgs) {
|
||||
|
||||
this.definition = definition;
|
||||
this.confirm = confirm;
|
||||
|
@ -68,6 +70,7 @@ public final class PageAction {
|
|||
this.fireActionEvent = fireActionEvent;
|
||||
this.ignoreMoveAwayFromEdit = ignoreMoveAwayFromEdit;
|
||||
this.switchAction = switchAction;
|
||||
this.titleArgs = titleArgs;
|
||||
if (this.switchAction != null) {
|
||||
this.switchAction.switchAction = this;
|
||||
}
|
||||
|
@ -91,6 +94,14 @@ public final class PageAction {
|
|||
return Constants.EMPTY_NOTE;
|
||||
}
|
||||
|
||||
public LocTextKey getTitle() {
|
||||
if (this.titleArgs != null) {
|
||||
return this.definition.getTitle(this.titleArgs);
|
||||
} else {
|
||||
return this.definition.title;
|
||||
}
|
||||
}
|
||||
|
||||
public PageAction getSwitchAction() {
|
||||
return this.switchAction;
|
||||
}
|
||||
|
@ -277,7 +288,8 @@ public final class PageAction {
|
|||
source.exec,
|
||||
source.fireActionEvent,
|
||||
source.ignoreMoveAwayFromEdit,
|
||||
source.switchAction);
|
||||
source.switchAction,
|
||||
source.titleArgs);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class GetProctorRoomConnectionData extends RestCall<SEBProctoringConnectionData> {
|
||||
|
||||
public GetProctorRoomConnectionData() {
|
||||
super(new TypeKey<>(
|
||||
CallType.GET_SINGLE,
|
||||
EntityType.EXAM_PROCTOR_DATA,
|
||||
new TypeReference<SEBProctoringConnectionData>() {
|
||||
}),
|
||||
HttpMethod.GET,
|
||||
MediaType.APPLICATION_FORM_URLENCODED,
|
||||
API.EXAM_MONITORING_ENDPOINT
|
||||
+ API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.PROCTOR_PATH_SEGMENT);
|
||||
}
|
||||
|
||||
}
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
|
@ -17,27 +19,27 @@ import com.fasterxml.jackson.core.type.TypeReference;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class GetProctorDataForSEBClient extends RestCall<SEBClientProctoringConnectionData> {
|
||||
public class SEBClientsJoinProctorRoom extends RestCall<List<SEBProctoringConnectionData>> {
|
||||
|
||||
public GetProctorDataForSEBClient() {
|
||||
public SEBClientsJoinProctorRoom() {
|
||||
super(new TypeKey<>(
|
||||
CallType.GET_SINGLE,
|
||||
EntityType.EXAM_PROCTOR_DATA,
|
||||
new TypeReference<SEBClientProctoringConnectionData>() {
|
||||
new TypeReference<List<SEBProctoringConnectionData>>() {
|
||||
}),
|
||||
HttpMethod.GET,
|
||||
HttpMethod.POST,
|
||||
MediaType.APPLICATION_FORM_URLENCODED,
|
||||
API.EXAM_MONITORING_ENDPOINT
|
||||
+ API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.PROCTOR_PATH_SEGMENT
|
||||
+ API.EXAM_MONITORING_SEB_CONNECTION_TOKEN_PATH_SEGMENT);
|
||||
+ API.PROCTOR_JOIN_ROOM_PATH_SEGMENT);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class SEBClientsLeaveProctorRoom extends RestCall<List<SEBProctoringConnectionData>> {
|
||||
|
||||
public SEBClientsLeaveProctorRoom() {
|
||||
super(new TypeKey<>(
|
||||
CallType.GET_SINGLE,
|
||||
EntityType.EXAM_PROCTOR_DATA,
|
||||
new TypeReference<List<SEBProctoringConnectionData>>() {
|
||||
}),
|
||||
HttpMethod.POST,
|
||||
MediaType.APPLICATION_FORM_URLENCODED,
|
||||
API.EXAM_MONITORING_ENDPOINT
|
||||
+ API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.PROCTOR_PATH_SEGMENT
|
||||
+ API.PROCTOR_LEAVE_ROOM_PATH_SEGMENT);
|
||||
}
|
||||
}
|
|
@ -34,6 +34,8 @@ import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
|||
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService;
|
||||
|
||||
@Component
|
||||
@GuiProfile
|
||||
|
@ -46,10 +48,19 @@ public class CurrentUser {
|
|||
private SEBServerAuthorizationContext authContext = null;
|
||||
private Map<RoleTypeKey, Privilege> privileges = null;
|
||||
private final Map<String, String> attributes;
|
||||
private final ProctoringGUIService proctoringGUIService;
|
||||
|
||||
public CurrentUser(
|
||||
final AuthorizationContextHolder authorizationContextHolder,
|
||||
final RestService restService) {
|
||||
|
||||
public CurrentUser(final AuthorizationContextHolder authorizationContextHolder) {
|
||||
this.authorizationContextHolder = authorizationContextHolder;
|
||||
this.attributes = new HashMap<>();
|
||||
this.proctoringGUIService = new ProctoringGUIService(restService);
|
||||
}
|
||||
|
||||
public ProctoringGUIService getProctoringGUIService() {
|
||||
return this.proctoringGUIService;
|
||||
}
|
||||
|
||||
public void putAttribute(final String name, final String value) {
|
||||
|
@ -179,6 +190,7 @@ public class CurrentUser {
|
|||
this.attributes.clear();
|
||||
}
|
||||
|
||||
this.proctoringGUIService.clear();
|
||||
this.privileges = null;
|
||||
|
||||
if (isAvailable()) {
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.gui.service.session;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.tomcat.util.buf.StringUtils;
|
||||
import org.eclipse.rap.rwt.RWT;
|
||||
import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.SEBClientsJoinProctorRoom;
|
||||
|
||||
public class ProctoringGUIService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ProctoringGUIService.class);
|
||||
|
||||
private static final String CLOSE_ROOM_SCRIPT = "var existingWin = window.open('', '%s'); existingWin.close()";
|
||||
|
||||
private final AtomicInteger counter = new AtomicInteger(1);
|
||||
private final RestService restService;
|
||||
|
||||
final Set<String> openWindows = new HashSet<>();
|
||||
final Map<String, RoomConnectionData> rooms = new HashMap<>();
|
||||
|
||||
public ProctoringGUIService(final RestService restService) {
|
||||
this.restService = restService;
|
||||
}
|
||||
|
||||
public String createNewRoomName() {
|
||||
return "Room-" + this.counter.getAndIncrement();
|
||||
}
|
||||
|
||||
public void registerProctoringWindow(final String window) {
|
||||
this.openWindows.add(window);
|
||||
}
|
||||
|
||||
public Set<String> roomNames() {
|
||||
return this.rooms.keySet();
|
||||
}
|
||||
|
||||
public void registerNewProcotringRoom(
|
||||
final String examId,
|
||||
final String roomName,
|
||||
final Collection<String> connectionTokens) {
|
||||
|
||||
final List<SEBProctoringConnectionData> connections =
|
||||
this.restService.getBuilder(SEBClientsJoinProctorRoom.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, examId)
|
||||
.withFormParam(SEBProctoringConnectionData.ATTR_ROOM_NAME, roomName)
|
||||
.withFormParam(
|
||||
API.EXAM_API_SEB_CONNECTION_TOKEN,
|
||||
StringUtils.join(connectionTokens, Constants.LIST_SEPARATOR_CHAR))
|
||||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
this.rooms.put(roomName, new RoomConnectionData(roomName, examId, connections));
|
||||
this.openWindows.add(roomName);
|
||||
|
||||
}
|
||||
|
||||
public void addConnectionsToRoom(
|
||||
final String examId,
|
||||
final String room,
|
||||
final Collection<String> connectionTokens) {
|
||||
|
||||
if (this.rooms.containsKey(room)) {
|
||||
final RoomConnectionData roomConnectionData = this.rooms.get(room);
|
||||
if (!roomConnectionData.examId.equals(examId) || !roomConnectionData.roomName.equals(room)) {
|
||||
throw new IllegalArgumentException("Exam identifier mismatch");
|
||||
}
|
||||
final List<SEBProctoringConnectionData> newConnections =
|
||||
this.restService.getBuilder(SEBClientsJoinProctorRoom.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, examId)
|
||||
.withFormParam(SEBProctoringConnectionData.ATTR_ROOM_NAME, room)
|
||||
.withFormParam(
|
||||
API.EXAM_API_SEB_CONNECTION_TOKEN,
|
||||
StringUtils.join(connectionTokens, Constants.LIST_SEPARATOR_CHAR))
|
||||
.call()
|
||||
.getOrThrow();
|
||||
roomConnectionData.connections.addAll(newConnections);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeConnectionsFromRoom(
|
||||
final String examId,
|
||||
final String room,
|
||||
final Collection<String> connectionTokens) {
|
||||
|
||||
}
|
||||
|
||||
public Result<List<SEBProctoringConnectionData>> closeRoom(final String name) {
|
||||
return Result.tryCatch(() -> {
|
||||
closeWindow(name);
|
||||
final RoomConnectionData roomConnectionData = this.rooms.remove(name);
|
||||
if (roomConnectionData != null) {
|
||||
// first send instruction to leave this room and join the personal single room
|
||||
final String connectionsString = StringUtils.join(
|
||||
roomConnectionData.connections
|
||||
.stream()
|
||||
.map(c -> c.connectionToken)
|
||||
.collect(Collectors.toList()),
|
||||
Constants.LIST_SEPARATOR_CHAR);
|
||||
|
||||
// NOTE: uncomment this if we need to send first a LEAVE instruction before sending the JOIN instruction for the single room
|
||||
// this.restService.getBuilder(LeaveProctorRoom.class)
|
||||
// .withURIVariable(API.PARAM_MODEL_ID, roomConnectionData.examId)
|
||||
// .withFormParam(SEBProctoringConnectionData.ATTR_ROOM_NAME, name)
|
||||
// .withFormParam(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionsString)
|
||||
// .call()
|
||||
// .getOrThrow();
|
||||
|
||||
return this.restService.getBuilder(SEBClientsJoinProctorRoom.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, roomConnectionData.examId)
|
||||
.withFormParam(SEBProctoringConnectionData.ATTR_ROOM_NAME, name)
|
||||
.withFormParam(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionsString)
|
||||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
});
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
|
||||
if (!this.rooms.isEmpty()) {
|
||||
this.rooms.keySet().stream().forEach(this::closeRoom);
|
||||
this.rooms.clear();
|
||||
}
|
||||
|
||||
if (!this.openWindows.isEmpty()) {
|
||||
|
||||
this.openWindows.stream()
|
||||
.forEach(room -> closeWindow(room));
|
||||
this.openWindows.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void closeWindow(final String room) {
|
||||
try {
|
||||
RWT.getClient().getService(JavaScriptExecutor.class)
|
||||
.execute(String.format(CLOSE_ROOM_SCRIPT, room));
|
||||
} catch (final Exception e) {
|
||||
log.info("Failed to close opened proctoring window: {}", room);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RoomConnectionData {
|
||||
final String roomName;
|
||||
final String examId;
|
||||
final Collection<SEBProctoringConnectionData> connections;
|
||||
|
||||
protected RoomConnectionData(
|
||||
final String roomName,
|
||||
final String examId,
|
||||
final Collection<SEBProctoringConnectionData> connections) {
|
||||
|
||||
this.roomName = roomName;
|
||||
this.examId = examId;
|
||||
this.connections = connections != null ? new ArrayList<>(connections) : new ArrayList<>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -10,8 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.exam;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ServerType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
||||
public interface ExamProctoringService {
|
||||
|
@ -20,19 +19,21 @@ public interface ExamProctoringService {
|
|||
|
||||
Result<Boolean> testExamProctoring(final ProctoringSettings examProctoring);
|
||||
|
||||
public Result<SEBClientProctoringConnectionData> createProctoringConnectionData(
|
||||
Result<SEBProctoringConnectionData> createProctorPrivateRoomConnection(
|
||||
final ProctoringSettings examProctoring,
|
||||
final String connectionToken);
|
||||
|
||||
Result<SEBProctoringConnectionData> createProctorPublicRoomConnection(
|
||||
final ProctoringSettings examProctoring,
|
||||
final String roomName);
|
||||
|
||||
Result<SEBProctoringConnectionData> createClientPrivateRoomConnection(
|
||||
final ProctoringSettings examProctoring,
|
||||
final String connectionToken);
|
||||
|
||||
Result<SEBProctoringConnectionData> createClientPublicRoomConnection(
|
||||
final ProctoringSettings examProctoring,
|
||||
final String connectionToken,
|
||||
final boolean server);
|
||||
|
||||
Result<SEBClientProctoringConnectionData> createProctoringConnectionData(
|
||||
final ProctoringSettings examProctoring,
|
||||
ClientConnection clientConnection,
|
||||
boolean server);
|
||||
|
||||
Result<SEBClientProctoringConnectionData> createProcotringDataForRoom(
|
||||
final ProctoringSettings examProctoring,
|
||||
final String roomName,
|
||||
final boolean server);
|
||||
final String roomName);
|
||||
|
||||
}
|
||||
|
|
|
@ -25,8 +25,8 @@ import ch.ethz.seb.sebserver.gbl.Constants;
|
|||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ServerType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
@ -72,64 +72,101 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result<SEBClientProctoringConnectionData> createProctoringConnectionData(
|
||||
public Result<SEBProctoringConnectionData> createProctorPrivateRoomConnection(
|
||||
final ProctoringSettings examProctoring,
|
||||
final String connectionToken,
|
||||
final boolean server) {
|
||||
final String connectionToken) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
return createProctoringConnectionData(
|
||||
examProctoring,
|
||||
this.examSessionService
|
||||
.getConnectionData(connectionToken)
|
||||
.getOrThrow().clientConnection,
|
||||
server)
|
||||
.getOrThrow();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<SEBClientProctoringConnectionData> createProctoringConnectionData(
|
||||
final ProctoringSettings examProctoring,
|
||||
final ClientConnection clientConnection,
|
||||
final boolean server) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
final ClientConnectionData clientConnection = this.examSessionService.getConnectionData(connectionToken)
|
||||
.getOrThrow();
|
||||
|
||||
final long expTime = forExam(examProctoring);
|
||||
final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding();
|
||||
final String roomName = urlEncoder.encodeToString(
|
||||
Utils.toByteArray(clientConnection.connectionToken));
|
||||
Utils.toByteArray(clientConnection.clientConnection.connectionToken));
|
||||
|
||||
return createProctoringConnectionData(
|
||||
connectionToken,
|
||||
examProctoring.serverURL,
|
||||
examProctoring.appKey,
|
||||
examProctoring.getAppSecret(),
|
||||
this.authorizationService.getUserService().getCurrentUser().getUsername(),
|
||||
(server) ? "seb-server" : "seb-client",
|
||||
"seb-server",
|
||||
roomName,
|
||||
clientConnection.userSessionId,
|
||||
clientConnection.clientConnection.userSessionId,
|
||||
expTime)
|
||||
.getOrThrow();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<SEBClientProctoringConnectionData> createProcotringDataForRoom(
|
||||
public Result<SEBProctoringConnectionData> createProctorPublicRoomConnection(
|
||||
final ProctoringSettings examProctoring,
|
||||
final String roomName,
|
||||
final boolean server) {
|
||||
final String roomName) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
return createProctoringConnectionData(
|
||||
null,
|
||||
examProctoring.serverURL,
|
||||
examProctoring.appKey,
|
||||
examProctoring.getAppSecret(),
|
||||
this.authorizationService.getUserService().getCurrentUser().getUsername(),
|
||||
"seb-server",
|
||||
roomName,
|
||||
roomName,
|
||||
forExam(examProctoring))
|
||||
.getOrThrow();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<SEBProctoringConnectionData> createClientPrivateRoomConnection(
|
||||
final ProctoringSettings examProctoring,
|
||||
final String connectionToken) {
|
||||
|
||||
// TODO Auto-generated method stub
|
||||
return Result.tryCatch(() -> {
|
||||
final ClientConnectionData clientConnection = this.examSessionService.getConnectionData(connectionToken)
|
||||
.getOrThrow();
|
||||
|
||||
final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding();
|
||||
final String roomName = urlEncoder.encodeToString(
|
||||
Utils.toByteArray(clientConnection.clientConnection.connectionToken));
|
||||
|
||||
return createProctoringConnectionData(
|
||||
null,
|
||||
examProctoring.serverURL,
|
||||
examProctoring.appKey,
|
||||
examProctoring.getAppSecret(),
|
||||
clientConnection.clientConnection.userSessionId,
|
||||
"seb-server",
|
||||
roomName,
|
||||
clientConnection.clientConnection.userSessionId,
|
||||
forExam(examProctoring))
|
||||
.getOrThrow();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<SEBProctoringConnectionData> createClientPublicRoomConnection(
|
||||
final ProctoringSettings examProctoring,
|
||||
final String connectionToken,
|
||||
final String roomName) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
final long expTime = forExam(examProctoring);
|
||||
|
||||
final ClientConnectionData connectionData = this.examSessionService.getConnectionData(connectionToken)
|
||||
.getOrThrow();
|
||||
|
||||
return createProctoringConnectionData(
|
||||
connectionToken,
|
||||
examProctoring.serverURL,
|
||||
examProctoring.appKey,
|
||||
examProctoring.getAppSecret(),
|
||||
this.authorizationService.getUserService().getCurrentUser().getUsername(),
|
||||
(server) ? "seb-server" : "seb-client",
|
||||
connectionData.clientConnection.userSessionId,
|
||||
"seb-client",
|
||||
roomName,
|
||||
roomName,
|
||||
expTime)
|
||||
|
@ -138,7 +175,8 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
|
|||
|
||||
}
|
||||
|
||||
public Result<SEBClientProctoringConnectionData> createProctoringConnectionData(
|
||||
public Result<SEBProctoringConnectionData> createProctoringConnectionData(
|
||||
final String connectionToken,
|
||||
final String url,
|
||||
final String appKey,
|
||||
final CharSequence appSecret,
|
||||
|
@ -165,18 +203,13 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
|
|||
expTime,
|
||||
host);
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final String connectionURL = builder.append(roomUrl)
|
||||
.append("?jwt=")
|
||||
.append(token).toString();
|
||||
|
||||
return new SEBClientProctoringConnectionData(
|
||||
return new SEBProctoringConnectionData(
|
||||
connectionToken,
|
||||
host,
|
||||
roomUrl,
|
||||
roomName,
|
||||
subject,
|
||||
token,
|
||||
connectionURL);
|
||||
token);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
|||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ServerType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||
|
@ -360,12 +360,11 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
|||
.getExamProctoring(clientConnection.examId)
|
||||
.getOrThrow();
|
||||
|
||||
final SEBClientProctoringConnectionData proctoringData =
|
||||
final SEBProctoringConnectionData proctoringData =
|
||||
this.examAdminService.getExamProctoringService(proctoringSettings.serverType)
|
||||
.flatMap(s -> s.createProctoringConnectionData(
|
||||
.flatMap(s -> s.createClientPrivateRoomConnection(
|
||||
proctoringSettings,
|
||||
clientConnection.connectionToken,
|
||||
false))
|
||||
clientConnection.connectionToken))
|
||||
.getOrThrow();
|
||||
|
||||
final Map<String, String> attributes = new HashMap<>();
|
||||
|
|
|
@ -234,6 +234,9 @@ public class SEBInstructionServiceImpl implements SEBInstructionService {
|
|||
}
|
||||
|
||||
private ClientInstructionRecord chacheInstruction(final ClientInstructionRecord instruction) {
|
||||
|
||||
System.out.println("************* register instruction: " + instruction);
|
||||
|
||||
final String connectionToken = instruction.getConnectionToken();
|
||||
if (this.instructions.containsKey(connectionToken)) {
|
||||
// check if previous instruction is still valid
|
||||
|
|
|
@ -11,11 +11,13 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
|
@ -43,7 +45,7 @@ import ch.ethz.seb.sebserver.gbl.model.Domain;
|
|||
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
|
||||
|
@ -281,32 +283,49 @@ public class ExamMonitoringController {
|
|||
|
||||
}
|
||||
|
||||
//***********************************************************************************************
|
||||
//**** Proctoring
|
||||
|
||||
@RequestMapping(
|
||||
path = API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.PROCTOR_PATH_SEGMENT
|
||||
+ API.EXAM_MONITORING_SEB_CONNECTION_TOKEN_PATH_SEGMENT,
|
||||
+ API.PROCTOR_PATH_SEGMENT,
|
||||
method = RequestMethod.GET,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public SEBClientProctoringConnectionData getClientSingleRoomProctoringData(
|
||||
public SEBProctoringConnectionData getProctorSingleRoom(
|
||||
@RequestParam(
|
||||
name = API.PARAM_INSTITUTION_ID,
|
||||
required = true,
|
||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||
@PathVariable(name = API.PARAM_MODEL_ID) final Long examId,
|
||||
@PathVariable(name = API.EXAM_API_SEB_CONNECTION_TOKEN) final String connectionToken) {
|
||||
@RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = false) final String connectionToken,
|
||||
@RequestParam(name = SEBProctoringConnectionData.ATTR_ROOM_NAME, required = false) final String roomName) {
|
||||
|
||||
this.authorization.check(
|
||||
PrivilegeType.READ,
|
||||
EntityType.EXAM,
|
||||
institutionId);
|
||||
|
||||
return this.examSessionService.getRunningExam(examId)
|
||||
.flatMap(this.authorization::checkRead)
|
||||
.flatMap(this.examAdminService::getExamProctoring)
|
||||
.flatMap(proc -> this.examAdminService
|
||||
.getExamProctoringService(proc.serverType)
|
||||
.flatMap(s -> s.createProctoringConnectionData(proc, connectionToken, true)))
|
||||
.getOrThrow();
|
||||
if (StringUtils.isNotBlank(connectionToken)) {
|
||||
return this.examSessionService.getRunningExam(examId)
|
||||
.flatMap(this.authorization::checkRead)
|
||||
.flatMap(this.examAdminService::getExamProctoring)
|
||||
.flatMap(proc -> this.examAdminService
|
||||
.getExamProctoringService(proc.serverType)
|
||||
.flatMap(s -> s.createProctorPrivateRoomConnection(proc, connectionToken)))
|
||||
.getOrThrow();
|
||||
} else if (StringUtils.isNotBlank(roomName)) {
|
||||
|
||||
return this.examSessionService.getRunningExam(examId)
|
||||
.flatMap(this.authorization::checkRead)
|
||||
.flatMap(this.examAdminService::getExamProctoring)
|
||||
.flatMap(proc -> this.examAdminService
|
||||
.getExamProctoringService(proc.serverType)
|
||||
.flatMap(s -> s.createProctorPublicRoomConnection(proc, roomName)))
|
||||
.getOrThrow();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
|
@ -315,62 +334,14 @@ public class ExamMonitoringController {
|
|||
+ API.PROCTOR_JOIN_ROOM_PATH_SEGMENT,
|
||||
method = RequestMethod.POST,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public SEBClientProctoringConnectionData joinProctoringRoom(
|
||||
public List<SEBProctoringConnectionData> joinProctoringRoom(
|
||||
@RequestParam(
|
||||
name = API.PARAM_INSTITUTION_ID,
|
||||
required = true,
|
||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||
@PathVariable(name = API.PARAM_MODEL_ID) final Long examId,
|
||||
@RequestParam(
|
||||
name = SEBClientProctoringConnectionData.ATTR_ROOM_NAME,
|
||||
required = true) final String roomName,
|
||||
@RequestParam(
|
||||
name = API.EXAM_API_SEB_CONNECTION_TOKEN,
|
||||
required = true) final String connectionTokens) {
|
||||
|
||||
this.authorization.check(
|
||||
PrivilegeType.READ,
|
||||
EntityType.EXAM,
|
||||
institutionId);
|
||||
|
||||
final ProctoringSettings settings = this.examSessionService
|
||||
.getRunningExam(examId)
|
||||
.flatMap(this.authorization::checkRead)
|
||||
.flatMap(this.examAdminService::getExamProctoring)
|
||||
.getOrThrow();
|
||||
|
||||
final SEBClientProctoringConnectionData result = this.examAdminService
|
||||
.getExamProctoringService(settings.serverType)
|
||||
.flatMap(s -> s.createProcotringDataForRoom(settings, roomName, false))
|
||||
.getOrThrow();
|
||||
|
||||
if (StringUtils.isNotBlank(connectionTokens)) {
|
||||
(connectionTokens.contains(Constants.LIST_SEPARATOR)
|
||||
? Arrays.asList(StringUtils.split(connectionTokens, Constants.LIST_SEPARATOR))
|
||||
: Arrays.asList(connectionTokens)).stream()
|
||||
.forEach(connectionToken -> sendJoinInstruction(examId, connectionToken, result)
|
||||
.onError(error -> log.error(
|
||||
"Failed to send proctoring leave instruction to client: {} ",
|
||||
connectionToken, error)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.PROCTOR_PATH_SEGMENT
|
||||
+ API.PROCTOR_LEAVE_ROOM_PATH_SEGMENT,
|
||||
method = RequestMethod.POST,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public void leaveProctoringRoom(
|
||||
@RequestParam(
|
||||
name = API.PARAM_INSTITUTION_ID,
|
||||
required = true,
|
||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||
@PathVariable(name = API.PARAM_MODEL_ID) final Long examId,
|
||||
@RequestParam(
|
||||
name = SEBClientProctoringConnectionData.ATTR_ROOM_NAME,
|
||||
name = SEBProctoringConnectionData.ATTR_ROOM_NAME,
|
||||
required = true) final String roomName,
|
||||
@RequestParam(
|
||||
name = API.EXAM_API_SEB_CONNECTION_TOKEN,
|
||||
|
@ -391,17 +362,85 @@ public class ExamMonitoringController {
|
|||
.getExamProctoringService(settings.serverType)
|
||||
.getOrThrow();
|
||||
|
||||
(connectionTokens.contains(Constants.LIST_SEPARATOR)
|
||||
? Arrays.asList(StringUtils.split(connectionTokens, Constants.LIST_SEPARATOR))
|
||||
: Arrays.asList(connectionTokens)).stream()
|
||||
.forEach(connectionToken -> examProctoringService
|
||||
.createProctoringConnectionData(settings, connectionToken, false)
|
||||
.flatMap(data -> sendLeaveInstruction(examId, connectionToken, data))
|
||||
.onError(error -> log.error(
|
||||
"Failed to send proctoring leave instruction to client: {} ",
|
||||
connectionToken, error)));
|
||||
if (StringUtils.isNotBlank(connectionTokens)) {
|
||||
return (connectionTokens.contains(Constants.LIST_SEPARATOR)
|
||||
? Arrays.asList(StringUtils.split(connectionTokens, Constants.LIST_SEPARATOR))
|
||||
: Arrays.asList(connectionTokens))
|
||||
.stream()
|
||||
.map(connectionToken -> {
|
||||
final SEBProctoringConnectionData data = examProctoringService
|
||||
.createClientPublicRoomConnection(settings, connectionToken, roomName)
|
||||
.getOrThrow();
|
||||
sendJoinInstruction(examId, connectionToken, data)
|
||||
.onError(error -> log.error(
|
||||
"Failed to send proctoring leave instruction to client: {} ",
|
||||
connectionToken, error));
|
||||
return data;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.PROCTOR_PATH_SEGMENT
|
||||
+ API.PROCTOR_LEAVE_ROOM_PATH_SEGMENT,
|
||||
method = RequestMethod.POST,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public List<SEBProctoringConnectionData> leaveProctoringRoom(
|
||||
@RequestParam(
|
||||
name = API.PARAM_INSTITUTION_ID,
|
||||
required = true,
|
||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||
@PathVariable(name = API.PARAM_MODEL_ID) final Long examId,
|
||||
@RequestParam(
|
||||
name = SEBProctoringConnectionData.ATTR_ROOM_NAME,
|
||||
required = true) final String roomName,
|
||||
@RequestParam(
|
||||
name = API.EXAM_API_SEB_CONNECTION_TOKEN,
|
||||
required = true) final String connectionTokens) {
|
||||
|
||||
this.authorization.check(
|
||||
PrivilegeType.READ,
|
||||
EntityType.EXAM,
|
||||
institutionId);
|
||||
|
||||
final ProctoringSettings settings = this.examSessionService
|
||||
.getRunningExam(examId)
|
||||
.flatMap(this.authorization::checkRead)
|
||||
.flatMap(this.examAdminService::getExamProctoring)
|
||||
.getOrThrow();
|
||||
|
||||
final ExamProctoringService examProctoringService = this.examAdminService
|
||||
.getExamProctoringService(settings.serverType)
|
||||
.getOrThrow();
|
||||
|
||||
if (StringUtils.isNotBlank(connectionTokens)) {
|
||||
return (connectionTokens.contains(Constants.LIST_SEPARATOR)
|
||||
? Arrays.asList(StringUtils.split(connectionTokens, Constants.LIST_SEPARATOR))
|
||||
: Arrays.asList(connectionTokens))
|
||||
.stream()
|
||||
.map(connectionToken -> {
|
||||
final SEBProctoringConnectionData data = examProctoringService
|
||||
.createClientPublicRoomConnection(settings, connectionToken, roomName)
|
||||
.getOrThrow();
|
||||
|
||||
sendLeaveInstruction(examId, connectionTokens, data)
|
||||
.onError(error -> log.error(
|
||||
"Failed to send proctoring leave instruction for common room to client: {} ",
|
||||
connectionToken, error));
|
||||
return data;
|
||||
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
//**** Proctoring
|
||||
//***********************************************************************************************
|
||||
|
||||
private boolean hasRunningExamPrivilege(final Long examId, final Long institution) {
|
||||
return hasRunningExamPrivilege(
|
||||
this.examSessionService.getRunningExam(examId).getOr(null),
|
||||
|
@ -420,7 +459,7 @@ public class ExamMonitoringController {
|
|||
private Result<Void> sendJoinInstruction(
|
||||
final Long examId,
|
||||
final String connectionToken,
|
||||
final SEBClientProctoringConnectionData data) {
|
||||
final SEBProctoringConnectionData data) {
|
||||
|
||||
return sendProctorInstruction(
|
||||
examId,
|
||||
|
@ -432,7 +471,7 @@ public class ExamMonitoringController {
|
|||
private Result<Void> sendLeaveInstruction(
|
||||
final Long examId,
|
||||
final String connectionToken,
|
||||
final SEBClientProctoringConnectionData data) {
|
||||
final SEBProctoringConnectionData data) {
|
||||
|
||||
return sendProctorInstruction(
|
||||
examId,
|
||||
|
@ -444,7 +483,7 @@ public class ExamMonitoringController {
|
|||
private Result<Void> sendProctorInstruction(
|
||||
final Long examId,
|
||||
final String connectionToken,
|
||||
final SEBClientProctoringConnectionData data,
|
||||
final SEBProctoringConnectionData data,
|
||||
final String method) {
|
||||
final Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put(
|
||||
|
|
|
@ -1428,6 +1428,11 @@ sebserver.monitoring.exam.list.title=Running Exams
|
|||
sebserver.monitoring.exam.list.actions=
|
||||
sebserver.monitoring.exam.action.detail.view=Back To Monitoring
|
||||
sebserver.monitoring.exam.action.list.view=Monitoring
|
||||
sebserver.monitoring.exam.action.newroom=New Proctor Room
|
||||
sebserver.monitoring.exam.action.viewroom=View Room {0}
|
||||
sebserver.monitoring.exam.action.closeroom=Close Room {0}
|
||||
sebserver.exam.monitoring.action.category.filter=Filter
|
||||
sebserver.exam.overall.action.category.proctoring=Proctoring
|
||||
|
||||
|
||||
sebserver.monitoring.exam.info.pleaseSelect=At first please select an Exam from the list
|
||||
|
|
|
@ -24,7 +24,7 @@ public class CurrentUserTest extends GuiIntegrationTest {
|
|||
public void testCurrentUserLoginAndGet() {
|
||||
final OAuth2AuthorizationContextHolder authorizationContextHolder = getAuthorizationContextHolder();
|
||||
|
||||
final CurrentUser currentUser = new CurrentUser(authorizationContextHolder);
|
||||
final CurrentUser currentUser = new CurrentUser(authorizationContextHolder, null);
|
||||
|
||||
// no user is logged in for now
|
||||
try {
|
||||
|
@ -51,7 +51,7 @@ public class CurrentUserTest extends GuiIntegrationTest {
|
|||
public void testCurrentUserPrivileges() {
|
||||
final OAuth2AuthorizationContextHolder authorizationContextHolder = getAuthorizationContextHolder();
|
||||
|
||||
final CurrentUser currentUser = new CurrentUser(authorizationContextHolder);
|
||||
final CurrentUser currentUser = new CurrentUser(authorizationContextHolder, null);
|
||||
// login as SEB Administrator
|
||||
authorizationContextHolder.getAuthorizationContext().login("admin", "admin");
|
||||
|
||||
|
@ -64,7 +64,7 @@ public class CurrentUserTest extends GuiIntegrationTest {
|
|||
@Test
|
||||
public void testCurrentUserLogin() {
|
||||
final OAuth2AuthorizationContextHolder authorizationContextHolder = login("admin", "admin");
|
||||
final CurrentUser currentUser = new CurrentUser(authorizationContextHolder);
|
||||
final CurrentUser currentUser = new CurrentUser(authorizationContextHolder, null);
|
||||
final UserInfo userInfo = currentUser.getOrHandleError(error -> {
|
||||
fail("expecting no error here");
|
||||
return null;
|
||||
|
|
|
@ -14,7 +14,7 @@ import static org.junit.Assert.assertNotNull;
|
|||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
|
||||
|
||||
public class ExamJITSIProctoringServiceTest {
|
||||
|
@ -25,7 +25,8 @@ public class ExamJITSIProctoringServiceTest {
|
|||
Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn("fbvgeghergrgrthrehreg123");
|
||||
final ExamJITSIProctoringService examJITSIProctoringService =
|
||||
new ExamJITSIProctoringService(null, null, cryptorMock);
|
||||
final SEBClientProctoringConnectionData data = examJITSIProctoringService.createProctoringConnectionData(
|
||||
final SEBProctoringConnectionData data = examJITSIProctoringService.createProctoringConnectionData(
|
||||
"connectionToken",
|
||||
"https://seb-jitsi.example.ch",
|
||||
"test-app",
|
||||
"fbvgeghergrgrthrehreg123",
|
||||
|
@ -43,9 +44,6 @@ public class ExamJITSIProctoringServiceTest {
|
|||
assertEquals(
|
||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7InVzZXIiOnsibmFtZSI6IlRlc3QgTmFtZSJ9fSwiaXNzIjoidGVzdC1hcHAiLCJhdWQiOiJ0ZXN0LWNsaWVudCIsInN1YiI6InNlYi1qaXRzaS5leGFtcGxlLmNoIiwicm9vbSI6IlNvbWVSb29tIiwiZXhwIjoxNjA5NDU5MjAwfQ.4ovqUkG6jrLvkDEZNdhbtFI_DFLDFsM2eBJHhcYq7a4",
|
||||
data.accessToken);
|
||||
assertEquals(
|
||||
"https://seb-jitsi.example.ch/SomeRoom?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7InVzZXIiOnsibmFtZSI6IlRlc3QgTmFtZSJ9fSwiaXNzIjoidGVzdC1hcHAiLCJhdWQiOiJ0ZXN0LWNsaWVudCIsInN1YiI6InNlYi1qaXRzaS5leGFtcGxlLmNoIiwicm9vbSI6IlNvbWVSb29tIiwiZXhwIjoxNjA5NDU5MjAwfQ.4ovqUkG6jrLvkDEZNdhbtFI_DFLDFsM2eBJHhcYq7a4",
|
||||
data.connectionURL);
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue