fixed quit JSON
This commit is contained in:
parent
857706c7b7
commit
1ecaa132a9
18 changed files with 427 additions and 220 deletions
|
@ -57,6 +57,7 @@ public final class Constants {
|
|||
public static final Character LIST_SEPARATOR_CHAR = COMMA;
|
||||
public static final Character COMPLEX_VALUE_SEPARATOR = COLON;
|
||||
|
||||
public static final String NULL = "null";
|
||||
public static final String PERCENTAGE_STRING = Constants.PERCENTAGE.toString();
|
||||
public static final String LIST_SEPARATOR = COMMA.toString();
|
||||
public static final String EMBEDDED_LIST_SEPARATOR = PIPE.toString();
|
||||
|
|
|
@ -157,6 +157,7 @@ public final class API {
|
|||
public static final String EXAM_MONITORING_DISABLE_CONNECTION_ENDPOINT = "/disable-connection";
|
||||
public static final String EXAM_MONITORING_SEB_CONNECTION_TOKEN_PATH_SEGMENT =
|
||||
"/{" + EXAM_API_SEB_CONNECTION_TOKEN + "}";
|
||||
public static final String EXAM_MONITORING_STATE_FILTER = "hidden-states";
|
||||
|
||||
public static final String SEB_CLIENT_CONNECTION_ENDPOINT = "/seb-client-connection";
|
||||
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* 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.widgets.Composite;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
|
||||
import ch.ethz.seb.sebserver.gui.service.ResourceService;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
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.PageService.PageActionBuilder;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
||||
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.seb.examconfig.GetExamConfigNodePage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck;
|
||||
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
|
||||
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute;
|
||||
import ch.ethz.seb.sebserver.gui.table.EntityTable;
|
||||
import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class ConfigTemplateList implements TemplateComposer {
|
||||
|
||||
private static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION =
|
||||
new LocTextKey("sebserver.examconfig.list.action.no.modify.privilege");
|
||||
private static final LocTextKey TITLE_TEMPLATE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.configtemplate.list.title");
|
||||
private static final LocTextKey EMPTY_TEMPLATE_LIST_TEXT_KEY =
|
||||
new LocTextKey("sebserver.configtemplate.list.empty");
|
||||
private static final LocTextKey INSTITUTION_TEXT_KEY =
|
||||
new LocTextKey("sebserver.examconfig.list.column.institution");
|
||||
private static final LocTextKey NAME_TEXT_KEY =
|
||||
new LocTextKey("sebserver.examconfig.list.column.name");
|
||||
private static final LocTextKey DESCRIPTION_TEXT_KEY =
|
||||
new LocTextKey("sebserver.examconfig.list.column.description");
|
||||
private static final LocTextKey EMPTY_TEMPLATE_SELECTION_TEXT_KEY =
|
||||
new LocTextKey("sebserver.configtemplate.info.pleaseSelect");
|
||||
|
||||
private final PageService pageService;
|
||||
private final RestService restService;
|
||||
private final CurrentUser currentUser;
|
||||
private final ResourceService resourceService;
|
||||
private final int pageSize;
|
||||
|
||||
private final TableFilterAttribute institutionFilter;
|
||||
private final TableFilterAttribute nameFilter =
|
||||
new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME);
|
||||
private final TableFilterAttribute descFilter =
|
||||
new TableFilterAttribute(CriteriaType.TEXT, ConfigurationNode.FILTER_ATTR_DESCRIPTION);
|
||||
|
||||
protected ConfigTemplateList(
|
||||
final PageService pageService,
|
||||
final RestService restService,
|
||||
final CurrentUser currentUser,
|
||||
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
||||
|
||||
this.pageService = pageService;
|
||||
this.restService = restService;
|
||||
this.currentUser = currentUser;
|
||||
this.resourceService = pageService.getResourceService();
|
||||
this.pageSize = pageSize;
|
||||
|
||||
this.institutionFilter = new TableFilterAttribute(
|
||||
CriteriaType.SINGLE_SELECTION,
|
||||
Entity.FILTER_ATTR_INSTITUTION,
|
||||
this.resourceService::institutionResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compose(final PageContext pageContext) {
|
||||
final WidgetFactory widgetFactory = this.pageService.getWidgetFactory();
|
||||
final Composite content = widgetFactory.defaultPageLayout(
|
||||
pageContext.getParent(),
|
||||
TITLE_TEMPLATE_TEXT_KEY);
|
||||
|
||||
final boolean isSEBAdmin = this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN);
|
||||
final PageActionBuilder pageActionBuilder =
|
||||
this.pageService.pageActionBuilder(pageContext.clearEntityKeys());
|
||||
|
||||
final EntityTable<ConfigurationNode> templateTable =
|
||||
this.pageService.entityTableBuilder(
|
||||
TITLE_TEMPLATE_TEXT_KEY.name,
|
||||
this.restService.getRestCall(GetExamConfigNodePage.class))
|
||||
.withStaticFilter(
|
||||
Domain.CONFIGURATION_NODE.ATTR_TYPE,
|
||||
ConfigurationType.TEMPLATE.name())
|
||||
.withEmptyMessage(EMPTY_TEMPLATE_LIST_TEXT_KEY)
|
||||
.withPaging(this.pageSize)
|
||||
.withColumnIf(
|
||||
() -> isSEBAdmin,
|
||||
() -> new ColumnDefinition<>(
|
||||
Domain.LMS_SETUP.ATTR_INSTITUTION_ID,
|
||||
INSTITUTION_TEXT_KEY,
|
||||
this.resourceService::localizedExamConfigInstitutionName)
|
||||
.withFilter(this.institutionFilter)
|
||||
.sortable())
|
||||
.withColumn(new ColumnDefinition<>(
|
||||
Domain.CONFIGURATION_NODE.ATTR_NAME,
|
||||
NAME_TEXT_KEY,
|
||||
ConfigurationNode::getName)
|
||||
.withFilter(this.nameFilter)
|
||||
.sortable())
|
||||
.withColumn(new ColumnDefinition<>(
|
||||
Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION,
|
||||
DESCRIPTION_TEXT_KEY,
|
||||
ConfigurationNode::getDescription)
|
||||
.withFilter(this.descFilter)
|
||||
.sortable())
|
||||
.withDefaultAction(pageActionBuilder
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_VIEW_FROM_LIST)
|
||||
.create())
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
final GrantCheck examConfigGrant = this.currentUser.grantCheck(EntityType.CONFIGURATION_NODE);
|
||||
pageActionBuilder
|
||||
// Exam Configuration template actions...
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_NEW)
|
||||
.publishIf(examConfigGrant::iw)
|
||||
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_VIEW_FROM_LIST)
|
||||
.withSelect(templateTable::getSelection, PageAction::applySingleSelectionAsEntityKey,
|
||||
EMPTY_TEMPLATE_SELECTION_TEXT_KEY)
|
||||
.publishIf(() -> templateTable.hasAnyContent())
|
||||
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_MODIFY_FROM_LIST)
|
||||
.withSelect(
|
||||
templateTable.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION),
|
||||
PageAction::applySingleSelectionAsEntityKey, EMPTY_TEMPLATE_SELECTION_TEXT_KEY)
|
||||
.publishIf(() -> examConfigGrant.im() && templateTable.hasAnyContent());
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -192,43 +192,77 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
action -> this.disableSebClients(action, clientTable, false),
|
||||
EMPTY_SELECTION_TEXT_KEY)
|
||||
.noEventPropagation()
|
||||
.publishIf(privilege)
|
||||
|
||||
;
|
||||
|
||||
clientTable.hideStatus(ConnectionStatus.DISABLED);
|
||||
.publishIf(privilege);
|
||||
|
||||
if (privilege.getAsBoolean()) {
|
||||
|
||||
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.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();
|
||||
}
|
||||
|
||||
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.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();
|
||||
}
|
||||
|
||||
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();
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
package ch.ethz.seb.sebserver.gui.content;
|
||||
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
@ -46,12 +47,8 @@ public class SebExamConfigList implements TemplateComposer {
|
|||
new LocTextKey("sebserver.examconfig.list.action.no.modify.privilege");
|
||||
private static final LocTextKey EMPTY_CONFIG_LIST_TEXT_KEY =
|
||||
new LocTextKey("sebserver.examconfig.list.empty");
|
||||
private static final LocTextKey EMPTY_TEMPLATE_LIST_TEXT_KEY =
|
||||
new LocTextKey("sebserver.configtemplate.list.empty");
|
||||
private static final LocTextKey TITLE_CONFIGURATION_TEXT_KEY =
|
||||
new LocTextKey("sebserver.examconfig.list.title");
|
||||
private static final LocTextKey TITLE_TEMPLATE_TEXT_KEY =
|
||||
new LocTextKey("sebserver.configtemplate.list.title");
|
||||
private static final LocTextKey INSTITUTION_TEXT_KEY =
|
||||
new LocTextKey("sebserver.examconfig.list.column.institution");
|
||||
private static final LocTextKey NAME_TEXT_KEY =
|
||||
|
@ -62,28 +59,31 @@ public class SebExamConfigList implements TemplateComposer {
|
|||
new LocTextKey("sebserver.examconfig.list.column.status");
|
||||
private static final LocTextKey EMPTY_SELECTION_TEXT_KEY =
|
||||
new LocTextKey("sebserver.examconfig.info.pleaseSelect");
|
||||
private static final LocTextKey EMPTY_TEMPLATE_SELECTION_TEXT_KEY =
|
||||
new LocTextKey("sebserver.configtemplate.info.pleaseSelect");
|
||||
|
||||
private final TableFilterAttribute institutionFilter;
|
||||
private final TableFilterAttribute nameFilter =
|
||||
new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME);
|
||||
private final TableFilterAttribute descFilter =
|
||||
new TableFilterAttribute(CriteriaType.TEXT, ConfigurationNode.FILTER_ATTR_DESCRIPTION);
|
||||
private final TableFilterAttribute statusFilter;
|
||||
|
||||
private final PageService pageService;
|
||||
private final RestService restService;
|
||||
private final CurrentUser currentUser;
|
||||
private final ResourceService resourceService;
|
||||
private final int pageSize;
|
||||
|
||||
protected SebExamConfigList(
|
||||
final PageService pageService,
|
||||
final RestService restService,
|
||||
final CurrentUser currentUser) {
|
||||
final CurrentUser currentUser,
|
||||
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
||||
|
||||
this.pageService = pageService;
|
||||
this.restService = restService;
|
||||
this.currentUser = currentUser;
|
||||
this.resourceService = pageService.getResourceService();
|
||||
this.pageSize = pageSize;
|
||||
|
||||
this.institutionFilter = new TableFilterAttribute(
|
||||
CriteriaType.SINGLE_SELECTION,
|
||||
|
@ -99,7 +99,6 @@ public class SebExamConfigList implements TemplateComposer {
|
|||
@Override
|
||||
public void compose(final PageContext pageContext) {
|
||||
final WidgetFactory widgetFactory = this.pageService.getWidgetFactory();
|
||||
|
||||
final Composite content = widgetFactory.defaultPageLayout(
|
||||
pageContext.getParent(),
|
||||
TITLE_CONFIGURATION_TEXT_KEY);
|
||||
|
@ -115,7 +114,7 @@ public class SebExamConfigList implements TemplateComposer {
|
|||
Domain.CONFIGURATION_NODE.ATTR_TYPE,
|
||||
ConfigurationType.EXAM_CONFIG.name())
|
||||
.withEmptyMessage(EMPTY_CONFIG_LIST_TEXT_KEY)
|
||||
.withPaging(6)
|
||||
.withPaging(this.pageSize)
|
||||
.withColumnIf(
|
||||
() -> isSEBAdmin,
|
||||
() -> new ColumnDefinition<>(
|
||||
|
@ -134,7 +133,7 @@ public class SebExamConfigList implements TemplateComposer {
|
|||
Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION,
|
||||
DESCRIPTION_TEXT_KEY,
|
||||
ConfigurationNode::getDescription)
|
||||
.withFilter(this.nameFilter)
|
||||
.withFilter(this.descFilter)
|
||||
.sortable())
|
||||
.withColumn(new ColumnDefinition<ConfigurationNode>(
|
||||
Domain.CONFIGURATION_NODE.ATTR_STATUS,
|
||||
|
@ -147,45 +146,6 @@ public class SebExamConfigList implements TemplateComposer {
|
|||
.create())
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
// configuration template table
|
||||
widgetFactory.label(content, "");
|
||||
widgetFactory.labelLocalizedTitle(
|
||||
content,
|
||||
TITLE_TEMPLATE_TEXT_KEY);
|
||||
widgetFactory.labelSeparator(content);
|
||||
|
||||
final EntityTable<ConfigurationNode> templateTable =
|
||||
this.pageService.entityTableBuilder(this.restService.getRestCall(GetExamConfigNodePage.class))
|
||||
.withStaticFilter(
|
||||
Domain.CONFIGURATION_NODE.ATTR_TYPE,
|
||||
ConfigurationType.TEMPLATE.name())
|
||||
.withEmptyMessage(EMPTY_TEMPLATE_LIST_TEXT_KEY)
|
||||
.withPaging(6)
|
||||
.withColumnIf(
|
||||
() -> isSEBAdmin,
|
||||
() -> new ColumnDefinition<>(
|
||||
Domain.LMS_SETUP.ATTR_INSTITUTION_ID,
|
||||
INSTITUTION_TEXT_KEY,
|
||||
this.resourceService::localizedExamConfigInstitutionName)
|
||||
.withFilter(this.institutionFilter)
|
||||
.sortable())
|
||||
.withColumn(new ColumnDefinition<>(
|
||||
Domain.CONFIGURATION_NODE.ATTR_NAME,
|
||||
NAME_TEXT_KEY,
|
||||
ConfigurationNode::getName)
|
||||
.withFilter(this.nameFilter)
|
||||
.sortable())
|
||||
.withColumn(new ColumnDefinition<>(
|
||||
Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION,
|
||||
DESCRIPTION_TEXT_KEY,
|
||||
ConfigurationNode::getDescription)
|
||||
.withFilter(this.nameFilter)
|
||||
.sortable())
|
||||
.withDefaultAction(pageActionBuilder
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_VIEW_FROM_LIST)
|
||||
.create())
|
||||
.compose(pageContext.copyOf(content));
|
||||
|
||||
final GrantCheck examConfigGrant = this.currentUser.grantCheck(EntityType.CONFIGURATION_NODE);
|
||||
pageActionBuilder
|
||||
// Exam Configuration actions...
|
||||
|
@ -214,20 +174,7 @@ public class SebExamConfigList implements TemplateComposer {
|
|||
.noEventPropagation()
|
||||
.publishIf(() -> examConfigGrant.im())
|
||||
|
||||
// Exam Configuration template actions...
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_NEW)
|
||||
.publishIf(examConfigGrant::iw)
|
||||
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_VIEW_FROM_LIST)
|
||||
.withSelect(templateTable::getSelection, PageAction::applySingleSelectionAsEntityKey,
|
||||
EMPTY_TEMPLATE_SELECTION_TEXT_KEY)
|
||||
.publishIf(() -> templateTable.hasAnyContent())
|
||||
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_MODIFY_FROM_LIST)
|
||||
.withSelect(
|
||||
templateTable.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION),
|
||||
PageAction::applySingleSelectionAsEntityKey, EMPTY_TEMPLATE_SELECTION_TEXT_KEY)
|
||||
.publishIf(() -> examConfigGrant.im() && templateTable.hasAnyContent());
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ public enum ActionCategory {
|
|||
INDICATOR_LIST(new LocTextKey("sebserver.exam.indicator.list.actions"), 2),
|
||||
SEB_CLIENT_CONFIG_LIST(new LocTextKey("sebserver.clientconfig.list.actions"), 1),
|
||||
SEB_EXAM_CONFIG_LIST(new LocTextKey("sebserver.examconfig.list.actions"), 1),
|
||||
SEB_CONFIG_TEMPLATE_LIST(new LocTextKey("sebserver.configtemplate.list.actions"), 2),
|
||||
SEB_CONFIG_TEMPLATE_LIST(new LocTextKey("sebserver.configtemplate.list.actions"), 1),
|
||||
SEB_CONFIG_TEMPLATE_ATTRIBUTE_LIST(new LocTextKey("sebserver.configtemplate.attr.list.actions"), 1),
|
||||
RUNNING_EXAM_LIST(new LocTextKey("sebserver.monitoring.exam.list.actions"), 1),
|
||||
CLIENT_EVENT_LIST(new LocTextKey("sebserver.monitoring.exam.connection.list.actions"), 1),
|
||||
|
|
|
@ -467,6 +467,9 @@ public enum ActionDefinition {
|
|||
PageStateDefinitionImpl.SEB_EXAM_CONFIG_EDIT,
|
||||
ActionCategory.FORM),
|
||||
|
||||
SEB_EXAM_CONFIG_TEMPLATE_LIST(
|
||||
new LocTextKey("sebserver.configtemplate.action.list"),
|
||||
PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_LIST),
|
||||
SEB_EXAM_CONFIG_TEMPLATE_NEW(
|
||||
new LocTextKey("sebserver.configtemplate.action.list.new"),
|
||||
ImageIcon.TEMPLATE,
|
||||
|
|
|
@ -214,6 +214,18 @@ public class ActivitiesPane implements TemplateComposer {
|
|||
.create());
|
||||
}
|
||||
|
||||
// SEB Exam Config Template
|
||||
if (examConfigRead) {
|
||||
final TreeItem examConfigTemplate = this.widgetFactory.treeItemLocalized(
|
||||
sebConfigs,
|
||||
ActivityDefinition.SEB_EXAM_CONFIG_TEMPLATE.displayName);
|
||||
injectActivitySelection(
|
||||
examConfigTemplate,
|
||||
actionBuilder
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_LIST)
|
||||
.create());
|
||||
}
|
||||
|
||||
sebConfigs.setExpanded(this.currentUser.get().hasAnyRole(UserRole.EXAM_ADMIN));
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ public enum ActivityDefinition implements Activity {
|
|||
SEB_CONFIGURATION(new LocTextKey("sebserver.overall.activity.title.sebconfig")),
|
||||
SEB_CLIENT_CONFIG(new LocTextKey("sebserver.clientconfig.action.list")),
|
||||
SEB_EXAM_CONFIG(new LocTextKey("sebserver.examconfig.action.list")),
|
||||
SEB_EXAM_CONFIG_TEMPLATE(new LocTextKey("sebserver.configtemplate.action.list")),
|
||||
MONITORING(new LocTextKey("sebserver.overall.activity.title.monitoring")),
|
||||
MONITORING_EXAMS(new LocTextKey("sebserver.monitoring.action.list")),
|
||||
SEB_CLIENT_LOGS(new LocTextKey("sebserver.logs.activity.seblogs"));
|
||||
|
|
|
@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.gui.content.activity;
|
|||
|
||||
import ch.ethz.seb.sebserver.gui.content.ConfigTemplateAttributeForm;
|
||||
import ch.ethz.seb.sebserver.gui.content.ConfigTemplateForm;
|
||||
import ch.ethz.seb.sebserver.gui.content.ConfigTemplateList;
|
||||
import ch.ethz.seb.sebserver.gui.content.ExamForm;
|
||||
import ch.ethz.seb.sebserver.gui.content.ExamList;
|
||||
import ch.ethz.seb.sebserver.gui.content.IndicatorForm;
|
||||
|
@ -68,8 +69,12 @@ public enum PageStateDefinitionImpl implements PageStateDefinition {
|
|||
SEB_EXAM_CONFIG_EDIT(Type.FORM_IN_TIME_EDIT, SebExamConfigSettingsForm.class, ActivityDefinition.SEB_EXAM_CONFIG),
|
||||
SEB_EXAM_CONFIG_VIEW(Type.FORM_VIEW, SebExamConfigSettingsForm.class, ActivityDefinition.SEB_EXAM_CONFIG),
|
||||
|
||||
SEB_EXAM_CONFIG_TEMPLATE_VIEW(Type.FORM_VIEW, ConfigTemplateForm.class, ActivityDefinition.SEB_EXAM_CONFIG),
|
||||
SEB_EXAM_CONFIG_TEMPLATE_EDIT(Type.FORM_EDIT, ConfigTemplateForm.class, ActivityDefinition.SEB_EXAM_CONFIG),
|
||||
SEB_EXAM_CONFIG_TEMPLATE_LIST(Type.LIST_VIEW, ConfigTemplateList.class,
|
||||
ActivityDefinition.SEB_EXAM_CONFIG_TEMPLATE),
|
||||
SEB_EXAM_CONFIG_TEMPLATE_VIEW(Type.FORM_VIEW, ConfigTemplateForm.class,
|
||||
ActivityDefinition.SEB_EXAM_CONFIG_TEMPLATE),
|
||||
SEB_EXAM_CONFIG_TEMPLATE_EDIT(Type.FORM_EDIT, ConfigTemplateForm.class,
|
||||
ActivityDefinition.SEB_EXAM_CONFIG_TEMPLATE),
|
||||
|
||||
SEB_EXAM_CONFIG_TEMPLATE_ATTRIBUTE_EDIT(
|
||||
Type.FORM_EDIT,
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
package ch.ethz.seb.sebserver.gui.service.session;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
@ -65,6 +66,7 @@ public final class ClientConnectionTable {
|
|||
|
||||
private static final int BOTTOM_PADDING = 20;
|
||||
private static final int NUMBER_OF_NONE_INDICATOR_COLUMNS = 3;
|
||||
private static final String USER_SESSION_STATUS_FILTER_ATTRIBUTE = "USER_SESSION_STATUS_FILTER_ATTRIBUTE";
|
||||
|
||||
private static final String INDICATOR_NAME_TEXT_KEY_PREFIX =
|
||||
"sebserver.monitoring.connection.list.column.indicator.";
|
||||
|
@ -117,6 +119,7 @@ public final class ClientConnectionTable {
|
|||
NUMBER_OF_NONE_INDICATOR_COLUMNS);
|
||||
|
||||
this.statusFilter = EnumSet.noneOf(ConnectionStatus.class);
|
||||
loadStatusFilter();
|
||||
|
||||
this.table = this.widgetFactory.tableLocalized(tableRoot, SWT.MULTI | SWT.V_SCROLL);
|
||||
final GridLayout gridLayout = new GridLayout(3 + indicators.size(), true);
|
||||
|
@ -169,10 +172,45 @@ public final class ClientConnectionTable {
|
|||
|
||||
public void hideStatus(final ConnectionStatus status) {
|
||||
this.statusFilter.add(status);
|
||||
saveStatusFilter();
|
||||
}
|
||||
|
||||
public void showStatus(final ConnectionStatus status) {
|
||||
this.statusFilter.remove(status);
|
||||
saveStatusFilter();
|
||||
}
|
||||
|
||||
private void saveStatusFilter() {
|
||||
try {
|
||||
this.resourceService
|
||||
.getCurrentUser()
|
||||
.putAttribute(
|
||||
USER_SESSION_STATUS_FILTER_ATTRIBUTE,
|
||||
StringUtils.join(this.statusFilter, Constants.LIST_SEPARATOR));
|
||||
} catch (final Exception e) {
|
||||
log.warn("Failed to save status filter to user session");
|
||||
}
|
||||
}
|
||||
|
||||
private void loadStatusFilter() {
|
||||
try {
|
||||
final String attribute = this.resourceService
|
||||
.getCurrentUser()
|
||||
.getAttribute(USER_SESSION_STATUS_FILTER_ATTRIBUTE);
|
||||
if (attribute != null) {
|
||||
this.statusFilter.clear();
|
||||
Arrays.asList(StringUtils.split(attribute, Constants.LIST_SEPARATOR))
|
||||
.forEach(name -> this.statusFilter.add(ConnectionStatus.valueOf(name)));
|
||||
|
||||
} else {
|
||||
this.statusFilter.clear();
|
||||
this.statusFilter.add(ConnectionStatus.DISABLED);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
log.warn("Failed to load status filter to user session");
|
||||
this.statusFilter.clear();
|
||||
this.statusFilter.add(ConnectionStatus.DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
public void withDefaultAction(final PageAction pageAction, final PageService pageService) {
|
||||
|
|
|
@ -10,52 +10,45 @@ package ch.ethz.seb.sebserver.gui.widget;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.rap.rwt.widgets.DropDown;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Combo;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.eclipse.swt.widgets.Event;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Listener;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
|
||||
|
||||
public final class MultiSelectionCombo extends Composite implements Selection {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MultiSelectionCombo.class);
|
||||
private static final long serialVersionUID = -7787134114963647332L;
|
||||
private static final int ACTION_COLUMN_WIDTH = 20;
|
||||
|
||||
private static final LocTextKey DEFAULT_ADD_TOOLTIP_KEY = new LocTextKey("sebserver.overall.add");
|
||||
private static final LocTextKey DEFAULT_REMOVE_TOOLTIP_KEY = new LocTextKey("sebserver.overall.remove");
|
||||
|
||||
private final WidgetFactory widgetFactory;
|
||||
private final Combo combo;
|
||||
private final LocTextKey addTextKey;
|
||||
private final LocTextKey removeTextKey;
|
||||
|
||||
private final List<Tuple<Control>> selectionControls = new ArrayList<>();
|
||||
private final List<Control> selectionControls = new ArrayList<>();
|
||||
|
||||
private final List<Tuple<String>> valueMapping = new ArrayList<>();
|
||||
private final List<Tuple<String>> availableValues = new ArrayList<>();
|
||||
private final List<Tuple<String>> selectedValues = new ArrayList<>();
|
||||
private final Map<String, String> mapping = new HashMap<>();
|
||||
|
||||
private final GridData comboCell;
|
||||
private final GridData actionCell;
|
||||
private final DropDown dropDown;
|
||||
private final Text textInput;
|
||||
private final GridData textCell;
|
||||
private final Composite updateAnchor;
|
||||
|
||||
private Listener listener = null;
|
||||
|
@ -68,14 +61,8 @@ public final class MultiSelectionCombo extends Composite implements Selection {
|
|||
|
||||
super(parent, SWT.NONE);
|
||||
this.widgetFactory = widgetFactory;
|
||||
this.addTextKey = (locTextPrefix != null)
|
||||
? new LocTextKey(locTextPrefix + ".add")
|
||||
: DEFAULT_ADD_TOOLTIP_KEY;
|
||||
this.removeTextKey = (locTextPrefix != null)
|
||||
? new LocTextKey(locTextPrefix + ".remove")
|
||||
: DEFAULT_REMOVE_TOOLTIP_KEY;
|
||||
|
||||
final GridLayout gridLayout = new GridLayout(2, false);
|
||||
final GridLayout gridLayout = new GridLayout();
|
||||
gridLayout.verticalSpacing = 1;
|
||||
gridLayout.marginLeft = 0;
|
||||
gridLayout.marginHeight = 0;
|
||||
|
@ -84,22 +71,43 @@ public final class MultiSelectionCombo extends Composite implements Selection {
|
|||
setLayout(gridLayout);
|
||||
|
||||
this.addListener(SWT.Resize, this::adaptColumnWidth);
|
||||
this.textInput = widgetFactory.textInput(this);
|
||||
this.textCell = new GridData(SWT.LEFT, SWT.CENTER, true, true);
|
||||
this.textInput.setLayoutData(this.textCell);
|
||||
this.dropDown = new DropDown(this.textInput, SWT.NONE);
|
||||
this.textInput.addListener(SWT.FocusIn, event -> {
|
||||
openDropDown();
|
||||
});
|
||||
this.textInput.addListener(SWT.Modify, event -> {
|
||||
openDropDown();
|
||||
});
|
||||
this.dropDown.addListener(SWT.Selection, event -> {
|
||||
final int selectionIndex = this.dropDown.getSelectionIndex();
|
||||
if (selectionIndex >= 0) {
|
||||
final String selectedItem = this.dropDown.getItems()[selectionIndex];
|
||||
addSelection(itemForName(selectedItem));
|
||||
}
|
||||
});
|
||||
|
||||
this.combo = new Combo(this, SWT.NONE);
|
||||
this.comboCell = new GridData(SWT.FILL, SWT.CENTER, true, false);
|
||||
this.combo.setLayoutData(this.comboCell);
|
||||
|
||||
final Label imageButton = widgetFactory.imageButton(
|
||||
ImageIcon.ADD_BOX,
|
||||
this,
|
||||
this.addTextKey,
|
||||
this::addComboSelection);
|
||||
this.actionCell = new GridData(SWT.LEFT, SWT.CENTER, true, false);
|
||||
this.actionCell.widthHint = ACTION_COLUMN_WIDTH;
|
||||
imageButton.setLayoutData(this.actionCell);
|
||||
this.updateAnchor = updateAnchor;
|
||||
}
|
||||
|
||||
private void openDropDown() {
|
||||
final String text = this.textInput.getText();
|
||||
if (text == null) {
|
||||
this.dropDown.setVisible(false);
|
||||
return;
|
||||
}
|
||||
final Collection<String> items = this.availableValues
|
||||
.stream()
|
||||
.filter(it -> it._2 != null && it._2.startsWith(text))
|
||||
.map(t -> t._2)
|
||||
.collect(Collectors.toList());
|
||||
this.dropDown.setItems(items.toArray(new String[items.size()]));
|
||||
this.dropDown.setSelectionIndex(0);
|
||||
this.dropDown.setVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type type() {
|
||||
return Type.MULTI_COMBO;
|
||||
|
@ -112,8 +120,8 @@ public final class MultiSelectionCombo extends Composite implements Selection {
|
|||
|
||||
@Override
|
||||
public void applyNewMapping(final List<Tuple<String>> mapping) {
|
||||
this.mapping.putAll(mapping.stream()
|
||||
.collect(Collectors.toMap(t -> t._1, t -> t._2)));
|
||||
this.valueMapping.clear();
|
||||
this.valueMapping.addAll(mapping);
|
||||
this.clear();
|
||||
}
|
||||
|
||||
|
@ -126,9 +134,22 @@ public final class MultiSelectionCombo extends Composite implements Selection {
|
|||
|
||||
Arrays.asList(StringUtils.split(keys, Constants.LIST_SEPARATOR))
|
||||
.stream()
|
||||
.map(this::itemForName)
|
||||
.forEach(this::addSelection);
|
||||
}
|
||||
|
||||
private Tuple<String> itemForName(final String name) {
|
||||
final Optional<Tuple<String>> findFirst = this.availableValues
|
||||
.stream()
|
||||
.filter(it -> it._2 != null && it._2.equals(name))
|
||||
.findFirst();
|
||||
if (findFirst.isPresent()) {
|
||||
return findFirst.get();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectionValue() {
|
||||
if (this.selectedValues.isEmpty()) {
|
||||
|
@ -151,61 +172,28 @@ public final class MultiSelectionCombo extends Composite implements Selection {
|
|||
this.selectedValues.clear();
|
||||
this.selectionControls
|
||||
.stream()
|
||||
.forEach(t -> {
|
||||
t._1.dispose();
|
||||
t._2.dispose();
|
||||
});
|
||||
.forEach(Control::dispose);
|
||||
this.selectionControls.clear();
|
||||
this.combo.setItems(this.mapping.values().toArray(new String[this.mapping.size()]));
|
||||
this.availableValues.clear();
|
||||
this.availableValues.addAll(this.valueMapping);
|
||||
}
|
||||
|
||||
private void addComboSelection(final Event event) {
|
||||
final int selectionIndex = this.combo.getSelectionIndex();
|
||||
if (selectionIndex < 0) {
|
||||
private void addSelection(final Tuple<String> item) {
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String itemName = this.combo.getItem(selectionIndex);
|
||||
if (itemName == null) {
|
||||
return;
|
||||
}
|
||||
this.selectedValues.add(item);
|
||||
final Label label = this.widgetFactory.label(this, item._2);
|
||||
label.setData(OPTION_VALUE, item._2);
|
||||
final GridData textCell = new GridData(SWT.LEFT, SWT.CENTER, true, true);
|
||||
label.setLayoutData(textCell);
|
||||
label.addListener(SWT.MouseDoubleClick, event -> {
|
||||
removeComboSelection(event);
|
||||
});
|
||||
this.selectionControls.add(label);
|
||||
|
||||
final Optional<Entry<String, String>> findFirst = this.mapping.entrySet()
|
||||
.stream()
|
||||
.filter(entity -> entity.getValue().equals(itemName))
|
||||
.findFirst();
|
||||
|
||||
if (!findFirst.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
addSelection(findFirst.get().getKey());
|
||||
if (this.listener != null) {
|
||||
this.listener.handleEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
private void addSelection(final String itemKey) {
|
||||
final String itemName = this.mapping.get(itemKey);
|
||||
if (itemName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedValues.add(new Tuple<>(itemKey, itemName));
|
||||
final Label label = this.widgetFactory.label(this, itemName);
|
||||
final Label imageButton = this.widgetFactory.imageButton(
|
||||
ImageIcon.REMOVE_BOX,
|
||||
this,
|
||||
this.removeTextKey,
|
||||
this::removeComboSelection);
|
||||
imageButton.setData(OPTION_VALUE, itemName);
|
||||
final GridData actionCell = new GridData(SWT.LEFT, SWT.CENTER, true, false);
|
||||
actionCell.widthHint = ACTION_COLUMN_WIDTH;
|
||||
imageButton.setLayoutData(actionCell);
|
||||
|
||||
this.selectionControls.add(new Tuple<>(label, imageButton));
|
||||
|
||||
this.combo.remove(itemName);
|
||||
this.availableValues.remove(item);
|
||||
PageService.updateScrolledComposite(this);
|
||||
this.updateAnchor.layout(true, true);
|
||||
|
||||
|
@ -217,22 +205,20 @@ public final class MultiSelectionCombo extends Composite implements Selection {
|
|||
}
|
||||
|
||||
final String selectionKey = (String) event.widget.getData(OPTION_VALUE);
|
||||
final Optional<Tuple<Control>> findFirst = this.selectionControls.stream()
|
||||
.filter(t -> selectionKey.equals(t._2.getData(OPTION_VALUE)))
|
||||
final Optional<Control> findFirst = this.selectionControls.stream()
|
||||
.filter(t -> selectionKey.equals(t.getData(OPTION_VALUE)))
|
||||
.findFirst();
|
||||
if (!findFirst.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Tuple<Control> tuple = findFirst.get();
|
||||
final int indexOf = this.selectionControls.indexOf(tuple);
|
||||
this.selectionControls.remove(tuple);
|
||||
|
||||
tuple._1.dispose();
|
||||
tuple._2.dispose();
|
||||
final Control control = findFirst.get();
|
||||
final int indexOf = this.selectionControls.indexOf(control);
|
||||
this.selectionControls.remove(control);
|
||||
control.dispose();
|
||||
|
||||
final Tuple<String> value = this.selectedValues.remove(indexOf);
|
||||
this.combo.add(value._2, this.combo.getItemCount());
|
||||
this.availableValues.add(value);
|
||||
|
||||
PageService.updateScrolledComposite(this);
|
||||
this.updateAnchor.layout(true, true);
|
||||
|
@ -244,7 +230,7 @@ public final class MultiSelectionCombo extends Composite implements Selection {
|
|||
private void adaptColumnWidth(final Event event) {
|
||||
try {
|
||||
final int currentTableWidth = this.getClientArea().width;
|
||||
this.comboCell.widthHint = currentTableWidth - ACTION_COLUMN_WIDTH;
|
||||
this.textCell.widthHint = currentTableWidth;
|
||||
this.layout();
|
||||
} catch (final Exception e) {
|
||||
log.warn("Failed to adaptColumnWidth: ", e);
|
||||
|
|
|
@ -136,9 +136,12 @@ public interface ExamSessionService {
|
|||
* a subset of them.
|
||||
*
|
||||
* @param examId The exam identifier
|
||||
* @param filter a filter predicate to apply
|
||||
* @return collection of ClientConnectionData of all active SEB client connections
|
||||
* of a running exam */
|
||||
Result<Collection<ClientConnectionData>> getConnectionData(Long examId);
|
||||
Result<Collection<ClientConnectionData>> getConnectionData(
|
||||
Long examId,
|
||||
Predicate<ClientConnectionData> filter);
|
||||
|
||||
/** Use this to check if the current cached running exam is up to date
|
||||
* and if not to flush the cache.
|
||||
|
|
|
@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -224,12 +225,7 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService {
|
|||
}
|
||||
|
||||
private void checkActiveClientConnections(final Exam exam) {
|
||||
if (this.examSessionService.getConnectionData(exam.id)
|
||||
.getOrThrow()
|
||||
.stream()
|
||||
.filter(ExamSessionService::isActiveConnection)
|
||||
.count() > 0) {
|
||||
|
||||
if (this.examSessionService.hasActiveSebClientConnections(exam.id)) {
|
||||
throw new APIMessage.APIMessageException(
|
||||
ErrorMessage.INTEGRITY_VALIDATION,
|
||||
"Integrity violation: There are currently active SEB Client connection.");
|
||||
|
@ -286,7 +282,7 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService {
|
|||
final long activeConnections = involvedExams
|
||||
.stream()
|
||||
.flatMap(examId -> {
|
||||
return this.examSessionService.getConnectionData(examId)
|
||||
return this.examSessionService.getConnectionData(examId, Objects::nonNull)
|
||||
.getOrThrow()
|
||||
.stream();
|
||||
})
|
||||
|
|
|
@ -164,12 +164,9 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
|||
return false;
|
||||
}
|
||||
|
||||
return this.getConnectionData(examId)
|
||||
return !this.getConnectionData(examId, ExamSessionService::isActiveConnection)
|
||||
.getOrThrow()
|
||||
.stream()
|
||||
.filter(ExamSessionService::isActiveConnection)
|
||||
.findFirst()
|
||||
.isPresent();
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -313,14 +310,17 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result<Collection<ClientConnectionData>> getConnectionData(final Long examId) {
|
||||
public Result<Collection<ClientConnectionData>> getConnectionData(
|
||||
final Long examId,
|
||||
final Predicate<ClientConnectionData> filter) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
return this.clientConnectionDAO
|
||||
.getConnectionTokens(examId)
|
||||
.getOrThrow()
|
||||
.stream()
|
||||
.map(this.examSessionCacheService::getActiveClientConnection)
|
||||
.filter(data -> data != null)
|
||||
.filter(filter)
|
||||
.collect(Collectors.toList());
|
||||
});
|
||||
}
|
||||
|
|
|
@ -123,8 +123,9 @@ public class SebInstructionServiceImpl implements SebInstructionService {
|
|||
log.error("Failed to delete SEB client instruction on persistent storage: ", delete.getError());
|
||||
}
|
||||
|
||||
// {"instruction":"%s", "attributes":{%s}}
|
||||
return new StringBuilder()
|
||||
// {"instruction":"%s", "attributes":%s}
|
||||
final String attributes = clientInstruction.getAttributes();
|
||||
final StringBuilder sBuilder = new StringBuilder()
|
||||
.append(Constants.CURLY_BRACE_OPEN)
|
||||
.append(Constants.DOUBLE_QUOTE)
|
||||
.append(JSON_INST)
|
||||
|
@ -137,10 +138,16 @@ public class SebInstructionServiceImpl implements SebInstructionService {
|
|||
.append(Constants.DOUBLE_QUOTE)
|
||||
.append(JSON_ATTR)
|
||||
.append(Constants.DOUBLE_QUOTE)
|
||||
.append(Constants.COLON)
|
||||
.append(Constants.CURLY_BRACE_OPEN)
|
||||
.append(clientInstruction.getAttributes())
|
||||
.append(Constants.CURLY_BRACE_CLOSE)
|
||||
.append(Constants.COLON);
|
||||
if (attributes == null || attributes.isEmpty()) {
|
||||
sBuilder.append(Constants.NULL);
|
||||
|
||||
} else {
|
||||
sBuilder.append(Constants.CURLY_BRACE_OPEN)
|
||||
.append(attributes)
|
||||
.append(Constants.CURLY_BRACE_CLOSE);
|
||||
}
|
||||
return sBuilder
|
||||
.append(Constants.CURLY_BRACE_CLOSE)
|
||||
.toString();
|
||||
}
|
||||
|
|
|
@ -10,7 +10,9 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
|
@ -23,6 +25,7 @@ import org.springframework.web.bind.WebDataBinder;
|
|||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
@ -35,6 +38,7 @@ import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
|||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||
|
@ -153,7 +157,8 @@ public class ExamMonitoringController {
|
|||
name = API.PARAM_INSTITUTION_ID,
|
||||
required = true,
|
||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||
@PathVariable(name = API.PARAM_MODEL_ID, required = true) final Long examId) {
|
||||
@PathVariable(name = API.PARAM_MODEL_ID, required = true) final Long examId,
|
||||
@RequestHeader(name = API.EXAM_MONITORING_STATE_FILTER, required = false) final String hiddenStates) {
|
||||
|
||||
// check overall privilege
|
||||
this.authorization.checkRole(
|
||||
|
@ -169,8 +174,20 @@ public class ExamMonitoringController {
|
|||
this.authorization.getUserService().getCurrentUser().getUserInfo());
|
||||
}
|
||||
|
||||
final EnumSet<ConnectionStatus> filterStates = EnumSet.noneOf(ConnectionStatus.class);
|
||||
if (StringUtils.isNoneBlank(hiddenStates)) {
|
||||
final String[] split = StringUtils.split(hiddenStates, Constants.LIST_SEPARATOR);
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
filterStates.add(ConnectionStatus.valueOf(split[0]));
|
||||
}
|
||||
}
|
||||
|
||||
return this.examSessionService
|
||||
.getConnectionData(examId)
|
||||
.getConnectionData(
|
||||
examId,
|
||||
filterStates.isEmpty()
|
||||
? Objects::nonNull
|
||||
: conn -> conn != null && filterStates.contains(conn.clientConnection.status))
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ sebserver.overall.action.cancel=Cancel
|
|||
sebserver.overall.action.close=Close
|
||||
sebserver.overall.action.goAwayFromEditPageConfirm=Are you sure you want to leave this page? Unsaved data will be lost.
|
||||
sebserver.overall.action.category.varia=
|
||||
sebserver.overall.action.category.filter=Connection Status Filter
|
||||
sebserver.overall.action.category.filter=
|
||||
|
||||
sebserver.overall.status.active=Active
|
||||
sebserver.overall.status.inactive=Inactive
|
||||
|
@ -380,7 +380,7 @@ sebserver.exam.status.UP_COMING=Up Coming
|
|||
sebserver.exam.status.RUNNING=Running
|
||||
sebserver.exam.status.FINISHED=Finished
|
||||
|
||||
sebserver.exam.configuration.list.actions=SEB Exam Configuration
|
||||
sebserver.exam.configuration.list.actions=
|
||||
sebserver.exam.configuration.list.title=SEB Exam Configuration
|
||||
sebserver.exam.configuration.list.column.name=Name
|
||||
sebserver.exam.configuration.list.column.description=Description
|
||||
|
@ -404,7 +404,7 @@ sebserver.exam.configuration.form.description=Description
|
|||
sebserver.exam.configuration.form.status=Status
|
||||
sebserver.exam.configuration.form.encryptSecret.confirm=Confirm Password
|
||||
|
||||
sebserver.exam.indicator.list.actions=Indicator
|
||||
sebserver.exam.indicator.list.actions=
|
||||
sebserver.exam.indicator.list.title=Indicators
|
||||
sebserver.exam.indicator.list.column.type=Type
|
||||
sebserver.exam.indicator.list.column.name=Name
|
||||
|
@ -1011,6 +1011,7 @@ sebserver.examconfig.props.validation.WindowsSizeValidator=Invalid number
|
|||
# SEB Exam Configuration Template
|
||||
################################
|
||||
|
||||
sebserver.configtemplate.action.list=Configuration Templates
|
||||
sebserver.configtemplate.list.title=Configuration Templates
|
||||
sebserver.configtemplate.list.empty=There is currently no SEB-Exam configuration template available. Please create a new one
|
||||
sebserver.configtemplate.list.actions=
|
||||
|
|
Loading…
Reference in a new issue