SEBSERV-139 single used town-hall and code-cleanup
This commit is contained in:
parent
9d5ed34ec6
commit
8470e3b160
10 changed files with 290 additions and 136 deletions
|
@ -186,6 +186,7 @@ public final class API {
|
|||
public static final String EXAM_PROCTORING_ACTIVATE_TOWNHALL_ROOM = "activate-towhall-room";
|
||||
public static final String EXAM_PROCTORING_DEACTIVATE_TOWNHALL_ROOM = "deactivate-towhall-room";
|
||||
public static final String EXAM_PROCTORING_TOWNHALL_ROOM_DATA = "towhall-room-data";
|
||||
public static final String EXAM_PROCTORING_TOWNHALL_ROOM_AVAILABLE = "towhall-available";
|
||||
|
||||
public static final String SEB_CLIENT_CONNECTION_ENDPOINT = "/seb-client-connection";
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.util.function.BooleanSupplier;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.eclipse.rap.rwt.RWT;
|
||||
import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
|
||||
import org.eclipse.swt.SWT;
|
||||
|
@ -78,6 +79,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClient
|
|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetProcotringRooms;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetProctorRoomConnectionData;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetTownhallRoom;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.IsTownhallRoomAvailable;
|
||||
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;
|
||||
|
@ -123,6 +125,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
|
||||
private final ServerPushService serverPushService;
|
||||
private final PageService pageService;
|
||||
private final RestService restService;
|
||||
private final ResourceService resourceService;
|
||||
private final AsyncRunner asyncRunner;
|
||||
private final InstructionProcessor instructionProcessor;
|
||||
|
@ -147,6 +150,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
|
||||
this.serverPushService = serverPushService;
|
||||
this.pageService = pageService;
|
||||
this.restService = pageService.getRestService();
|
||||
this.resourceService = pageService.getResourceService();
|
||||
this.asyncRunner = asyncRunner;
|
||||
this.instructionProcessor = instructionProcessor;
|
||||
|
@ -214,13 +218,14 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
|
||||
this.serverPushService.runServerPush(
|
||||
new ServerPushContext(
|
||||
content, Utils.truePredicate(),
|
||||
content,
|
||||
Utils.truePredicate(),
|
||||
createServerPushUpdateErrorHandler(this.pageService, pageContext)),
|
||||
this.pollInterval,
|
||||
context -> clientTable.updateValues(),
|
||||
updateTableGUI(clientTable));
|
||||
|
||||
final BooleanSupplier privilege = () -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER);
|
||||
final BooleanSupplier isExamSupporter = () -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER);
|
||||
|
||||
actionBuilder
|
||||
|
||||
|
@ -242,20 +247,20 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
|
||||
return copyOfPageAction;
|
||||
})
|
||||
.publishIf(privilege, false)
|
||||
.publishIf(isExamSupporter, false)
|
||||
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_QUIT_ALL)
|
||||
.withEntityKey(entityKey)
|
||||
.withConfirm(() -> CONFIRM_QUIT_ALL)
|
||||
.withExec(action -> this.quitSEBClients(action, clientTable, true))
|
||||
.noEventPropagation()
|
||||
.publishIf(privilege)
|
||||
.publishIf(isExamSupporter)
|
||||
|
||||
.newAction(ActionDefinition.MONITORING_EXAM_SEARCH_CONNECTIONS)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(this::openSearchPopup)
|
||||
.noEventPropagation()
|
||||
.publishIf(privilege)
|
||||
.publishIf(isExamSupporter)
|
||||
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_QUIT_SELECTED)
|
||||
.withEntityKey(entityKey)
|
||||
|
@ -265,7 +270,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
action -> this.quitSEBClients(action, clientTable, false),
|
||||
EMPTY_ACTIVE_SELECTION_TEXT_KEY)
|
||||
.noEventPropagation()
|
||||
.publishIf(privilege, false)
|
||||
.publishIf(isExamSupporter, false)
|
||||
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION)
|
||||
.withEntityKey(entityKey)
|
||||
|
@ -275,81 +280,26 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
action -> this.disableSEBClients(action, clientTable, false),
|
||||
EMPTY_SELECTION_TEXT_KEY)
|
||||
.noEventPropagation()
|
||||
.publishIf(privilege, false);
|
||||
|
||||
if (privilege.getAsBoolean()) {
|
||||
|
||||
if (clientTable.isStatusHidden(ConnectionStatus.CLOSED)) {
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_CLOSED_CONNECTION)
|
||||
.withExec(showStateViewAction(clientTable, ConnectionStatus.CLOSED))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_CLOSED_CONNECTION)
|
||||
.withExec(hideStateViewAction(clientTable, ConnectionStatus.CLOSED))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.publish();
|
||||
} else {
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_CLOSED_CONNECTION)
|
||||
.withExec(hideStateViewAction(clientTable, ConnectionStatus.CLOSED))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_CLOSED_CONNECTION)
|
||||
.withExec(showStateViewAction(clientTable, ConnectionStatus.CLOSED))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.publish();
|
||||
}
|
||||
|
||||
if (clientTable.isStatusHidden(ConnectionStatus.CONNECTION_REQUESTED)) {
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_REQUESTED_CONNECTION)
|
||||
.withExec(showStateViewAction(clientTable, ConnectionStatus.CONNECTION_REQUESTED))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_REQUESTED_CONNECTION)
|
||||
.withExec(
|
||||
hideStateViewAction(clientTable, ConnectionStatus.CONNECTION_REQUESTED))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.publish();
|
||||
} else {
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_REQUESTED_CONNECTION)
|
||||
.withExec(hideStateViewAction(clientTable, ConnectionStatus.CONNECTION_REQUESTED))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_REQUESTED_CONNECTION)
|
||||
.withExec(
|
||||
showStateViewAction(clientTable, ConnectionStatus.CONNECTION_REQUESTED))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.publish();
|
||||
}
|
||||
|
||||
if (clientTable.isStatusHidden(ConnectionStatus.DISABLED)) {
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_DISABLED_CONNECTION)
|
||||
.withExec(showStateViewAction(clientTable, ConnectionStatus.DISABLED))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_DISABLED_CONNECTION)
|
||||
.withExec(hideStateViewAction(clientTable, ConnectionStatus.DISABLED))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.publish();
|
||||
} else {
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_DISABLED_CONNECTION)
|
||||
.withExec(hideStateViewAction(clientTable, ConnectionStatus.DISABLED))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_DISABLED_CONNECTION)
|
||||
.withExec(showStateViewAction(clientTable, ConnectionStatus.DISABLED))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.publish();
|
||||
}
|
||||
.publishIf(isExamSupporter, false);
|
||||
|
||||
if (isExamSupporter.getAsBoolean()) {
|
||||
addFilterActions(actionBuilder, clientTable, isExamSupporter);
|
||||
addProctoringActions(
|
||||
currentUser.getProctoringGUIService(),
|
||||
pageContext,
|
||||
content,
|
||||
actionBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
final ProctoringSettings proctoringSettings = restService
|
||||
private void addProctoringActions(
|
||||
final ProctoringGUIService proctoringGUIService,
|
||||
final PageContext pageContext,
|
||||
final Composite parent,
|
||||
final PageActionBuilder actionBuilder) {
|
||||
|
||||
final EntityKey entityKey = pageContext.getEntityKey();
|
||||
final ProctoringSettings proctoringSettings = this.restService
|
||||
.getBuilder(GetProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||
.call()
|
||||
|
@ -359,7 +309,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(this::toggleTownhallRoom)
|
||||
.withExec(action -> this.toggleTownhallRoom(proctoringGUIService, action))
|
||||
.noEventPropagation()
|
||||
.publish();
|
||||
|
||||
|
@ -375,34 +325,126 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
|
||||
final Map<String, Pair<RemoteProctoringRoom, TreeItem>> availableRooms = new HashMap<>();
|
||||
updateRoomActions(
|
||||
entityKey,
|
||||
pageContext,
|
||||
availableRooms,
|
||||
actionBuilder,
|
||||
proctoringSettings);
|
||||
proctoringSettings,
|
||||
proctoringGUIService);
|
||||
this.serverPushService.runServerPush(
|
||||
new ServerPushContext(
|
||||
content,
|
||||
parent,
|
||||
Utils.truePredicate(),
|
||||
createServerPushUpdateErrorHandler(this.pageService, pageContext)),
|
||||
this.proctoringRoomUpdateInterval,
|
||||
context -> updateRoomActions(
|
||||
entityKey,
|
||||
pageContext,
|
||||
availableRooms,
|
||||
actionBuilder,
|
||||
proctoringSettings));
|
||||
proctoringSettings,
|
||||
proctoringGUIService));
|
||||
}
|
||||
}
|
||||
|
||||
private void addFilterActions(
|
||||
final PageActionBuilder actionBuilder,
|
||||
final ClientConnectionTable clientTable,
|
||||
final BooleanSupplier isExamSupporter) {
|
||||
|
||||
addClosedFilterAction(actionBuilder, clientTable);
|
||||
addRequestedFilterAction(actionBuilder, clientTable);
|
||||
addDisabledFilterAction(actionBuilder, clientTable);
|
||||
}
|
||||
|
||||
private void addDisabledFilterAction(
|
||||
final PageActionBuilder actionBuilder,
|
||||
final ClientConnectionTable clientTable) {
|
||||
|
||||
if (clientTable.isStatusHidden(ConnectionStatus.DISABLED)) {
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_DISABLED_CONNECTION)
|
||||
.withExec(showStateViewAction(clientTable, ConnectionStatus.DISABLED))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_DISABLED_CONNECTION)
|
||||
.withExec(hideStateViewAction(clientTable, ConnectionStatus.DISABLED))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.publish();
|
||||
} else {
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_DISABLED_CONNECTION)
|
||||
.withExec(hideStateViewAction(clientTable, ConnectionStatus.DISABLED))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_DISABLED_CONNECTION)
|
||||
.withExec(showStateViewAction(clientTable, ConnectionStatus.DISABLED))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.publish();
|
||||
}
|
||||
}
|
||||
|
||||
private void addRequestedFilterAction(
|
||||
final PageActionBuilder actionBuilder,
|
||||
final ClientConnectionTable clientTable) {
|
||||
|
||||
if (clientTable.isStatusHidden(ConnectionStatus.CONNECTION_REQUESTED)) {
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_REQUESTED_CONNECTION)
|
||||
.withExec(showStateViewAction(clientTable, ConnectionStatus.CONNECTION_REQUESTED))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_REQUESTED_CONNECTION)
|
||||
.withExec(
|
||||
hideStateViewAction(clientTable, ConnectionStatus.CONNECTION_REQUESTED))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.publish();
|
||||
} else {
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_REQUESTED_CONNECTION)
|
||||
.withExec(hideStateViewAction(clientTable, ConnectionStatus.CONNECTION_REQUESTED))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_REQUESTED_CONNECTION)
|
||||
.withExec(
|
||||
showStateViewAction(clientTable, ConnectionStatus.CONNECTION_REQUESTED))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.publish();
|
||||
}
|
||||
}
|
||||
|
||||
private void addClosedFilterAction(
|
||||
final PageActionBuilder actionBuilder,
|
||||
final ClientConnectionTable clientTable) {
|
||||
|
||||
if (clientTable.isStatusHidden(ConnectionStatus.CLOSED)) {
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_CLOSED_CONNECTION)
|
||||
.withExec(showStateViewAction(clientTable, ConnectionStatus.CLOSED))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_CLOSED_CONNECTION)
|
||||
.withExec(hideStateViewAction(clientTable, ConnectionStatus.CLOSED))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.publish();
|
||||
} else {
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_HIDE_CLOSED_CONNECTION)
|
||||
.withExec(hideStateViewAction(clientTable, ConnectionStatus.CLOSED))
|
||||
.noEventPropagation()
|
||||
.withSwitchAction(
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_SHOW_CLOSED_CONNECTION)
|
||||
.withExec(showStateViewAction(clientTable, ConnectionStatus.CLOSED))
|
||||
.noEventPropagation()
|
||||
.create())
|
||||
.publish();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isTownhallRoomActive(final String examModelId) {
|
||||
final RemoteProctoringRoom townhall = this.pageService.getRestService()
|
||||
.getBuilder(GetTownhallRoom.class)
|
||||
return !BooleanUtils.toBoolean(this.pageService
|
||||
.getRestService()
|
||||
.getBuilder(IsTownhallRoomAvailable.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, examModelId)
|
||||
.call()
|
||||
.getOr(null);
|
||||
|
||||
return townhall != null && townhall.id != null;
|
||||
.getOr(Constants.FALSE_STRING));
|
||||
}
|
||||
|
||||
private PageAction openSearchPopup(final PageAction action) {
|
||||
|
@ -410,9 +452,12 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
return action;
|
||||
}
|
||||
|
||||
private PageAction toggleTownhallRoom(final PageAction action) {
|
||||
private PageAction toggleTownhallRoom(
|
||||
final ProctoringGUIService proctoringGUIService,
|
||||
final PageAction action) {
|
||||
|
||||
if (isTownhallRoomActive(action.getEntityKey().modelId)) {
|
||||
closeTownhallRoom(action);
|
||||
closeTownhallRoom(proctoringGUIService, action);
|
||||
this.pageService.firePageEvent(
|
||||
new ActionActivationEvent(
|
||||
true,
|
||||
|
@ -422,7 +467,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
action.pageContext());
|
||||
return action;
|
||||
} else {
|
||||
openTownhallRoom(action);
|
||||
openTownhallRoom(proctoringGUIService, action);
|
||||
this.pageService.firePageEvent(
|
||||
new ActionActivationEvent(
|
||||
true,
|
||||
|
@ -434,14 +479,12 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
}
|
||||
}
|
||||
|
||||
private PageAction openTownhallRoom(final PageAction action) {
|
||||
private PageAction openTownhallRoom(
|
||||
final ProctoringGUIService proctoringGUIService,
|
||||
final PageAction action) {
|
||||
|
||||
try {
|
||||
final EntityKey examId = action.getEntityKey();
|
||||
|
||||
final ProctoringGUIService proctoringGUIService = this.pageService
|
||||
.getCurrentUser()
|
||||
.getProctoringGUIService();
|
||||
|
||||
String activeAllRoomName = proctoringGUIService.getTownhallRoom(examId.modelId);
|
||||
|
||||
if (activeAllRoomName == null) {
|
||||
|
@ -476,7 +519,10 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
return action;
|
||||
}
|
||||
|
||||
private PageAction closeTownhallRoom(final PageAction action) {
|
||||
private PageAction closeTownhallRoom(
|
||||
final ProctoringGUIService proctoringGUIService,
|
||||
final PageAction action) {
|
||||
|
||||
final String examId = action.getEntityKey().modelId;
|
||||
final RemoteProctoringRoom townhall = this.pageService.getRestService()
|
||||
.getBuilder(GetTownhallRoom.class)
|
||||
|
@ -492,45 +538,59 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
}
|
||||
|
||||
try {
|
||||
final ProctoringGUIService proctoringGUIService = this.pageService
|
||||
.getCurrentUser()
|
||||
.getProctoringGUIService();
|
||||
|
||||
proctoringGUIService.closeRoom(townhall.name);
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to close procotring townhall room for exam: {}", examId);
|
||||
log.error("Failed to close proctoring townhall room for exam: {}", examId);
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
private void updateTownhallButton(final EntityKey entityKey, final PageContext pageContext) {
|
||||
private void updateTownhallButton(
|
||||
final ProctoringGUIService proctoringGUIService,
|
||||
final PageContext pageContext) {
|
||||
final EntityKey entityKey = pageContext.getEntityKey();
|
||||
|
||||
if (isTownhallRoomActive(entityKey.modelId)) {
|
||||
this.pageService.firePageEvent(
|
||||
new ActionActivationEvent(
|
||||
true,
|
||||
new Tuple<>(
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
|
||||
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)),
|
||||
pageContext);
|
||||
final boolean townhallRoomFromThisUser = proctoringGUIService
|
||||
.getTownhallRoom(entityKey.modelId) != null;
|
||||
if (townhallRoomFromThisUser) {
|
||||
this.pageService.firePageEvent(
|
||||
new ActionActivationEvent(
|
||||
true,
|
||||
new Tuple<>(
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
|
||||
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)),
|
||||
pageContext);
|
||||
} else {
|
||||
this.pageService.firePageEvent(
|
||||
new ActionActivationEvent(
|
||||
false,
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
|
||||
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM),
|
||||
pageContext);
|
||||
}
|
||||
} else {
|
||||
this.pageService.firePageEvent(
|
||||
new ActionActivationEvent(
|
||||
true,
|
||||
new Tuple<>(
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM)),
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM),
|
||||
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
|
||||
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM),
|
||||
pageContext);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRoomActions(
|
||||
final EntityKey entityKey,
|
||||
final PageContext pageContext,
|
||||
final Map<String, Pair<RemoteProctoringRoom, TreeItem>> rooms,
|
||||
final PageActionBuilder actionBuilder,
|
||||
final ProctoringSettings proctoringSettings) {
|
||||
final ProctoringSettings proctoringSettings,
|
||||
final ProctoringGUIService proctoringGUIService) {
|
||||
|
||||
updateTownhallButton(entityKey, pageContext);
|
||||
final EntityKey entityKey = pageContext.getEntityKey();
|
||||
updateTownhallButton(proctoringGUIService, pageContext);
|
||||
final I18nSupport i18nSupport = this.pageService.getI18nSupport();
|
||||
this.pageService.getRestService().getBuilder(GetProcotringRooms.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||
|
@ -571,7 +631,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
this.pageService.publishAction(
|
||||
action,
|
||||
_treeItem -> rooms.put(room.name, new Pair<>(room, _treeItem)));
|
||||
addRoomConnectionsPopupListener(entityKey, pageContext, rooms);
|
||||
addRoomConnectionsPopupListener(pageContext, rooms);
|
||||
processProctorRoomActionActivation(rooms.get(room.name).b, room, pageContext);
|
||||
}
|
||||
});
|
||||
|
@ -596,11 +656,11 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
}
|
||||
|
||||
private void addRoomConnectionsPopupListener(
|
||||
final EntityKey entityKey,
|
||||
final PageContext pageContext,
|
||||
final Map<String, Pair<RemoteProctoringRoom, TreeItem>> rooms) {
|
||||
|
||||
if (!rooms.isEmpty()) {
|
||||
final EntityKey entityKey = pageContext.getEntityKey();
|
||||
final TreeItem treeItem = rooms.values().iterator().next().b;
|
||||
final Tree tree = treeItem.getParent();
|
||||
if (tree.getData(SHOW_CONNECTION_ACTION_APPLIED) == null) {
|
||||
|
|
|
@ -80,17 +80,18 @@ public class JitsiMeetProctoringView implements RemoteProctoringView {
|
|||
public void compose(final PageContext pageContext) {
|
||||
|
||||
final ProctoringWindowData proctoringWindowData = ProctoringGUIService.getCurrentProctoringWindowData();
|
||||
|
||||
final Composite parent = pageContext.getParent();
|
||||
|
||||
final Composite content = new Composite(parent, SWT.NONE | SWT.NO_SCROLL);
|
||||
final GridLayout gridLayout = new GridLayout();
|
||||
final ProctoringGUIService proctoringGUIService = this.pageService
|
||||
.getCurrentUser()
|
||||
.getProctoringGUIService();
|
||||
|
||||
content.setLayout(gridLayout);
|
||||
final GridData headerCell = new GridData(SWT.FILL, SWT.FILL, true, true);
|
||||
content.setLayoutData(headerCell);
|
||||
|
||||
parent.addListener(SWT.Dispose, event -> closeRoom(proctoringWindowData));
|
||||
parent.addListener(SWT.Dispose, event -> closeRoom(proctoringGUIService, proctoringWindowData));
|
||||
|
||||
final String url = this.guiServiceInfo
|
||||
.getExternalServerURIBuilder()
|
||||
|
@ -120,7 +121,7 @@ public class JitsiMeetProctoringView implements RemoteProctoringView {
|
|||
|
||||
final Button closeAction = widgetFactory.buttonLocalized(footer, CLOSE_WINDOW_TEXT_KEY);
|
||||
closeAction.setLayoutData(new RowData(150, 30));
|
||||
closeAction.addListener(SWT.Selection, event -> closeRoom(proctoringWindowData));
|
||||
closeAction.addListener(SWT.Selection, event -> closeRoom(proctoringGUIService, proctoringWindowData));
|
||||
|
||||
final BroadcastActionState broadcastActionState = new BroadcastActionState();
|
||||
final String connectionTokens = getConnectionTokens(proctoringWindowData);
|
||||
|
@ -256,11 +257,15 @@ public class JitsiMeetProctoringView implements RemoteProctoringView {
|
|||
boolean chat = false;
|
||||
}
|
||||
|
||||
private void closeRoom(final ProctoringWindowData proctoringWindowData) {
|
||||
this.pageService
|
||||
.getCurrentUser()
|
||||
.getProctoringGUIService()
|
||||
.closeRoom(proctoringWindowData.connectionData.roomName);
|
||||
private void closeRoom(
|
||||
final ProctoringGUIService proctoringGUIService,
|
||||
final ProctoringWindowData proctoringWindowData) {
|
||||
|
||||
try {
|
||||
proctoringGUIService.closeRoom(proctoringWindowData.connectionData.roomName);
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to close proctoring window properly: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class IsTownhallRoomAvailable extends RestCall<String> {
|
||||
|
||||
public IsTownhallRoomAvailable() {
|
||||
super(new TypeKey<>(
|
||||
CallType.GET_SINGLE,
|
||||
EntityType.REMOTE_PROCTORING_ROOM,
|
||||
new TypeReference<String>() {
|
||||
}),
|
||||
HttpMethod.GET,
|
||||
MediaType.APPLICATION_FORM_URLENCODED,
|
||||
API.EXAM_PROCTORING_ENDPOINT
|
||||
+ API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.EXAM_PROCTORING_TOWNHALL_ROOM_AVAILABLE);
|
||||
}
|
||||
|
||||
}
|
|
@ -195,7 +195,7 @@ public class ProctoringGUIService {
|
|||
closeWindow(name);
|
||||
final RoomConnectionData roomConnectionData = this.rooms.remove(name);
|
||||
if (roomConnectionData != null) {
|
||||
// send reset of broadcast attributes to all in the room
|
||||
// Send reset of broadcast attributes to all in the room
|
||||
this.restService.getBuilder(SendProctoringBroadcastAttributes.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, roomConnectionData.examId)
|
||||
.withFormParam(Domain.REMOTE_PROCTORING_ROOM.ATTR_ID, roomConnectionData.roomName)
|
||||
|
@ -209,7 +209,7 @@ public class ProctoringGUIService {
|
|||
"Failed to send reset broadcast attribute instruction call for room: {}, cause: {}",
|
||||
roomConnectionData.roomName,
|
||||
error.getMessage()));
|
||||
// send instruction to leave this room and join the own exam collecting room
|
||||
// Send instruction to leave this room and join the own exam collecting room
|
||||
if (!roomConnectionData.connections.isEmpty()) {
|
||||
this.restService.getBuilder(SendRejoinExamCollectionRoom.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, roomConnectionData.examId)
|
||||
|
@ -221,6 +221,7 @@ public class ProctoringGUIService {
|
|||
name,
|
||||
error.getMessage()));
|
||||
} else {
|
||||
// Close town-hall room
|
||||
this.restService.getBuilder(DisposeTownhallRoom.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, roomConnectionData.examId)
|
||||
.call()
|
||||
|
|
|
@ -23,6 +23,8 @@ public interface RemoteProctoringRoomDAO {
|
|||
|
||||
Result<String> getRoomName(Long roomId);
|
||||
|
||||
boolean isTownhallRoomActive(Long examId);
|
||||
|
||||
Result<RemoteProctoringRoom> getTownhallRoom(Long examId);
|
||||
|
||||
Result<RemoteProctoringRoom> createTownhallRoom(Long examId, String subject);
|
||||
|
|
|
@ -19,6 +19,8 @@ import java.util.stream.Collectors;
|
|||
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
@ -39,6 +41,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler;
|
|||
@WebServiceProfile
|
||||
public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(RemoteProctoringRoomDAOImpl.class);
|
||||
|
||||
private final RemoteProctoringRoomRecordMapper remoteProctoringRoomRecordMapper;
|
||||
|
||||
protected RemoteProctoringRoomDAOImpl(
|
||||
|
@ -94,14 +98,9 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO {
|
|||
@Transactional
|
||||
public Result<RemoteProctoringRoom> createTownhallRoom(final Long examId, final String subject) {
|
||||
return Result.tryCatch(() -> {
|
||||
// check first if town-hall room is not already active
|
||||
final long active = this.remoteProctoringRoomRecordMapper.countByExample()
|
||||
.where(RemoteProctoringRoomRecordDynamicSqlSupport.examId, isEqualTo(examId))
|
||||
.and(RemoteProctoringRoomRecordDynamicSqlSupport.townhallRoom, isNotEqualTo(0))
|
||||
.build()
|
||||
.execute();
|
||||
|
||||
if (active > 0) {
|
||||
// Check first if town-hall room is not already active
|
||||
if (isTownhallRoomActive(examId)) {
|
||||
throw new IllegalStateException("Townhall, for exam: " + examId + " already exists");
|
||||
}
|
||||
|
||||
|
@ -122,6 +121,24 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO {
|
|||
.onError(TransactionHandler::rollback);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public boolean isTownhallRoomActive(final Long examId) {
|
||||
try {
|
||||
final long active = this.remoteProctoringRoomRecordMapper.countByExample()
|
||||
.where(RemoteProctoringRoomRecordDynamicSqlSupport.examId, isEqualTo(examId))
|
||||
.and(RemoteProctoringRoomRecordDynamicSqlSupport.townhallRoom, isNotEqualTo(0))
|
||||
.build()
|
||||
.execute();
|
||||
return (active > 0);
|
||||
} catch (final Exception e) {
|
||||
log.error(
|
||||
"Failed to verify town-hall room activity for exam: {}. Mark it as active to avoid double openings",
|
||||
examId, e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<RemoteProctoringRoom> saveRoom(final Long examId, final RemoteProctoringRoom room) {
|
||||
|
|
|
@ -44,6 +44,12 @@ public interface ExamProctoringRoomService {
|
|||
* name of an exam. */
|
||||
void updateProctoringCollectingRooms();
|
||||
|
||||
/** Indicates whether the town-hall room for a specified exam is active or not.
|
||||
*
|
||||
* @param examId the exam identifier
|
||||
* @return true if the town-hall room for specified exam is active, false if not. */
|
||||
boolean isTownhallRoomActive(Long examId);
|
||||
|
||||
/** This creates a town-hall room for a specific exam. The exam must be active and running
|
||||
* and there must be no other town-hall room already be active. An unique room name will be
|
||||
* created and returned.
|
||||
|
|
|
@ -95,6 +95,11 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTownhallRoomActive(final Long examId) {
|
||||
return this.remoteProctoringRoomDAO.isTownhallRoomActive(examId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<RemoteProctoringRoom> createTownhallRoom(final Long examId, final String subject) {
|
||||
if (!this.examSessionService.isExamRunning(examId)) {
|
||||
|
|
|
@ -304,6 +304,22 @@ public class ExamProctoringController {
|
|||
Utils.toByteArray(connectionToken));
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.EXAM_PROCTORING_TOWNHALL_ROOM_AVAILABLE,
|
||||
method = RequestMethod.GET,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public String isTownhallRoomAvialbale(
|
||||
@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) {
|
||||
|
||||
checkExamReadAccess(institutionId);
|
||||
return String.valueOf(!this.examProcotringRoomService.isTownhallRoomActive(examId));
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.EXAM_PROCTORING_TOWNHALL_ROOM_DATA,
|
||||
|
|
Loading…
Reference in a new issue