diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/SEBClientConfig.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/SEBClientConfig.java index 890fb6a0..6765bf43 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/SEBClientConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/SEBClientConfig.java @@ -11,6 +11,11 @@ package ch.ethz.seb.sebserver.gbl.model.sebconfig; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; +import java.util.Collection; +import java.util.Set; + +import ch.ethz.seb.sebserver.gbl.util.Utils; +import org.apache.commons.lang3.StringUtils; import org.hibernate.validator.constraints.URL; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -52,6 +57,7 @@ public final class SEBClientConfig implements GrantEntity, Activatable { public static final String ATTR_ENCRYPT_CERTIFICATE_ASYM = "cert_encryption_asym"; public static final String FILTER_ATTR_CREATION_DATE = "creation_date"; + public static final String ATTR_EXAM_SELECTION = "exam_selection"; public enum ConfigPurpose { START_EXAM, @@ -174,6 +180,8 @@ public final class SEBClientConfig implements GrantEntity, Activatable { @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_LAST_UPDATE_USER) public final String lastUpdateUser; + @JsonProperty(SEBClientConfig.ATTR_EXAM_SELECTION) + public final Set selectedExams; @JsonCreator public SEBClientConfig( @@ -204,7 +212,8 @@ public final class SEBClientConfig implements GrantEntity, Activatable { @JsonProperty(ATTR_ENCRYPT_CERTIFICATE_ASYM) final Boolean encryptCertificateAsym, @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE) final Boolean active, @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_LAST_UPDATE_TIME) final DateTime lastUpdateTime, - @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_LAST_UPDATE_USER) final String lastUpdateUser) { + @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_LAST_UPDATE_USER) final String lastUpdateUser, + @JsonProperty(SEBClientConfig.ATTR_EXAM_SELECTION) final Set selectedExams) { this.id = id; this.institutionId = institutionId; @@ -240,6 +249,7 @@ public final class SEBClientConfig implements GrantEntity, Activatable { this.active = active; this.lastUpdateTime = lastUpdateTime; this.lastUpdateUser = lastUpdateUser; + this.selectedExams = Utils.immutableSetOf(selectedExams); } public SEBClientConfig(final Long institutionId, final POSTMapper postParams) { @@ -281,6 +291,7 @@ public final class SEBClientConfig implements GrantEntity, Activatable { this.active = false; this.lastUpdateTime = postParams.getDateTime(CONFIGURATION_NODE.ATTR_LAST_UPDATE_TIME); this.lastUpdateUser = postParams.getString(CONFIGURATION_NODE.ATTR_LAST_UPDATE_USER); + this.selectedExams = Utils.immutableSetOf(Utils.getIdsFromString(postParams.getString(SEBClientConfig.ATTR_EXAM_SELECTION))); } @Override @@ -424,6 +435,10 @@ public final class SEBClientConfig implements GrantEntity, Activatable { return this.lastUpdateUser; } + public Set getSelectedExams() { + return selectedExams; + } + @Override public String toString() { final StringBuilder builder = new StringBuilder(); @@ -503,7 +518,8 @@ public final class SEBClientConfig implements GrantEntity, Activatable { this.encryptCertificateAsym, this.active, this.lastUpdateTime, - this.lastUpdateUser); + this.lastUpdateUser, + this.selectedExams); } public static SEBClientConfig createNew(final Long institutionId, final long pingIterval) { @@ -533,6 +549,7 @@ public final class SEBClientConfig implements GrantEntity, Activatable { false, false, null, + null, null); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java index 5f558cdc..26a5a583 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java @@ -935,4 +935,22 @@ public final class Utils { return "Basic " + base64Creds; } + public static Set getIdsFromString(final String idsString) { + if (StringUtils.isBlank(idsString)) { + return Collections.emptySet(); + } + + return Arrays + .stream(StringUtils.split(idsString, Constants.LIST_SEPARATOR_CHAR)) + .map(s -> { + try { + return Long.valueOf(s); + } catch (final Exception e) { + log.warn("Failed to parse String: {} to Long", s); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigForm.java index 407e242e..5dbe780b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigForm.java @@ -134,6 +134,9 @@ public class SEBClientConfigForm implements TemplateComposer { private static final LocTextKey FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY = new LocTextKey("sebserver.clientconfig.form.encryptSecret.confirm"); + private static final LocTextKey FORM_EXAM_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.examselection"); + private static final LocTextKey DELETE_CONFIRM = new LocTextKey("sebserver.clientconfig.action.delete.confirm"); private static final LocTextKey DELETE_SUCCESS = @@ -409,20 +412,28 @@ public class SEBClientConfigForm implements TemplateComposer { .mandatory(!isReadonly)) .withDefaultSpanEmptyCell(3) + .addField(FormBuilder.multiComboSelection( + SEBClientConfig.ATTR_EXAM_SELECTION, + FORM_EXAM_SELECTION_TEXT_KEY, + StringUtils.join(clientConfig.selectedExams, Constants.LIST_SEPARATOR), + () -> pageService.getResourceService().getExamLogSelectionResources()) + .withInputSpan(5)) + .withDefaultSpanEmptyCell(1); + // VDI - .withDefaultSpanInput(2) - .addFieldIf( - () -> false, // TODO skipped for version 1.2 --> 1.3 or 1.4 - () -> FormBuilder.singleSelection( - SEBClientConfig.ATTR_VDI_TYPE, - VDI_TYPE_TEXT_KEY, - clientConfig.vdiType != null - ? clientConfig.vdiType.name() - : SEBClientConfig.VDIType.NO.name(), - () -> this.pageService.getResourceService().vdiTypeResources()) - .mandatory(!isReadonly)) - .withDefaultSpanEmptyCell(3); +// .withDefaultSpanInput(2) +// .addFieldIf( +// () -> false, // TODO skipped for version 1.2 --> 1.3 or 1.4 +// () -> FormBuilder.singleSelection( +// SEBClientConfig.ATTR_VDI_TYPE, +// VDI_TYPE_TEXT_KEY, +// clientConfig.vdiType != null +// ? clientConfig.vdiType.name() +// : SEBClientConfig.VDIType.NO.name(), +// () -> this.pageService.getResourceService().vdiTypeResources()) +// .mandatory(!isReadonly)) +// .withDefaultSpanEmptyCell(3); // VDI Attributes @@ -460,7 +471,10 @@ public class SEBClientConfigForm implements TemplateComposer { FALLBACK_TEXT_KEY, clientConfig.fallback != null ? clientConfig.fallback.toString() - : Constants.FALSE_STRING)); + : Constants.FALSE_STRING)) + .withDefaultSpanEmptyCell(3) + + ; // Fallback Attributes diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java index fdb121e4..0b15835c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java @@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.gui.form; import java.util.Arrays; import java.util.Collection; +import java.util.Comparator; import java.util.List; import java.util.function.Consumer; import java.util.function.Supplier; @@ -122,6 +123,7 @@ public final class SelectionFieldBuilder extends FieldBuilder { this.itemsSupplier.get() .stream() .filter(tuple -> keys.contains(tuple._1)) + .sorted((t1, t2) -> String.CASE_INSENSITIVE_ORDER.compare(t1._2, t2._2)) .map(tuple -> tuple._1) .forEach(v -> buildReadonlyLabel(builder, composite, v, 1)); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCombo.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCombo.java index 8f7dd457..0df1a8fa 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCombo.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCombo.java @@ -8,22 +8,17 @@ package ch.ethz.seb.sebserver.gui.widget; -import java.util.ArrayList; -import java.util.Arrays; +import java.util.*; import java.util.List; -import java.util.Optional; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import org.apache.commons.lang3.StringUtils; import org.eclipse.rap.rwt.widgets.DropDown; import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; -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.eclipse.swt.widgets.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,13 +28,11 @@ import ch.ethz.seb.sebserver.gui.service.page.PageService; public final class MultiSelectionCombo extends Composite implements Selection { + public static final LocTextKey DESELECT_TOOLTIP = new LocTextKey( "sebserver.form.multiselect.deselect.tooltip" ); + private static final Logger log = LoggerFactory.getLogger(MultiSelectionCombo.class); private static final long serialVersionUID = -7787134114963647332L; - private final WidgetFactory widgetFactory; - - private final List selectionControls = new ArrayList<>(); - private final List> valueMapping = new ArrayList<>(); private final List> availableValues = new ArrayList<>(); private final List> selectedValues = new ArrayList<>(); @@ -50,7 +43,10 @@ public final class MultiSelectionCombo extends Composite implements Selection { private final Composite updateAnchor; private final String testKey; + private final Table selectionTable; + private Listener listener = null; + private WidgetFactory widgetFactory; MultiSelectionCombo( final Composite parent, @@ -78,7 +74,7 @@ public final class MultiSelectionCombo extends Composite implements Selection { this.textInput.addListener(SWT.FocusIn, event -> openDropDown()); this.textInput.addListener(SWT.Modify, event -> openDropDown()); this.textInput.addListener(SWT.MouseUp, event -> openDropDown()); - this.dropDown.addListener(SWT.Selection, event -> { + this.dropDown.addListener(SWT.DefaultSelection, event -> { final int selectionIndex = this.dropDown.getSelectionIndex(); if (selectionIndex >= 0) { final String selectedItem = this.dropDown.getItems()[selectionIndex]; @@ -86,21 +82,29 @@ public final class MultiSelectionCombo extends Composite implements Selection { } }); + selectionTable = widgetFactory.tableLocalized(this, SWT.NONE); + final GridLayout tableLayout = new GridLayout(1, true); + selectionTable.setLayout(tableLayout); + final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false); + selectionTable.setLayoutData(gridData); + //selectionTable.setToolTipText(); + selectionTable.addListener(SWT.MouseDoubleClick, this::removeComboSelection); + selectionTable.addListener(SWT.Selection, event -> { + selectionTable.setSelection(-1); + }); + selectionTable.setHeaderVisible(false); + selectionTable.setLinesVisible(true); + this.updateAnchor = updateAnchor; } - private void openDropDown() { - final String text = this.textInput.getText(); - if (text == null) { - this.dropDown.setVisible(false); + @Override + public void setToolTipText(final String tooltipText) { + if (tooltipText == null) { + super.setToolTipText(widgetFactory.getI18nSupport().getText(DESELECT_TOOLTIP)); return; } - this.dropDown.setItems(this.availableValues - .stream() - .filter(it -> it._2 != null && it._2.startsWith(text)) - .map(t -> t._2).toArray(String[]::new)); - this.dropDown.setSelectionIndex(0); - this.dropDown.setVisible(true); + super.setToolTipText(tooltipText + "\n\n" + widgetFactory.getI18nSupport().getText(DESELECT_TOOLTIP)); } @Override @@ -157,9 +161,6 @@ public final class MultiSelectionCombo extends Composite implements Selection { @Override public void clear() { this.selectedValues.clear(); - this.selectionControls - .forEach(Control::dispose); - this.selectionControls.clear(); this.availableValues.clear(); this.availableValues.addAll(this.valueMapping); } @@ -170,42 +171,39 @@ public final class MultiSelectionCombo extends Composite implements Selection { } 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, this::removeComboSelection); - this.selectionControls.add(label); - WidgetFactory.setARIALabel(label, item._2); - WidgetFactory.setTestId(label, (this.testKey != null) ? this.testKey + "_" + item._1 : item._1); + sortSelectedTable(); this.availableValues.remove(item); PageService.updateScrolledComposite(this); this.updateAnchor.layout(true, true); } + private void sortSelectedTable() { + selectionTable.removeAll(); + selectedValues.sort((t1, t2) -> String.CASE_INSENSITIVE_ORDER.compare(t1._2, t2._2)); + selectedValues.stream().forEach(t -> { + final TableItem tItem = new TableItem(selectionTable, SWT.NONE); + tItem.setText(0, t._2); + tItem.setData("tuple", t); + WidgetFactory.setARIALabel(tItem, t._2); + WidgetFactory.setTestId(tItem, (this.testKey != null) ? this.testKey + "_" + t._1 : t._1); + }); + } + private void removeComboSelection(final Event event) { if (event.widget == null) { return; } - final String selectionKey = (String) event.widget.getData(OPTION_VALUE); - final Optional findFirst = this.selectionControls.stream() - .filter(t -> selectionKey.equals(t.getData(OPTION_VALUE))) - .findFirst(); - if (!findFirst.isPresent()) { - return; - } - - final Control control = findFirst.get(); - final int indexOf = this.selectionControls.indexOf(control); - this.selectionControls.remove(control); - control.dispose(); - - final Tuple value = this.selectedValues.remove(indexOf); + final TableItem item = selectionTable.getItem(new Point(event.x, event.y)); + @SuppressWarnings("unchecked") + final Tuple value = (Tuple) item.getData("tuple"); + this.selectedValues.remove(value); this.availableValues.add(value); + sortSelectedTable(); + PageService.updateScrolledComposite(this); this.updateAnchor.layout(true, true); if (this.listener != null) { @@ -238,4 +236,18 @@ public final class MultiSelectionCombo extends Composite implements Selection { return findFirst.orElse(null); } + private void openDropDown() { + final String text = this.textInput.getText(); + if (text == null) { + this.dropDown.setVisible(false); + return; + } + this.dropDown.setItems(this.availableValues + .stream() + .filter(it -> it._2 != null && it._2.startsWith(text)) + .map(t -> t._2).toArray(String[]::new)); + this.dropDown.setSelectionIndex(0); + this.dropDown.setVisible(true); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SEBClientConfigDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SEBClientConfigDAOImpl.java index 428e4756..6d708415 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SEBClientConfigDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SEBClientConfigDAOImpl.java @@ -489,7 +489,11 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO { additionalAttributes.containsKey(SEBClientConfig.ATTR_ENCRYPT_CERTIFICATE_ASYM), BooleanUtils.toBooleanObject(record.getActive()), Utils.toDateTimeUTC(record.getLastUpdateTime()), - record.getLastUpdateUser())); + record.getLastUpdateUser(), + Utils.getIdsFromString( + additionalAttributes.containsKey(SEBClientConfig.ATTR_EXAM_SELECTION) + ? additionalAttributes.get(SEBClientConfig.ATTR_EXAM_SELECTION).getValue() + : null))); } private String getEncryptionPassword(final SEBClientConfig sebClientConfig) { @@ -685,6 +689,21 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO { configId, SEBClientConfig.ATTR_ENCRYPT_CERTIFICATE_ASYM); } + + final Set selectedExams = sebClientConfig.getSelectedExams(); + if (selectedExams != null && !selectedExams.isEmpty()) { + final String ids = StringUtils.join(selectedExams, Constants.LIST_SEPARATOR); + this.additionalAttributesDAO.saveAdditionalAttribute( + EntityType.SEB_CLIENT_CONFIGURATION, + configId, + SEBClientConfig.ATTR_EXAM_SELECTION, + ids); + } else { + this.additionalAttributesDAO.delete( + EntityType.SEB_CLIENT_CONFIGURATION, + configId, + SEBClientConfig.ATTR_EXAM_SELECTION); + } } private Long disposeSEBClientConfig(final Long pk) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ClientConfigService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ClientConfigService.java index 3ebd907d..206393a5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ClientConfigService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ClientConfigService.java @@ -63,5 +63,5 @@ public interface ClientConfigService { boolean checkAccess(SEBClientConfig config); @Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME) - void initalCheckAccess(SEBClientConfig config); + void initialCheckAccess(SEBClientConfig config); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ClientConfigServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ClientConfigServiceImpl.java index 06f25b7c..034b7db5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ClientConfigServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ClientConfigServiceImpl.java @@ -504,7 +504,7 @@ public class ClientConfigServiceImpl implements ClientConfigService { } @Override - public void initalCheckAccess(final SEBClientConfig config) { + public void initialCheckAccess(final SEBClientConfig config) { checkAccess(config); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamSessionService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamSessionService.java index 602f3e0d..fa41d80c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamSessionService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamSessionService.java @@ -9,6 +9,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session; import java.io.OutputStream; +import java.security.Principal; import java.util.Collection; import java.util.Set; import java.util.function.Predicate; @@ -130,9 +131,10 @@ public interface ExamSessionService { /** Gets all currently running Exams for a particular Institution. * * @param institutionId the Institution identifier + * @param examSelectionFilter Exam selection filter from SEB connection configuration * @return Result referencing the list of all currently running Exams of the institution or to an error if * happened. */ - Result> getRunningExamsForInstitution(Long institutionId); + Result> getRunningExams(Long institutionId, Predicate examSelectionFilter); /** Gets all currently running Exams for a particular FilterMap. * diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java index 87c3cc79..bae50e67 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java @@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; import java.io.IOException; import java.io.OutputStream; +import java.security.Principal; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -251,9 +252,10 @@ public class ExamSessionServiceImpl implements ExamSessionService { } @Override - public Result> getRunningExamsForInstitution(final Long institutionId) { + public Result> getRunningExams(final Long institutionId, final Predicate examSelectionFilter) { return this.examDAO.allIdsOfRunning(institutionId) .map(col -> col.stream() + .filter(examSelectionFilter) .map(this::getRunningExam) .filter(Result::hasValue) .map(Result::get) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java index 9b37dc09..2f35228a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java @@ -11,17 +11,17 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.Principal; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.function.Predicate; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig; +import ch.ethz.seb.sebserver.gbl.util.Utils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -132,7 +132,10 @@ public class ExamAPI_V1_Controller { // Crate list of running exams final List result; if (examId == null) { - result = this.examSessionService.getRunningExamsForInstitution(institutionId) + + result = this.examSessionService.getRunningExams( + institutionId, + getExamSelectionPredicate(principal.getName())) .getOrThrow() .stream() .map(this::createRunningExamInfo) @@ -161,17 +164,7 @@ public class ExamAPI_V1_Controller { this.executor); } - private boolean checkConsistency(final RunningExamInfo info) { - if (StringUtils.isNotBlank(info.name) && - StringUtils.isNotBlank(info.url) && - StringUtils.isNotBlank(info.examId)) { - return true; - } - - log.warn("Invalid running exam detected. Filter out exam : {}", info); - return false; - } @RequestMapping( path = API.EXAM_API_HANDSHAKE_ENDPOINT, @@ -420,4 +413,31 @@ public class ExamAPI_V1_Controller { .onSuccess(bek -> response.setHeader(API.EXAM_API_EXAM_ALT_BEK, bek)); } + private Predicate getExamSelectionPredicate(final String clientName) { + return this.sebClientConfigDAO + .byClientName(clientName) + .map(this::getExamSelectionPredicate) + .onError(error -> log.warn("Failed to get SEB connection configuration by name: {}", clientName)) + .getOr(Utils.truePredicate()); + } + + private Predicate getExamSelectionPredicate(final SEBClientConfig config) { + if (config == null || config.selectedExams.isEmpty()) { + return Utils.truePredicate(); + } + return config.getSelectedExams()::contains; + } + + private boolean checkConsistency(final RunningExamInfo info) { + if (StringUtils.isNotBlank(info.name) && + StringUtils.isNotBlank(info.url) && + StringUtils.isNotBlank(info.examId)) { + + return true; + } + + log.warn("Invalid running exam detected. Filter out exam : {}", info); + return false; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/SEBClientConfigController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/SEBClientConfigController.java index 3aac116f..58931d96 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/SEBClientConfigController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/SEBClientConfigController.java @@ -182,7 +182,7 @@ public class SEBClientConfigController extends ActivatableEntityController notifySaved(final SEBClientConfig entity) { if (entity.isActive()) { // try to get access token for SEB client - this.sebClientConfigService.initalCheckAccess(entity); + this.sebClientConfigService.initialCheckAccess(entity); } return super.notifySaved(entity); } diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 4ceb7c7f..66219338 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -129,6 +129,7 @@ sebserver.form.mandatory.label={0} mandatory sebserver.form.confirm.label=confirm {0} sebserver.form.tablefilter.label={0} table-column filter sebserver.table.column.sort.default.tooltip=Click on the column header to sort the table within this column +sebserver.form.multiselect.deselect.tooltip=Please use double-click to deselect selected items. sebserver.dialog.confirm.deactivation=Note that there are {0} other entities that belong to this entity.
Those will also be deactivated by deactivating this entity.

Are you sure to deactivate this entity? sebserver.dialog.confirm.deactivation.noDependencies=Are you sure you want to deactivate? @@ -1033,6 +1034,8 @@ sebserver.clientconfig.form.certificate=Encrypt with Certificate sebserver.clientconfig.form.certificate.tooltip=Choose identity certificate to be used for encrypting the connection configuration sebserver.clientconfig.form.type.async=Use asymmetric-only encryption sebserver.clientconfig.form.type.async.tooltip=Use old asymmetric-only encryption (for SEB < 2.2) +sebserver.clientconfig.form.examselection=Exams +sebserver.clientconfig.form.examselection.tooltip=List of Exams selected to work with this Connection Configuration. sebserver.clientconfig.form.credentials.title=Client Credentials of Connection Configuration sebserver.clientconfig.form.credentials.info=A SEB client that loads this connection configuration
uses the following credentials to securely connect to the SEB Server. diff --git a/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java b/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java index e2f50695..91244bf7 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java +++ b/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java @@ -138,7 +138,8 @@ public class ModelObjectJSONGenerator { false, true, DateTime.now(), - "user123"); + "user123", + null); System.out.println(domainObject.getClass().getSimpleName() + ":"); System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject)); diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/ClientConfigTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/ClientConfigTest.java index f5aabb5a..3266b133 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/integration/ClientConfigTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/ClientConfigTest.java @@ -128,6 +128,7 @@ public class ClientConfigTest extends GuiIntegrationTest { false, null, null, + null, null)) .call(); @@ -161,6 +162,7 @@ public class ClientConfigTest extends GuiIntegrationTest { false, null, null, + null, null)) .call() .getOrThrow(); diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamProctoringRoomServiceTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamProctoringRoomServiceTest.java index 493bc12f..34a7f68c 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamProctoringRoomServiceTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamProctoringRoomServiceTest.java @@ -12,6 +12,7 @@ import static org.junit.Assert.*; import java.util.Collection; +import ch.ethz.seb.sebserver.gbl.util.Utils; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Order; @@ -61,7 +62,7 @@ public class ExamProctoringRoomServiceTest extends AdministrationAPIIntegrationT @Order(1) public void test01_checkExamRunning() { final Result> runningExamsForInstitution = - this.examSessionService.getRunningExamsForInstitution(1L); + this.examSessionService.getRunningExams(1L, Utils.truePredicate()); assertFalse(runningExamsForInstitution.hasError()); final Collection collection = runningExamsForInstitution.get(); assertFalse(collection.isEmpty());