added more actions and connection list to proctor rooms
This commit is contained in:
parent
1d7d15c02c
commit
daebd9b5f7
7 changed files with 221 additions and 86 deletions
|
@ -84,4 +84,29 @@ public class RemoteProctoringRoom {
|
|||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
final RemoteProctoringRoom other = (RemoteProctoringRoom) obj;
|
||||
if (this.id == null) {
|
||||
if (other.id != null)
|
||||
return false;
|
||||
} else if (!this.id.equals(other.id))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.eclipse.swt.SWT;
|
|||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Tree;
|
||||
import org.eclipse.swt.widgets.TreeItem;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -90,6 +91,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
"}";
|
||||
// @formatter:on
|
||||
|
||||
private static final String SHOW_CONNECTION_ACTION_APPLIED = "SHOW_CONNECTION_ACTION_APPLIED";
|
||||
private static final LocTextKey EMPTY_SELECTION_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.exam.connection.emptySelection");
|
||||
private static final LocTextKey EMPTY_ACTIVE_SELECTION_TEXT_KEY =
|
||||
|
@ -108,12 +110,14 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
private final GuiServiceInfo guiServiceInfo;
|
||||
private final long pollInterval;
|
||||
private final String remoteProctoringEndpoint;
|
||||
private final ProctorRoomConnectionsPopup proctorRoomConnectionsPopup;
|
||||
|
||||
protected MonitoringRunningExam(
|
||||
final ServerPushService serverPushService,
|
||||
final PageService pageService,
|
||||
final InstructionProcessor instructionProcessor,
|
||||
final GuiServiceInfo guiServiceInfo,
|
||||
final ProctorRoomConnectionsPopup proctorRoomConnectionsPopup,
|
||||
@Value("${sebserver.gui.webservice.poll-interval:1000}") final long pollInterval,
|
||||
@Value("${sebserver.gui.remote.proctoring.entrypoint:/remote-proctoring}") final String remoteProctoringEndpoint) {
|
||||
|
||||
|
@ -124,6 +128,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
this.guiServiceInfo = guiServiceInfo;
|
||||
this.pollInterval = pollInterval;
|
||||
this.remoteProctoringEndpoint = remoteProctoringEndpoint;
|
||||
this.proctorRoomConnectionsPopup = proctorRoomConnectionsPopup;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -315,10 +320,11 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
.getOr(null);
|
||||
|
||||
if (proctoringSettings != null && proctoringSettings.enableProctoring) {
|
||||
final Map<String, TreeItem> availableRoomNames = new HashMap<>();
|
||||
final Map<RemoteProctoringRoom, TreeItem> availableRooms = new HashMap<>();
|
||||
updateRoomActions(
|
||||
entityKey,
|
||||
availableRoomNames,
|
||||
pageContext,
|
||||
availableRooms,
|
||||
actionBuilder,
|
||||
proctoringSettings);
|
||||
this.serverPushService.runServerPush(
|
||||
|
@ -326,7 +332,8 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
5000,
|
||||
context -> updateRoomActions(
|
||||
entityKey,
|
||||
availableRoomNames,
|
||||
pageContext,
|
||||
availableRooms,
|
||||
actionBuilder,
|
||||
proctoringSettings));
|
||||
}
|
||||
|
@ -334,7 +341,8 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
|
||||
private void updateRoomActions(
|
||||
final EntityKey entityKey,
|
||||
final Map<String, TreeItem> rooms,
|
||||
final PageContext pageContext,
|
||||
final Map<RemoteProctoringRoom, TreeItem> rooms,
|
||||
final PageActionBuilder actionBuilder,
|
||||
final ProctoringSettings proctoringSettings) {
|
||||
|
||||
|
@ -345,9 +353,9 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
.getOrThrow()
|
||||
.stream()
|
||||
.forEach(room -> {
|
||||
if (rooms.containsKey(room.name)) {
|
||||
if (rooms.containsKey(room)) {
|
||||
// update action
|
||||
final TreeItem treeItem = rooms.get(room.name);
|
||||
final TreeItem treeItem = rooms.get(room);
|
||||
treeItem.setText(i18nSupport.getText(new LocTextKey(
|
||||
ActionDefinition.MONITOR_EXAM_VIEW_PROCTOR_ROOM.title.name,
|
||||
room.subject,
|
||||
|
@ -367,10 +375,36 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
.noEventPropagation()
|
||||
.create();
|
||||
|
||||
this.pageService.publishAction(action, treeItem -> rooms.put(room.name, treeItem));
|
||||
this.pageService.publishAction(action, treeItem -> rooms.put(room, treeItem));
|
||||
|
||||
// check and add show connections in room action adaptation
|
||||
if (!rooms.isEmpty()) {
|
||||
final TreeItem treeItem = rooms.values().iterator().next();
|
||||
final Tree tree = treeItem.getParent();
|
||||
if (tree.getData(SHOW_CONNECTION_ACTION_APPLIED) == null) {
|
||||
tree.addListener(SWT.Selection, event -> {
|
||||
final TreeItem item = (TreeItem) event.item;
|
||||
treeItem.getParent().deselectAll();
|
||||
if (event.button == 3) {
|
||||
rooms.entrySet()
|
||||
.stream()
|
||||
.filter(e -> e.getValue().equals(item))
|
||||
.findFirst()
|
||||
.ifPresent(e -> {
|
||||
final PageContext pc = pageContext.copy()
|
||||
.clearAttributes()
|
||||
.withEntityKey(new EntityKey(e.getKey().getName(),
|
||||
EntityType.REMOTE_PROCTORING_ROOM))
|
||||
.withParentEntityKey(entityKey);
|
||||
this.proctorRoomConnectionsPopup.show(pc, e.getKey().getSubject());
|
||||
});
|
||||
}
|
||||
});
|
||||
tree.setData(SHOW_CONNECTION_ACTION_APPLIED, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private PageAction showExamProctoringRoom(
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.content;
|
||||
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetProctorRoomConnections;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class ProctorRoomConnectionsPopup {
|
||||
|
||||
private static final LocTextKey TITLE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.exam.proctoring.room.connections.title");
|
||||
|
||||
private final PageService pageService;
|
||||
|
||||
protected ProctorRoomConnectionsPopup(final PageService pageService) {
|
||||
this.pageService = pageService;
|
||||
}
|
||||
|
||||
public void show(final PageContext pageContext, final String roomSubject) {
|
||||
final ModalInputDialog<Void> dialog = new ModalInputDialog<>(
|
||||
pageContext.getParent().getShell(),
|
||||
this.pageService.getWidgetFactory());
|
||||
dialog.setLargeDialogWidth();
|
||||
dialog.open(
|
||||
new LocTextKey(TITLE_TEXT_KEY.name, roomSubject),
|
||||
pageContext,
|
||||
this::compose);
|
||||
}
|
||||
|
||||
private void compose(final PageContext pageContext) {
|
||||
final Composite parent = pageContext.getParent();
|
||||
final Composite grid = this.pageService.getWidgetFactory().createPopupScrollComposite(parent);
|
||||
final EntityKey entityKey = pageContext.getEntityKey();
|
||||
final EntityKey parentEntityKey = pageContext.getParentEntityKey();
|
||||
|
||||
this.pageService.getRestService().getBuilder(GetProctorRoomConnections.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, parentEntityKey.modelId)
|
||||
.withQueryParam(Domain.REMOTE_PROCTORING_ROOM.ATTR_ID, entityKey.modelId)
|
||||
.call()
|
||||
.getOrThrow()
|
||||
.stream()
|
||||
.forEach(connection -> {
|
||||
final Label label = new Label(grid, SWT.NONE);
|
||||
label.setText(connection.userSessionId);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -42,19 +42,19 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
|||
public class JitsiMeetProctoringView implements RemoteProctoringView {
|
||||
|
||||
private static final LocTextKey CLOSE_WINDOW_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.exam.action.close");
|
||||
new LocTextKey("sebserver.monitoring.exam.proctoring.action.close");
|
||||
private static final LocTextKey BROADCAST_AUDIO_ON_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.exam.action.broadcaston.audio");
|
||||
new LocTextKey("sebserver.monitoring.exam.proctoring.action.broadcaston.audio");
|
||||
private static final LocTextKey BROADCAST_AUDIO_OFF_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.exam.action.broadcastoff.audio");
|
||||
new LocTextKey("sebserver.monitoring.exam.proctoring.action.broadcastoff.audio");
|
||||
private static final LocTextKey BROADCAST_VIDEO_ON_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.exam.action.broadcaston.video");
|
||||
new LocTextKey("sebserver.monitoring.exam.proctoring.action.broadcaston.video");
|
||||
private static final LocTextKey BROADCAST_VIDEO_OFF_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.exam.action.broadcastoff.video");
|
||||
new LocTextKey("sebserver.monitoring.exam.proctoring.action.broadcastoff.video");
|
||||
private static final LocTextKey CHAT_ON_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.exam.action.broadcaston.chat");
|
||||
new LocTextKey("sebserver.monitoring.exam.proctoring.action.broadcaston.chat");
|
||||
private static final LocTextKey CHAT_OFF_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.exam.action.broadcastoff.chat");
|
||||
new LocTextKey("sebserver.monitoring.exam.proctoring.action.broadcastoff.chat");
|
||||
|
||||
private final PageService pageService;
|
||||
private final GuiServiceInfo guiServiceInfo;
|
||||
|
@ -132,7 +132,8 @@ public class JitsiMeetProctoringView implements RemoteProctoringView {
|
|||
broadcastVideoAction.addListener(SWT.Selection, event -> toggleBroadcastVideo(
|
||||
proctoringWindowData.examId,
|
||||
proctoringWindowData.connectionData.roomName,
|
||||
broadcastVideoAction));
|
||||
broadcastVideoAction,
|
||||
broadcastAudioAction));
|
||||
broadcastVideoAction.setData(BroadcastActionState.KEY_NAME, broadcastActionState);
|
||||
|
||||
final Button chatAction = widgetFactory.buttonLocalized(footer, CHAT_ON_TEXT_KEY);
|
||||
|
@ -172,24 +173,37 @@ public class JitsiMeetProctoringView implements RemoteProctoringView {
|
|||
state.audio = !state.audio;
|
||||
}
|
||||
|
||||
private void toggleBroadcastVideo(final String examId, final String roomName, final Button broadcastAction) {
|
||||
private void toggleBroadcastVideo(
|
||||
final String examId,
|
||||
final String roomName,
|
||||
final Button videoAction,
|
||||
final Button audioAction) {
|
||||
final BroadcastActionState state =
|
||||
(BroadcastActionState) broadcastAction.getData(BroadcastActionState.KEY_NAME);
|
||||
(BroadcastActionState) videoAction.getData(BroadcastActionState.KEY_NAME);
|
||||
if (state.video) {
|
||||
this.pageService.getPolyglotPageService().injectI18n(broadcastAction, BROADCAST_VIDEO_ON_TEXT_KEY);
|
||||
this.pageService.getPolyglotPageService().injectI18n(audioAction, BROADCAST_AUDIO_ON_TEXT_KEY);
|
||||
this.pageService.getPolyglotPageService().injectI18n(videoAction, BROADCAST_VIDEO_ON_TEXT_KEY);
|
||||
|
||||
this.pageService.getRestService().getBuilder(SendProctoringBroadcastOffInstruction.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, examId)
|
||||
.withFormParam(Domain.REMOTE_PROCTORING_ROOM.ATTR_ID, roomName)
|
||||
.withFormParam(
|
||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_RECONFIGURE_SETTINGS.JITSI_RECEIVE_AUDIO,
|
||||
Constants.TRUE_STRING)
|
||||
.withFormParam(
|
||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_RECONFIGURE_SETTINGS.JITSI_RECEIVE_VIDEO,
|
||||
Constants.TRUE_STRING)
|
||||
.call()
|
||||
.getOrThrow();
|
||||
} else {
|
||||
this.pageService.getPolyglotPageService().injectI18n(broadcastAction, BROADCAST_VIDEO_OFF_TEXT_KEY);
|
||||
this.pageService.getPolyglotPageService().injectI18n(audioAction, BROADCAST_AUDIO_OFF_TEXT_KEY);
|
||||
this.pageService.getPolyglotPageService().injectI18n(videoAction, BROADCAST_VIDEO_OFF_TEXT_KEY);
|
||||
this.pageService.getRestService().getBuilder(SendProctoringBroadcastOnInstruction.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, examId)
|
||||
.withFormParam(Domain.REMOTE_PROCTORING_ROOM.ATTR_ID, roomName)
|
||||
.withFormParam(
|
||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_RECONFIGURE_SETTINGS.JITSI_RECEIVE_AUDIO,
|
||||
Constants.TRUE_STRING)
|
||||
.withFormParam(
|
||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_RECONFIGURE_SETTINGS.JITSI_RECEIVE_VIDEO,
|
||||
Constants.TRUE_STRING)
|
||||
|
@ -197,6 +211,7 @@ public class JitsiMeetProctoringView implements RemoteProctoringView {
|
|||
.getOrThrow();
|
||||
}
|
||||
state.video = !state.video;
|
||||
state.audio = state.video;
|
||||
}
|
||||
|
||||
private void toggleChat(final String examId, final String roomName, final Button broadcastAction) {
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.Collection;
|
||||
|
||||
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.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class GetProctorRoomConnections extends RestCall<Collection<ClientConnection>> {
|
||||
|
||||
public GetProctorRoomConnections() {
|
||||
super(new TypeKey<>(
|
||||
CallType.GET_LIST,
|
||||
EntityType.CLIENT_CONNECTION,
|
||||
new TypeReference<Collection<ClientConnection>>() {
|
||||
}),
|
||||
HttpMethod.GET,
|
||||
MediaType.APPLICATION_FORM_URLENCODED,
|
||||
API.EXAM_MONITORING_ENDPOINT
|
||||
+ API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.PROCTORING_PATH_SEGMENT
|
||||
+ API.PROCTORING_ROOM_CONNECTIONS_PATH_SEGMENT);
|
||||
}
|
||||
|
||||
}
|
|
@ -360,7 +360,7 @@ public class ExamMonitoringController {
|
|||
@PathVariable(name = API.PARAM_MODEL_ID) final Long examId,
|
||||
@RequestParam(
|
||||
name = Domain.REMOTE_PROCTORING_ROOM.ATTR_ID,
|
||||
required = true) final Long roomId) {
|
||||
required = true) final String roomName) {
|
||||
|
||||
this.authorization.check(
|
||||
PrivilegeType.READ,
|
||||
|
@ -370,7 +370,7 @@ public class ExamMonitoringController {
|
|||
this.authorization.checkRead(
|
||||
this.examSessionService.getExamDAO().byPK(examId).getOrThrow());
|
||||
|
||||
return this.examProcotringRoomService.getRoomConnections(roomId)
|
||||
return this.examProcotringRoomService.getRoomConnections(examId, roomName)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
|
@ -639,62 +639,6 @@ public class ExamMonitoringController {
|
|||
.getOrThrow();
|
||||
}
|
||||
|
||||
// @RequestMapping(
|
||||
// path = API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
// + API.PROCTORING_PATH_SEGMENT
|
||||
// + API.PROCTORING_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, 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
|
||||
//***********************************************************************************************
|
||||
|
||||
|
|
|
@ -1437,17 +1437,19 @@ 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.close=Close Window
|
||||
sebserver.monitoring.exam.action.broadcaston.audio=Start Audio Broadcast
|
||||
sebserver.monitoring.exam.action.broadcastoff.audio=End Audio Broadcast
|
||||
sebserver.monitoring.exam.action.broadcaston.video=Start Video Broadcast
|
||||
sebserver.monitoring.exam.action.broadcastoff.video=End Video Broadcast
|
||||
sebserver.monitoring.exam.action.broadcaston.chat=Enable Chat
|
||||
sebserver.monitoring.exam.action.broadcastoff.chat=Disable Chat
|
||||
sebserver.monitoring.exam.action.viewroom=View {0} | {1} / {2}
|
||||
sebserver.monitoring.exam.action.viewroom=View {0} ( {1} / {2} )
|
||||
sebserver.exam.monitoring.action.category.filter=Filter
|
||||
sebserver.exam.overall.action.category.proctoring=Proctoring
|
||||
|
||||
sebserver.monitoring.exam.proctoring.action.close=Close Window
|
||||
sebserver.monitoring.exam.proctoring.action.broadcaston.audio=Start Audio Broadcast
|
||||
sebserver.monitoring.exam.proctoring.action.broadcastoff.audio=End Audio Broadcast
|
||||
sebserver.monitoring.exam.proctoring.action.broadcaston.video=Start Video Broadcast
|
||||
sebserver.monitoring.exam.proctoring.action.broadcastoff.video=End Video Broadcast
|
||||
sebserver.monitoring.exam.proctoring.action.broadcaston.chat=Enable Chat
|
||||
sebserver.monitoring.exam.proctoring.action.broadcastoff.chat=Disable Chat
|
||||
sebserver.monitoring.exam.proctoring.room.connections.title=SEB Connections in {0}
|
||||
|
||||
|
||||
sebserver.monitoring.exam.info.pleaseSelect=At first please select an Exam from the list
|
||||
sebserver.monitoring.exam.list.empty=There are currently no running exams
|
||||
|
|
Loading…
Add table
Reference in a new issue