SEBSERV-180 fixes
This commit is contained in:
parent
7a686c0e6e
commit
a172326fa5
10 changed files with 200 additions and 107 deletions
|
@ -382,7 +382,7 @@ public final class Result<T> {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
throw new RuntimeException("!!!!!!!!!!!!!");
|
||||
throw new RuntimeException("Result.toString is probably called by mistake !!!");
|
||||
//return "Result [value=" + this.value + ", error=" + this.error + "]";
|
||||
}
|
||||
|
||||
|
|
|
@ -371,15 +371,18 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
.call()
|
||||
.onError(error -> log.error("Failed to get ProctoringServiceSettings", error))
|
||||
.getOr(null);
|
||||
final ProctoringGUIService proctoringGUIService = currentUser.getProctoringGUIService();
|
||||
|
||||
if (procotringSettings != null && procotringSettings.enableProctoring) {
|
||||
final ProctoringGUIService proctoringGUIService = this.resourceService
|
||||
.getCurrentUser()
|
||||
.getProctoringGUIService();
|
||||
actionBuilder
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_PROCTORING)
|
||||
.withEntityKey(parentEntityKey)
|
||||
.withExec(action -> this.monitoringProctoringService.openOneToOneRoom(
|
||||
action,
|
||||
connectionData, proctoringGUIService))
|
||||
connectionData,
|
||||
proctoringGUIService))
|
||||
.noEventPropagation()
|
||||
.publish()
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService;
|
|||
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.TemplateComposer;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.event.ActionActivationEvent;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.event.ActionActivationEventListener;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEventListener;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener;
|
||||
|
@ -118,19 +119,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());
|
||||
actionItem.setImage(image);
|
||||
if (event.activation) {
|
||||
actionItem.setForeground(null);
|
||||
} else {
|
||||
actionItem.setForeground(new Color(parent.getDisplay(), new RGBA(150, 150, 150, 50)));
|
||||
ActionPane.this.pageService.getPolyglotPageService().injectI18n(
|
||||
actionItem,
|
||||
(action != null) ? action.getTitle() : ad.title);
|
||||
}
|
||||
de_activate_action_icon(event, parent, actionItem);
|
||||
}
|
||||
|
||||
if (event.decoration != null) {
|
||||
|
@ -141,10 +130,33 @@ public class ActionPane implements TemplateComposer {
|
|||
ActionPane.this.pageService.getPolyglotPageService().injectI18n(
|
||||
actionItemToDecorate, event.decoration._2.title);
|
||||
}
|
||||
|
||||
de_activate_action_icon(event, parent, actionItemToDecorate);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void de_activate_action_icon(
|
||||
final ActionActivationEvent event,
|
||||
final Composite parent,
|
||||
final TreeItem actionItemToDecorate) {
|
||||
|
||||
final PageAction action = (PageAction) actionItemToDecorate.getData(ACTION_EVENT_CALL_KEY);
|
||||
final Image image = event.activation
|
||||
? event.decoration._1.icon.getImage(parent.getDisplay())
|
||||
: event.decoration._1.icon.getGreyedImage(parent.getDisplay());
|
||||
actionItemToDecorate.setImage(image);
|
||||
if (event.activation) {
|
||||
actionItemToDecorate.setForeground(null);
|
||||
} else {
|
||||
actionItemToDecorate
|
||||
.setForeground(new Color(parent.getDisplay(), new RGBA(150, 150, 150, 50)));
|
||||
ActionPane.this.pageService.getPolyglotPageService().injectI18n(
|
||||
actionItemToDecorate,
|
||||
(action != null) ? action.getTitle() : event.decoration._1.title);
|
||||
}
|
||||
}
|
||||
|
||||
private TreeItem findAction(
|
||||
final Map<String, Tree> actionTrees,
|
||||
final Composite parent,
|
||||
|
|
|
@ -44,6 +44,7 @@ public class PageContextImpl implements PageContext {
|
|||
|
||||
private static final Logger log = LoggerFactory.getLogger(PageContextImpl.class);
|
||||
|
||||
private static final LocTextKey UNEXPECTED_ERROR = new LocTextKey("sebserver.error.unexpected");
|
||||
private static final String ENTITY_LIST_TYPE = null;
|
||||
|
||||
private final I18nSupport i18nSupport;
|
||||
|
@ -312,6 +313,18 @@ public class PageContextImpl implements PageContext {
|
|||
@Override
|
||||
public void notifyError(final LocTextKey message, final Exception error) {
|
||||
|
||||
if (error == null) {
|
||||
final MessageBox messageBox = new Message(
|
||||
getShell(),
|
||||
this.i18nSupport.getText(UNEXPECTED_ERROR),
|
||||
this.i18nSupport.getText(message),
|
||||
SWT.ERROR,
|
||||
this.i18nSupport);
|
||||
messageBox.setMarkupEnabled(true);
|
||||
messageBox.open(null);
|
||||
return;
|
||||
}
|
||||
|
||||
log.error("Unexpected GUI error notified: {}", error.getMessage());
|
||||
|
||||
final String errorMessage = message != null
|
||||
|
|
|
@ -59,6 +59,19 @@ public class MonitoringProctoringService {
|
|||
|
||||
private static final Logger log = LoggerFactory.getLogger(MonitoringProctoringService.class);
|
||||
|
||||
public static final LocTextKey OPEN_TOWNHALL_ERROR =
|
||||
new LocTextKey("sebserver.exam.proctoring.townhall.open.error");
|
||||
public static final LocTextKey CLOSE_TOWNHALL_ERROR =
|
||||
new LocTextKey("sebserver.exam.proctoring.townhall.close.error");
|
||||
public static final LocTextKey OPEN_ONE_ERROR =
|
||||
new LocTextKey("sebserver.exam.proctoring.one.open.error");
|
||||
public static final LocTextKey CLOSE_ONE_ERROR =
|
||||
new LocTextKey("sebserver.exam.proctoring.one.close.error");
|
||||
public static final LocTextKey OPEN_COLLECTING_ERROR =
|
||||
new LocTextKey("sebserver.exam.proctoring.collecting.open.error");
|
||||
public static final LocTextKey CLOSE_COLLECTING_ERROR =
|
||||
new LocTextKey("sebserver.exam.proctoring.collecting.close.error");
|
||||
|
||||
private static final LocTextKey EXAM_ROOM_NAME =
|
||||
new LocTextKey("sebserver.monitoring.exam.proctoring.room.all.name");
|
||||
|
||||
|
@ -109,26 +122,32 @@ public class MonitoringProctoringService {
|
|||
final PageAction action) {
|
||||
|
||||
if (isTownhallRoomActive(action.getEntityKey().modelId)) {
|
||||
closeTownhallRoom(proctoringGUIService, action);
|
||||
this.pageService.firePageEvent(
|
||||
new ActionActivationEvent(
|
||||
true,
|
||||
new Tuple<>(
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM)),
|
||||
action.pageContext());
|
||||
return action;
|
||||
if (closeTownhallRoom(proctoringGUIService, action)) {
|
||||
this.pageService.firePageEvent(
|
||||
new ActionActivationEvent(
|
||||
true,
|
||||
new Tuple<>(
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM)),
|
||||
action.pageContext());
|
||||
} else {
|
||||
action.pageContext().notifyError(CLOSE_TOWNHALL_ERROR, null);
|
||||
}
|
||||
|
||||
} else {
|
||||
openTownhallRoom(proctoringGUIService, action);
|
||||
this.pageService.firePageEvent(
|
||||
new ActionActivationEvent(
|
||||
true,
|
||||
new Tuple<>(
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
|
||||
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)),
|
||||
action.pageContext());
|
||||
return action;
|
||||
if (openTownhallRoom(proctoringGUIService, action)) {
|
||||
this.pageService.firePageEvent(
|
||||
new ActionActivationEvent(
|
||||
true,
|
||||
new Tuple<>(
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
|
||||
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)),
|
||||
action.pageContext());
|
||||
} else {
|
||||
action.pageContext().notifyError(OPEN_TOWNHALL_ERROR, null);
|
||||
}
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
public void initCollectingRoomActions(
|
||||
|
@ -219,50 +238,56 @@ public class MonitoringProctoringService {
|
|||
final PageAction action,
|
||||
final ClientConnectionData connectionData) {
|
||||
|
||||
final String examId = action.getEntityKey().modelId;
|
||||
try {
|
||||
final String examId = action.getEntityKey().modelId;
|
||||
|
||||
final ProctoringServiceSettings proctoringSettings = this.pageService.getRestService()
|
||||
.getBuilder(GetProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, examId)
|
||||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
final Optional<RemoteProctoringRoom> roomOptional =
|
||||
this.pageService.getRestService().getBuilder(GetCollectingRooms.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, examId)
|
||||
.call()
|
||||
.getOrThrow()
|
||||
.stream()
|
||||
.filter(room -> room.id.equals(connectionData.clientConnection.remoteProctoringRoomId))
|
||||
.findFirst();
|
||||
|
||||
if (roomOptional.isPresent()) {
|
||||
final RemoteProctoringRoom room = roomOptional.get();
|
||||
final ProctoringRoomConnection proctoringConnectionData = this.pageService
|
||||
.getRestService()
|
||||
.getBuilder(GetProctorRoomConnection.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(proctoringSettings.examId))
|
||||
.withQueryParam(ProctoringRoomConnection.ATTR_ROOM_NAME, room.name)
|
||||
.withQueryParam(ProctoringRoomConnection.ATTR_SUBJECT, Utils.encodeFormURL_UTF_8(room.subject))
|
||||
final ProctoringServiceSettings proctoringSettings = this.pageService.getRestService()
|
||||
.getBuilder(GetProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, examId)
|
||||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
ProctoringGUIService.setCurrentProctoringWindowData(examId, proctoringConnectionData);
|
||||
final String script = String.format(
|
||||
MonitoringProctoringService.OPEN_ROOM_SCRIPT,
|
||||
room.name,
|
||||
800,
|
||||
1200,
|
||||
this.guiServiceInfo.getExternalServerURIBuilder().toUriString(),
|
||||
this.remoteProctoringEndpoint);
|
||||
final Optional<RemoteProctoringRoom> roomOptional =
|
||||
this.pageService.getRestService().getBuilder(GetCollectingRooms.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, examId)
|
||||
.call()
|
||||
.getOrThrow()
|
||||
.stream()
|
||||
.filter(room -> room.id.equals(connectionData.clientConnection.remoteProctoringRoomId))
|
||||
.findFirst();
|
||||
|
||||
RWT.getClient()
|
||||
.getService(JavaScriptExecutor.class)
|
||||
.execute(script);
|
||||
if (roomOptional.isPresent()) {
|
||||
final RemoteProctoringRoom room = roomOptional.get();
|
||||
final ProctoringRoomConnection proctoringConnectionData = this.pageService
|
||||
.getRestService()
|
||||
.getBuilder(GetProctorRoomConnection.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(proctoringSettings.examId))
|
||||
.withQueryParam(ProctoringRoomConnection.ATTR_ROOM_NAME, room.name)
|
||||
.withQueryParam(ProctoringRoomConnection.ATTR_SUBJECT, Utils.encodeFormURL_UTF_8(room.subject))
|
||||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
this.pageService.getCurrentUser()
|
||||
.getProctoringGUIService()
|
||||
.registerProctoringWindow(examId, room.name, room.name);
|
||||
ProctoringGUIService.setCurrentProctoringWindowData(examId, proctoringConnectionData);
|
||||
final String script = String.format(
|
||||
MonitoringProctoringService.OPEN_ROOM_SCRIPT,
|
||||
room.name,
|
||||
800,
|
||||
1200,
|
||||
this.guiServiceInfo.getExternalServerURIBuilder().toUriString(),
|
||||
this.remoteProctoringEndpoint);
|
||||
|
||||
RWT.getClient()
|
||||
.getService(JavaScriptExecutor.class)
|
||||
.execute(script);
|
||||
|
||||
this.pageService.getCurrentUser()
|
||||
.getProctoringGUIService()
|
||||
.registerProctoringWindow(examId, room.name, room.name);
|
||||
}
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to open popup for collecting room: ", e);
|
||||
action.pageContext().notifyError(CLOSE_COLLECTING_ERROR, e);
|
||||
}
|
||||
|
||||
return action;
|
||||
|
@ -273,41 +298,49 @@ public class MonitoringProctoringService {
|
|||
final ClientConnectionData connectionData,
|
||||
final ProctoringGUIService proctoringGUIService) {
|
||||
|
||||
final String connectionToken = connectionData.clientConnection.connectionToken;
|
||||
final String examId = action.getEntityKey().modelId;
|
||||
try {
|
||||
|
||||
if (!proctoringGUIService.hasWindow(connectionToken)) {
|
||||
final ProctoringRoomConnection proctoringConnectionData = proctoringGUIService
|
||||
.openBreakOutRoom(
|
||||
examId,
|
||||
connectionToken,
|
||||
connectionData.clientConnection.userSessionId,
|
||||
Arrays.asList(connectionToken))
|
||||
.onError(error -> log.error(
|
||||
"Failed to open single proctoring room for connection {} {}",
|
||||
connectionToken,
|
||||
error.getMessage()))
|
||||
.getOr(null);
|
||||
final String connectionToken = connectionData.clientConnection.connectionToken;
|
||||
final String examId = action.getEntityKey().modelId;
|
||||
|
||||
ProctoringGUIService.setCurrentProctoringWindowData(
|
||||
examId,
|
||||
if (!proctoringGUIService.hasWindow(connectionToken)) {
|
||||
final ProctoringRoomConnection proctoringConnectionData = proctoringGUIService
|
||||
.openBreakOutRoom(
|
||||
examId,
|
||||
connectionToken,
|
||||
connectionData.clientConnection.userSessionId,
|
||||
Arrays.asList(connectionToken))
|
||||
.onError(error -> log.error(
|
||||
"Failed to open single proctoring room for connection {} {}",
|
||||
connectionToken,
|
||||
error.getMessage()))
|
||||
.getOr(null);
|
||||
|
||||
ProctoringGUIService.setCurrentProctoringWindowData(
|
||||
examId,
|
||||
connectionToken,
|
||||
proctoringConnectionData);
|
||||
}
|
||||
|
||||
final JavaScriptExecutor javaScriptExecutor = RWT.getClient().getService(JavaScriptExecutor.class);
|
||||
final String script = String.format(
|
||||
MonitoringProctoringService.OPEN_ROOM_SCRIPT,
|
||||
connectionToken,
|
||||
proctoringConnectionData);
|
||||
420,
|
||||
640,
|
||||
this.guiServiceInfo.getExternalServerURIBuilder().toUriString(),
|
||||
this.remoteProctoringEndpoint);
|
||||
javaScriptExecutor.execute(script);
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to open popup for one to one room: ", e);
|
||||
action.pageContext().notifyError(OPEN_ONE_ERROR, e);
|
||||
}
|
||||
|
||||
final JavaScriptExecutor javaScriptExecutor = RWT.getClient().getService(JavaScriptExecutor.class);
|
||||
final String script = String.format(
|
||||
MonitoringProctoringService.OPEN_ROOM_SCRIPT,
|
||||
connectionToken,
|
||||
420,
|
||||
640,
|
||||
this.guiServiceInfo.getExternalServerURIBuilder().toUriString(),
|
||||
this.remoteProctoringEndpoint);
|
||||
javaScriptExecutor.execute(script);
|
||||
return action;
|
||||
}
|
||||
|
||||
private PageAction openTownhallRoom(
|
||||
private boolean openTownhallRoom(
|
||||
final ProctoringGUIService proctoringGUIService,
|
||||
final PageAction action) {
|
||||
|
||||
|
@ -342,11 +375,13 @@ public class MonitoringProctoringService {
|
|||
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to open popup for town-hall room: ", e);
|
||||
return false;
|
||||
}
|
||||
return action;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private PageAction closeTownhallRoom(
|
||||
private boolean closeTownhallRoom(
|
||||
final ProctoringGUIService proctoringGUIService,
|
||||
final PageAction action) {
|
||||
|
||||
|
@ -360,8 +395,10 @@ public class MonitoringProctoringService {
|
|||
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to close proctoring town-hall room for exam: {}", examId);
|
||||
return false;
|
||||
}
|
||||
return action;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateTownhallButton(
|
||||
|
@ -384,16 +421,18 @@ public class MonitoringProctoringService {
|
|||
this.pageService.firePageEvent(
|
||||
new ActionActivationEvent(
|
||||
false,
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
|
||||
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM),
|
||||
new Tuple<>(
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
|
||||
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)),
|
||||
pageContext);
|
||||
}
|
||||
} else {
|
||||
this.pageService.firePageEvent(
|
||||
new ActionActivationEvent(
|
||||
proctoringGUIService.getNumberOfProctoringParticipants() > 0,
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
|
||||
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM),
|
||||
new Tuple<>(
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM)),
|
||||
pageContext);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -246,7 +246,9 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
|||
EntityType.EXAM,
|
||||
examId,
|
||||
ProctoringServiceSettings.ATTR_APP_SECRET,
|
||||
this.cryptor.encrypt(proctoringServiceSettings.appSecret).toString());
|
||||
this.cryptor.encrypt(proctoringServiceSettings.appSecret)
|
||||
.getOrThrow()
|
||||
.toString());
|
||||
|
||||
return proctoringServiceSettings;
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session;
|
|||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
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.ProctoringRoomConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
|
@ -75,6 +76,8 @@ public interface ExamProctoringRoomService {
|
|||
* @return Result refer to the RemoteProctoringRoom data or to an error when happened */
|
||||
Result<RemoteProctoringRoom> getTownhallRoomData(final Long examId);
|
||||
|
||||
Result<EntityKey> closeTownhallRoom(Long examId);
|
||||
|
||||
/** Used to create a break out room for all active SEB clients given by the connectionTokens.
|
||||
* This first notifies the underling proctoring specific service layer on room creation that will create a room
|
||||
* on the meeting service if necessary. Then creating the room internally for holding data and tracking the new
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.springframework.context.annotation.Lazy;
|
|||
import org.springframework.stereotype.Service;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
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.Exam.ExamStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringRoomConnection;
|
||||
|
@ -323,6 +324,16 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
return this.remoteProctoringRoomDAO.isTownhallRoomActive(examId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<EntityKey> closeTownhallRoom(final Long examId) {
|
||||
if (isTownhallRoomActive(examId)) {
|
||||
return this.remoteProctoringRoomDAO.getTownhallRoom(examId)
|
||||
.flatMap(room -> this.remoteProctoringRoomDAO.deleteRoom(room.id));
|
||||
}
|
||||
|
||||
return Result.ofRuntimeError("No active town-hall for exam: " + examId);
|
||||
}
|
||||
|
||||
private void closeTownhall(
|
||||
final Long examId,
|
||||
final ProctoringServiceSettings proctoringSettings,
|
||||
|
|
|
@ -286,6 +286,9 @@ public class ExamProctoringController {
|
|||
checkAccess(institutionId, examId);
|
||||
return this.examProcotringRoomService
|
||||
.openTownhallRoom(examId, subject)
|
||||
.onError(error -> this.examProcotringRoomService.closeTownhallRoom(examId)
|
||||
.onError(err -> log.error("Failed to close town-hall after failed opening: {}",
|
||||
err.getMessage())))
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
|
|
|
@ -665,6 +665,13 @@ sebserver.exam.proctoring.type.servertype.JITSI_MEET.tooltip=Use a Jitsi Meet se
|
|||
sebserver.exam.proctoring.type.servertype.ZOOM=Zoom Server
|
||||
sebserver.exam.proctoring.type.servertype.ZOOM.tooltip=Use a Zoom meeting server for proctoring
|
||||
|
||||
sebserver.exam.proctoring.townhall.open.error=Failed to open the town-hall room.
|
||||
sebserver.exam.proctoring.townhall.close.error=Failed to close the town-hall room properly.
|
||||
sebserver.exam.proctoring.one.open.error=Failed to open the one-to-one room.
|
||||
sebserver.exam.proctoring.one.close.error=Failed to close the one-to-one room properly.
|
||||
sebserver.exam.proctoring.collecting.open.error=Failed to open the collecting room.
|
||||
sebserver.exam.proctoring.collecting.close.error=Failed to close the collecting room properly.
|
||||
|
||||
|
||||
################################
|
||||
# Connection Configuration
|
||||
|
|
Loading…
Reference in a new issue