SEBSERV-151 and SEBSERV-189
This commit is contained in:
parent
f0fa591348
commit
3cbfd80206
13 changed files with 404 additions and 61 deletions
|
@ -67,6 +67,7 @@ public final class ClientConnection implements GrantEntity {
|
|||
public static final String FILTER_ATTR_SESSION_ID = Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID;
|
||||
public static final String FILTER_ATTR_IP_STRING = Domain.CLIENT_CONNECTION.ATTR_CLIENT_ADDRESS;
|
||||
public static final String FILTER_ATTR_INFO = ATTR_INFO;
|
||||
public static final String FILTER_ATTR_TOKEN_LIST = "CONNECTION_TOKENS";
|
||||
|
||||
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID)
|
||||
public final Long id;
|
||||
|
@ -388,7 +389,7 @@ public final class ClientConnection implements GrantEntity {
|
|||
}
|
||||
|
||||
public static Predicate<ClientConnection> getStatusPredicate(final ConnectionStatus... status) {
|
||||
final EnumSet<ConnectionStatus> states = EnumSet.allOf(ConnectionStatus.class);
|
||||
final EnumSet<ConnectionStatus> states = EnumSet.noneOf(ConnectionStatus.class);
|
||||
if (status != null) {
|
||||
Collections.addAll(states, status);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,8 @@ public final class ClientInstruction {
|
|||
SEB_QUIT,
|
||||
SEB_PROCTORING,
|
||||
SEB_RECONFIGURE_SETTINGS,
|
||||
NOTIFICATION_CONFIRM
|
||||
NOTIFICATION_CONFIRM,
|
||||
SEB_FORCE_LOCK_SCREEN
|
||||
}
|
||||
|
||||
public enum ProctoringInstructionMethod {
|
||||
|
@ -70,6 +71,11 @@ public final class ClientInstruction {
|
|||
public static final String ZOOM_RECEIVE_VIDEO = "zoomReceiveVideo";
|
||||
public static final String ZOOM_ALLOW_CHAT = "zoomFeatureFlagChat";
|
||||
}
|
||||
|
||||
public interface SEB_FORCE_LOCK_SCREEN {
|
||||
public static final String MESSAGE = "message";
|
||||
public static final String IMAGE_URL = "imageURL";
|
||||
}
|
||||
}
|
||||
|
||||
@JsonProperty(Domain.CLIENT_INSTRUCTION.ATTR_ID)
|
||||
|
|
|
@ -30,6 +30,9 @@ public enum ActionCategory {
|
|||
EXAM_MONITORING_NOTIFICATION_LIST(new LocTextKey(
|
||||
"sebserver.monitoring.exam.connection.notificationlist.actions"),
|
||||
1),
|
||||
EXAM_MONITORING_2(new LocTextKey(
|
||||
"sebserver.monitoring.exam.connection.selected.actions"), 2),
|
||||
EXAM_MONITORING_3(null, 3),
|
||||
CLIENT_EVENT_LIST(new LocTextKey("sebserver.monitoring.exam.connection.list.actions"), 1),
|
||||
LOGS_USER_ACTIVITY_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1),
|
||||
LOGS_SEB_CLIENT_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1),
|
||||
|
|
|
@ -769,7 +769,7 @@ public enum ActionDefinition {
|
|||
new LocTextKey("sebserver.monitoring.exam.connection.action.view"),
|
||||
ImageIcon.SHOW,
|
||||
PageStateDefinitionImpl.MONITORING_CLIENT_CONNECTION,
|
||||
ActionCategory.CLIENT_EVENT_LIST),
|
||||
ActionCategory.EXAM_MONITORING_3),
|
||||
MONITOR_EXAM_CLIENT_CONNECTION_QUIT(
|
||||
new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit"),
|
||||
ImageIcon.SEND_QUIT,
|
||||
|
@ -795,12 +795,18 @@ public enum ActionDefinition {
|
|||
new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit.selected"),
|
||||
ImageIcon.SEND_QUIT,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.CLIENT_EVENT_LIST),
|
||||
ActionCategory.EXAM_MONITORING_2),
|
||||
MONITOR_EXAM_QUIT_ALL(
|
||||
new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit.all"),
|
||||
ImageIcon.SEND_QUIT,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.FORM),
|
||||
MONITOR_EXAM_LOCK_SELECTED(
|
||||
new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.lock.selected"),
|
||||
ImageIcon.LOCK,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.EXAM_MONITORING_2),
|
||||
|
||||
MONITOR_EXAM_BACK_TO_OVERVIEW(
|
||||
new LocTextKey("sebserver.monitoring.exam.action.detail.view"),
|
||||
ImageIcon.SHOW,
|
||||
|
@ -811,7 +817,7 @@ public enum ActionDefinition {
|
|||
new LocTextKey("sebserver.monitoring.exam.connection.action.disable"),
|
||||
ImageIcon.DISABLE,
|
||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||
ActionCategory.CLIENT_EVENT_LIST),
|
||||
ActionCategory.EXAM_MONITORING_3),
|
||||
|
||||
MONITOR_EXAM_HIDE_REQUESTED_CONNECTION(
|
||||
new LocTextKey("sebserver.monitoring.exam.connection.action.hide.requested"),
|
||||
|
|
|
@ -94,6 +94,7 @@ public class ProctoringSettingsPopup {
|
|||
.withAttribute(
|
||||
PageContext.AttributeKeys.FORCE_READ_ONLY,
|
||||
(modifyGrant) ? Constants.FALSE_STRING : Constants.TRUE_STRING);
|
||||
|
||||
final ModalInputDialog<FormHandle<?>> dialog =
|
||||
new ModalInputDialog<FormHandle<?>>(
|
||||
action.pageContext().getParent().getShell(),
|
||||
|
|
|
@ -52,8 +52,8 @@ import ch.ethz.seb.sebserver.gui.service.push.UpdateErrorHandler;
|
|||
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.GetExamProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.ConfirmPendingClientNotification;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionData;
|
||||
|
@ -365,7 +365,7 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
.withConfirm(() -> CONFIRM_QUIT)
|
||||
.withExec(action -> {
|
||||
this.instructionProcessor.propagateSEBQuitInstruction(
|
||||
exam.id,
|
||||
exam.getModelId(),
|
||||
connectionToken,
|
||||
pageContext);
|
||||
return action;
|
||||
|
|
|
@ -55,8 +55,8 @@ import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
|||
import ch.ethz.seb.sebserver.gui.service.push.ServerPushService;
|
||||
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.GetExamProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators;
|
||||
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.FullPageMonitoringGUIUpdate;
|
||||
|
@ -93,6 +93,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
private final AsyncRunner asyncRunner;
|
||||
private final InstructionProcessor instructionProcessor;
|
||||
private final MonitoringExamSearchPopup monitoringExamSearchPopup;
|
||||
private final SEBSendLockPopup sebSendLockPopup;
|
||||
private final MonitoringProctoringService monitoringProctoringService;
|
||||
private final boolean distributedSetup;
|
||||
private final long pollInterval;
|
||||
|
@ -103,6 +104,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
final AsyncRunner asyncRunner,
|
||||
final InstructionProcessor instructionProcessor,
|
||||
final MonitoringExamSearchPopup monitoringExamSearchPopup,
|
||||
final SEBSendLockPopup sebSendLockPopup,
|
||||
final MonitoringProctoringService monitoringProctoringService,
|
||||
final GuiServiceInfo guiServiceInfo,
|
||||
@Value("${sebserver.gui.webservice.poll-interval:2000}") final long pollInterval) {
|
||||
|
@ -117,6 +119,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
this.pollInterval = pollInterval;
|
||||
this.distributedSetup = guiServiceInfo.isDistributedSetup();
|
||||
this.monitoringExamSearchPopup = monitoringExamSearchPopup;
|
||||
this.sebSendLockPopup = sebSendLockPopup;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -177,11 +180,48 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
pageContext,
|
||||
ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION,
|
||||
ActionDefinition.MONITOR_EXAM_QUIT_SELECTED,
|
||||
ActionDefinition.MONITOR_EXAM_LOCK_SELECTED,
|
||||
ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION,
|
||||
ActionDefinition.MONITOR_EXAM_NEW_PROCTOR_ROOM));
|
||||
|
||||
actionBuilder
|
||||
|
||||
.newAction(ActionDefinition.MONITORING_EXAM_SEARCH_CONNECTIONS)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(this::openSearchPopup)
|
||||
.noEventPropagation()
|
||||
.publishIf(isExamSupporter)
|
||||
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_QUIT_ALL)
|
||||
.withEntityKey(entityKey)
|
||||
.withConfirm(() -> CONFIRM_QUIT_ALL)
|
||||
.withExec(action -> this.quitSEBClients(action, clientTable, true))
|
||||
.noEventPropagation()
|
||||
.publishIf(isExamSupporter)
|
||||
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_QUIT_SELECTED)
|
||||
.withEntityKey(entityKey)
|
||||
.withConfirm(() -> CONFIRM_QUIT_SELECTED)
|
||||
.withSelect(
|
||||
() -> this.selectionForInstruction(clientTable),
|
||||
action -> this.quitSEBClients(action, clientTable, false),
|
||||
EMPTY_ACTIVE_SELECTION_TEXT_KEY)
|
||||
.noEventPropagation()
|
||||
.publishIf(isExamSupporter, false)
|
||||
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_LOCK_SELECTED)
|
||||
.withEntityKey(entityKey)
|
||||
.withSelect(
|
||||
() -> this.selectionForInstruction(clientTable),
|
||||
action -> this.sebSendLockPopup.show(
|
||||
action,
|
||||
statesPredicate -> clientTable.getConnectionTokens(
|
||||
statesPredicate,
|
||||
true)),
|
||||
EMPTY_ACTIVE_SELECTION_TEXT_KEY)
|
||||
.noEventPropagation()
|
||||
.publishIf(isExamSupporter, false)
|
||||
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION)
|
||||
.withParentEntityKey(entityKey)
|
||||
.withExec(pageAction -> {
|
||||
|
@ -202,29 +242,6 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
})
|
||||
.publishIf(isExamSupporter, false)
|
||||
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_QUIT_ALL)
|
||||
.withEntityKey(entityKey)
|
||||
.withConfirm(() -> CONFIRM_QUIT_ALL)
|
||||
.withExec(action -> this.quitSEBClients(action, clientTable, true))
|
||||
.noEventPropagation()
|
||||
.publishIf(isExamSupporter)
|
||||
|
||||
.newAction(ActionDefinition.MONITORING_EXAM_SEARCH_CONNECTIONS)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(this::openSearchPopup)
|
||||
.noEventPropagation()
|
||||
.publishIf(isExamSupporter)
|
||||
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_QUIT_SELECTED)
|
||||
.withEntityKey(entityKey)
|
||||
.withConfirm(() -> CONFIRM_QUIT_SELECTED)
|
||||
.withSelect(
|
||||
() -> this.selectionForQuitInstruction(clientTable),
|
||||
action -> this.quitSEBClients(action, clientTable, false),
|
||||
EMPTY_ACTIVE_SELECTION_TEXT_KEY)
|
||||
.noEventPropagation()
|
||||
.publishIf(isExamSupporter, false)
|
||||
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION)
|
||||
.withEntityKey(entityKey)
|
||||
.withConfirm(() -> CONFIRM_DISABLE_SELECTED)
|
||||
|
@ -461,7 +478,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
};
|
||||
}
|
||||
|
||||
private Set<EntityKey> selectionForQuitInstruction(final ClientConnectionTable clientTable) {
|
||||
private Set<EntityKey> selectionForInstruction(final ClientConnectionTable clientTable) {
|
||||
final Set<String> connectionTokens = clientTable.getConnectionTokens(
|
||||
cc -> cc.status.clientActiveStatus,
|
||||
true);
|
||||
|
@ -478,7 +495,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
final boolean all) {
|
||||
|
||||
this.instructionProcessor.propagateSEBQuitInstruction(
|
||||
clientTable.getExam().id,
|
||||
clientTable.getExam().getModelId(),
|
||||
statesPredicate -> clientTable.getConnectionTokens(
|
||||
statesPredicate,
|
||||
!all),
|
||||
|
@ -489,6 +506,23 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
return action;
|
||||
}
|
||||
|
||||
// private PageAction lockSEBClients(
|
||||
// final PageAction action,
|
||||
// final ClientConnectionTable clientTable,
|
||||
// final boolean all) {
|
||||
//
|
||||
// this.instructionProcessor.propagateSEBLockInstruction(
|
||||
// clientTable.getExam().getModelId(),
|
||||
// statesPredicate -> clientTable.getConnectionTokens(
|
||||
// statesPredicate,
|
||||
// !all),
|
||||
// action.pageContext());
|
||||
//
|
||||
// clientTable.removeSelection();
|
||||
// clientTable.forceUpdateAll();
|
||||
// return action;
|
||||
// }
|
||||
|
||||
private PageAction disableSEBClients(
|
||||
final PageAction action,
|
||||
final ClientConnectionTable clientTable,
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.monitoring;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.tomcat.util.buf.StringUtils;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
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.ClientInstruction;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
|
||||
import ch.ethz.seb.sebserver.gui.form.FormHandle;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.ModalInputDialogComposer;
|
||||
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.page.impl.PageAction;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionPage;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.InstructionProcessor;
|
||||
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class SEBSendLockPopup {
|
||||
|
||||
private static final LocTextKey TITLE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.lock.title");
|
||||
private static final LocTextKey FORM_INFO_TITLE =
|
||||
new LocTextKey("sebserver.monitoring.lock.form.info.title");
|
||||
private static final LocTextKey FORM_INFO =
|
||||
new LocTextKey("sebserver.monitoring.lock.form.info");
|
||||
private static final LocTextKey FORM_MESSAGE =
|
||||
new LocTextKey("sebserver.monitoring.lock.form.message");
|
||||
|
||||
private static final LocTextKey TABLE_TITLE =
|
||||
new LocTextKey("sebserver.monitoring.lock.list.title");
|
||||
private static final LocTextKey TABLE_COLUMN_NAME =
|
||||
new LocTextKey("sebserver.monitoring.lock.list.name");
|
||||
private static final LocTextKey TABLE_COLUMN_INFO =
|
||||
new LocTextKey("sebserver.monitoring.lock.list.info");
|
||||
|
||||
private final PageService pageService;
|
||||
private final InstructionProcessor instructionProcessor;
|
||||
|
||||
protected SEBSendLockPopup(
|
||||
final PageService pageService,
|
||||
final InstructionProcessor instructionProcessor) {
|
||||
|
||||
this.pageService = pageService;
|
||||
this.instructionProcessor = instructionProcessor;
|
||||
}
|
||||
|
||||
public PageAction show(
|
||||
final PageAction action,
|
||||
final Function<Predicate<ClientConnection>, Set<String>> selectionFunction) {
|
||||
|
||||
final PageContext pageContext = action.pageContext();
|
||||
final Set<String> selection = selectionFunction.apply(ClientConnection.getStatusPredicate(
|
||||
ConnectionStatus.CONNECTION_REQUESTED,
|
||||
ConnectionStatus.ACTIVE));
|
||||
|
||||
if (selection == null || selection.isEmpty()) {
|
||||
action
|
||||
.pageContext()
|
||||
.publishInfo(new LocTextKey("sebserver.monitoring.lock.noselection"));
|
||||
return action;
|
||||
}
|
||||
|
||||
final String connectionTokens = StringUtils.join(selection, Constants.LIST_SEPARATOR_CHAR);
|
||||
final boolean showList = selection.size() > 1;
|
||||
final PopupComposer popupComposer = new PopupComposer(
|
||||
this.pageService,
|
||||
pageContext,
|
||||
connectionTokens,
|
||||
showList);
|
||||
|
||||
final ModalInputDialog<FormHandle<?>> dialog =
|
||||
new ModalInputDialog<FormHandle<?>>(
|
||||
action.pageContext().getParent().getShell(),
|
||||
this.pageService.getWidgetFactory())
|
||||
.setDialogWidth(800)
|
||||
.setDialogHeight(showList ? 500 : 200);
|
||||
|
||||
final Predicate<FormHandle<?>> doLock = formHandle -> propagateLockInstruction(
|
||||
connectionTokens,
|
||||
pageContext,
|
||||
formHandle);
|
||||
|
||||
dialog.open(
|
||||
TITLE_TEXT_KEY,
|
||||
doLock,
|
||||
Utils.EMPTY_EXECUTION,
|
||||
popupComposer);
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
private final class PopupComposer implements ModalInputDialogComposer<FormHandle<?>> {
|
||||
|
||||
private final PageService pageService;
|
||||
private final PageContext pageContext;
|
||||
private final String connectionTokens;
|
||||
private final boolean showList;
|
||||
|
||||
protected PopupComposer(
|
||||
final PageService pageService,
|
||||
final PageContext pageContext,
|
||||
final String connectionTokens,
|
||||
final boolean showList) {
|
||||
|
||||
this.pageService = pageService;
|
||||
this.pageContext = pageContext;
|
||||
this.connectionTokens = connectionTokens;
|
||||
this.showList = showList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<FormHandle<?>> compose(final Composite parent) {
|
||||
final EntityKey examKey = this.pageContext.getEntityKey();
|
||||
final RestService restService = this.pageService.getRestService();
|
||||
|
||||
final PageContext formContext = this.pageContext.copyOf(parent);
|
||||
final FormHandle<?> form = this.pageService.formBuilder(formContext)
|
||||
.addField(FormBuilder.text(
|
||||
"Info",
|
||||
FORM_INFO_TITLE,
|
||||
this.pageService.getI18nSupport().getText(FORM_INFO))
|
||||
.asArea(50)
|
||||
.asHTML()
|
||||
.readonly(true))
|
||||
.addField(FormBuilder.text(
|
||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_FORCE_LOCK_SCREEN.MESSAGE,
|
||||
FORM_MESSAGE)
|
||||
.asArea(50)
|
||||
.asHTML())
|
||||
.build();
|
||||
|
||||
if (this.showList) {
|
||||
this.pageService
|
||||
.getWidgetFactory()
|
||||
.labelLocalized(parent, CustomVariant.TEXT_H3, TABLE_TITLE);
|
||||
|
||||
// table of selected SEB connections
|
||||
this.pageService
|
||||
.entityTableBuilder(restService.getRestCall(GetClientConnectionPage.class))
|
||||
.withStaticFilter(
|
||||
ClientConnection.FILTER_ATTR_TOKEN_LIST,
|
||||
this.connectionTokens)
|
||||
.withStaticFilter(
|
||||
ClientConnection.FILTER_ATTR_EXAM_ID,
|
||||
examKey.modelId)
|
||||
.withPaging(10)
|
||||
|
||||
.withColumn(new ColumnDefinition<>(
|
||||
Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID,
|
||||
TABLE_COLUMN_NAME,
|
||||
ClientConnection::getUserSessionId))
|
||||
|
||||
.withColumn(new ColumnDefinition<>(
|
||||
ClientConnection.ATTR_INFO,
|
||||
TABLE_COLUMN_INFO,
|
||||
ClientConnection::getInfo))
|
||||
|
||||
.compose(formContext);
|
||||
}
|
||||
|
||||
return () -> form;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean propagateLockInstruction(
|
||||
final String connectionTokens,
|
||||
final PageContext pageContext,
|
||||
final FormHandle<?> formHandle) {
|
||||
|
||||
final EntityKey examKey = pageContext.getEntityKey();
|
||||
final String lockMessage = formHandle
|
||||
.getForm()
|
||||
.getFieldValue(ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_FORCE_LOCK_SCREEN.MESSAGE);
|
||||
|
||||
this.instructionProcessor.propagateSEBLockInstruction(
|
||||
examKey.modelId,
|
||||
lockMessage,
|
||||
null,
|
||||
connectionTokens,
|
||||
pageContext);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -9,6 +9,8 @@
|
|||
package ch.ethz.seb.sebserver.gui.service.session;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
@ -55,7 +57,7 @@ public class InstructionProcessor {
|
|||
}
|
||||
|
||||
public void propagateSEBQuitInstruction(
|
||||
final Long examId,
|
||||
final String examId,
|
||||
final String connectionToken,
|
||||
final PageContext pageContext) {
|
||||
|
||||
|
@ -67,40 +69,89 @@ public class InstructionProcessor {
|
|||
}
|
||||
|
||||
public void propagateSEBQuitInstruction(
|
||||
final Long examId,
|
||||
final String examId,
|
||||
final Function<Predicate<ClientConnection>, Set<String>> selectionFunction,
|
||||
final PageContext pageContext) {
|
||||
|
||||
final Set<String> connectionTokens = selectionFunction
|
||||
.apply(ClientConnection.getStatusPredicate(
|
||||
ConnectionStatus.CONNECTION_REQUESTED,
|
||||
ConnectionStatus.ACTIVE));
|
||||
try {
|
||||
final Set<String> connectionTokens = selectionFunction
|
||||
.apply(ClientConnection.getStatusPredicate(
|
||||
ConnectionStatus.CONNECTION_REQUESTED,
|
||||
ConnectionStatus.ACTIVE));
|
||||
|
||||
if (connectionTokens.isEmpty()) {
|
||||
log.warn("Empty selection");
|
||||
return;
|
||||
if (connectionTokens.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Propagate SEB quit instruction for exam: {} and connections: {}",
|
||||
examId,
|
||||
connectionTokens);
|
||||
}
|
||||
|
||||
final ClientInstruction clientInstruction = new ClientInstruction(
|
||||
null,
|
||||
Long.valueOf(examId),
|
||||
InstructionType.SEB_QUIT,
|
||||
StringUtils.join(connectionTokens, Constants.LIST_SEPARATOR),
|
||||
null);
|
||||
|
||||
processInstruction(() -> this.restService.getBuilder(PropagateInstruction.class)
|
||||
.withURIVariable(API.PARAM_PARENT_MODEL_ID, String.valueOf(examId))
|
||||
.withBody(clientInstruction)
|
||||
.call()
|
||||
.getOrThrow(),
|
||||
pageContext);
|
||||
|
||||
} catch (final Exception e) {
|
||||
pageContext.notifyUnexpectedError(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Propagate SEB quit instruction for exam: {} and connections: {}",
|
||||
examId,
|
||||
connectionTokens);
|
||||
public void propagateSEBLockInstruction(
|
||||
final String examId,
|
||||
final String message,
|
||||
final String imageURL,
|
||||
final String connectionTokens,
|
||||
final PageContext pageContext) {
|
||||
|
||||
try {
|
||||
|
||||
if (connectionTokens.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Propagate SEB lock instruction for exam: {} and connections: {}",
|
||||
examId,
|
||||
connectionTokens);
|
||||
}
|
||||
|
||||
final Map<String, String> attributes = new HashMap<>();
|
||||
if (StringUtils.isNotBlank(message)) {
|
||||
attributes.put(ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_FORCE_LOCK_SCREEN.MESSAGE, message);
|
||||
}
|
||||
if (StringUtils.isNotBlank(imageURL)) {
|
||||
attributes.put(ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_FORCE_LOCK_SCREEN.IMAGE_URL, imageURL);
|
||||
}
|
||||
|
||||
final ClientInstruction clientInstruction = new ClientInstruction(
|
||||
null,
|
||||
Long.valueOf(examId),
|
||||
InstructionType.SEB_FORCE_LOCK_SCREEN,
|
||||
connectionTokens,
|
||||
attributes);
|
||||
|
||||
processInstruction(() -> this.restService.getBuilder(PropagateInstruction.class)
|
||||
.withURIVariable(API.PARAM_PARENT_MODEL_ID, examId)
|
||||
.withBody(clientInstruction)
|
||||
.call()
|
||||
.getOrThrow(),
|
||||
pageContext);
|
||||
|
||||
} catch (final Exception e) {
|
||||
pageContext.notifyUnexpectedError(e);
|
||||
}
|
||||
|
||||
final ClientInstruction clientInstruction = new ClientInstruction(
|
||||
null,
|
||||
examId,
|
||||
InstructionType.SEB_QUIT,
|
||||
StringUtils.join(connectionTokens, Constants.LIST_SEPARATOR),
|
||||
null);
|
||||
|
||||
processInstruction(() -> this.restService.getBuilder(PropagateInstruction.class)
|
||||
.withURIVariable(API.PARAM_PARENT_MODEL_ID, String.valueOf(examId))
|
||||
.withBody(clientInstruction)
|
||||
.call()
|
||||
.getOrThrow(),
|
||||
pageContext);
|
||||
|
||||
}
|
||||
|
||||
public void disableConnection(
|
||||
|
@ -113,6 +164,7 @@ public class InstructionProcessor {
|
|||
ConnectionStatus.CONNECTION_REQUESTED,
|
||||
ConnectionStatus.UNDEFINED,
|
||||
ConnectionStatus.CLOSED,
|
||||
ConnectionStatus.ACTIVE,
|
||||
ConnectionStatus.AUTHENTICATED));
|
||||
|
||||
if (connectionTokens.isEmpty()) {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -206,6 +207,15 @@ public class FilterMap extends POSTMapper {
|
|||
return Utils.toSQLWildcard(this.params.getFirst(name));
|
||||
}
|
||||
|
||||
public List<String> getClientConnectionTokenList() {
|
||||
final String tokenList = getString(ClientConnection.FILTER_ATTR_TOKEN_LIST);
|
||||
if (StringUtils.isBlank(tokenList)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Utils.asImmutableList(StringUtils.split(tokenList, Constants.LIST_SEPARATOR));
|
||||
}
|
||||
|
||||
public Long getClientConnectionExamId() {
|
||||
return getLong(ClientConnection.FILTER_ATTR_EXAM_ID);
|
||||
}
|
||||
|
|
|
@ -125,6 +125,9 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
|
|||
ClientConnectionRecordDynamicSqlSupport.institutionId,
|
||||
isEqualToWhenPresent(filterMap.getInstitutionId()));
|
||||
return whereClause
|
||||
.and(
|
||||
ClientConnectionRecordDynamicSqlSupport.connectionToken,
|
||||
isInWhenPresent(filterMap.getClientConnectionTokenList()))
|
||||
.and(
|
||||
ClientConnectionRecordDynamicSqlSupport.examId,
|
||||
isEqualToWhenPresent(filterMap.getClientConnectionExamId()))
|
||||
|
|
|
@ -63,6 +63,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
|||
ConnectionStatus.UNDEFINED,
|
||||
ConnectionStatus.CONNECTION_REQUESTED,
|
||||
ConnectionStatus.AUTHENTICATED,
|
||||
ConnectionStatus.ACTIVE,
|
||||
ConnectionStatus.CLOSED);
|
||||
|
||||
private final ExamSessionService examSessionService;
|
||||
|
|
|
@ -1873,6 +1873,8 @@ sebserver.monitoring.exam.connection.action.instruction.quit.all=Quit All SEB Cl
|
|||
sebserver.monitoring.exam.connection.action.instruction.quit.confirm=Are you sure to quit this SEB client connection?
|
||||
sebserver.monitoring.exam.connection.action.instruction.quit.selected.confirm=Are you sure to quit all selected, active SEB client connections?
|
||||
sebserver.monitoring.exam.connection.action.instruction.quit.all.confirm=Are you sure to quit all active SEB client connections?
|
||||
sebserver.monitoring.exam.connection.action.instruction.lock.selected=Lock Selected SEB Clients
|
||||
sebserver.monitoring.exam.connection.action.instruction.lock.confirm=Are you sure to lock this SEB client connection?
|
||||
sebserver.monitoring.exam.connection.action.instruction.disable.selected.confirm=Are you sure to disable all selected SEB client connections?
|
||||
sebserver.monitoring.exam.connection.action.instruction.disable.all.confirm=Are you sure to disable all active SEB client connections?
|
||||
sebserver.monitoring.exam.connection.action.disable=Mark As Canceled
|
||||
|
@ -1891,6 +1893,7 @@ sebserver.monitoring.exam.connection.action.proctoring.examroom=Exam Room Procto
|
|||
sebserver.monitoring.exam.connection.action.openTownhall.confirm=You are about to open the town-hall room and force all SEB clients to join the town-hall room.<br/>Are you sure to open the town-hall?
|
||||
sebserver.monitoring.exam.connection.action.closeTownhall.confirm=You are about to close the town-hall room and force all SEB clients to join it's proctoring room.<br/>Are you sure to close the town-hall?
|
||||
sebserver.monitoring.exam.connection.action.singleroom.confirm=You are about to open the single/one to one room for this participant.<br/>Are you sure you want to open the single room?
|
||||
sebserver.monitoring.exam.connection.selected.actions=
|
||||
|
||||
sebserver.monitoring.exam.connection.notificationlist.actions=
|
||||
sebserver.monitoring.exam.connection.action.confirm.notification=Confirm Notification
|
||||
|
@ -1936,6 +1939,16 @@ sebserver.monitoring.exam.connection.status.CLOSED=Closed
|
|||
sebserver.monitoring.exam.connection.status.ABORTED=Aborted
|
||||
sebserver.monitoring.exam.connection.status.DISABLED=Canceled
|
||||
|
||||
sebserver.monitoring.lock.title=Lock SEB Clients
|
||||
sebserver.monitoring.lock.form.info.title=Info
|
||||
sebserver.monitoring.lock.form.info=Please check all selected SEB connection<br/>before sending lock by click on "OK"
|
||||
sebserver.monitoring.lock.form.message=Lock Message
|
||||
sebserver.monitoring.lock.form.message.tooltip=A message that will be displayed by the SEB with the lock screen to the user
|
||||
sebserver.monitoring.lock.list.title=Selected SEB Client Connections
|
||||
sebserver.monitoring.lock.list.name=SEB User Session Identifier
|
||||
sebserver.monitoring.lock.list.info=SEB Connection Info
|
||||
sebserver.monitoring.lock.noselection=Please select at least one active SEB client connection.
|
||||
|
||||
################################
|
||||
# Finished Exams
|
||||
################################
|
||||
|
|
Loading…
Reference in a new issue