SEBSERV-376 migration task and GUI implementation
This commit is contained in:
parent
2062c3cddb
commit
a6f7c501ca
9 changed files with 504 additions and 13 deletions
|
@ -171,7 +171,7 @@ public class SEBSettingsForm implements TemplateComposer {
|
|||
view,
|
||||
viewContextSupplier,
|
||||
attributes,
|
||||
20,
|
||||
30,
|
||||
readonly,
|
||||
publishedMessagePanelViewCallback);
|
||||
viewContexts.add(viewContext);
|
||||
|
|
|
@ -216,7 +216,7 @@ public abstract class FieldBuilder<T> {
|
|||
|
||||
public static Label createErrorLabel(final Composite innerGrid) {
|
||||
final Label errorLabel = new Label(innerGrid, SWT.NONE);
|
||||
final GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, true);
|
||||
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, true);
|
||||
errorLabel.setLayoutData(gridData);
|
||||
errorLabel.setVisible(false);
|
||||
errorLabel.setData(RWT.CUSTOM_VARIANT, CustomVariant.ERROR.key);
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright (c) 2023 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.rap.rwt.RWT;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Listener;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.form.FieldBuilder;
|
||||
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
|
||||
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
|
||||
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||
import ch.ethz.seb.sebserver.gui.widget.TextListInput;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class TextFieldListBuilder extends AbstractTableFieldBuilder {
|
||||
|
||||
protected TextFieldListBuilder(
|
||||
final RestService restService,
|
||||
final WidgetFactory widgetFactory) {
|
||||
|
||||
super(restService, widgetFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean builderFor(final ConfigurationAttribute attribute, final Orientation orientation) {
|
||||
return AttributeType.TEXT_FIELD_LIST == attribute.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputField createInputField(
|
||||
final Composite parent,
|
||||
final ConfigurationAttribute attribute,
|
||||
final ViewContext viewContext) {
|
||||
|
||||
final Orientation orientation = viewContext
|
||||
.getOrientation(attribute.id);
|
||||
final Composite innerGrid = InputFieldBuilder
|
||||
.createInnerGrid(parent, attribute, orientation);
|
||||
|
||||
// final Composite scroll = PageService.createManagedVScrolledComposite(
|
||||
// innerGrid,
|
||||
// scrolledComposite -> {
|
||||
// final Composite result = new Composite(scrolledComposite, SWT.NONE);
|
||||
// final GridLayout gridLayout1 = new GridLayout();
|
||||
// result.setLayout(gridLayout1);
|
||||
// final GridData gridData1 = new GridData(SWT.FILL, SWT.FILL, true, true);
|
||||
// result.setLayoutData(gridData1);
|
||||
// return result;
|
||||
// },
|
||||
// false,
|
||||
// false, true);
|
||||
|
||||
final String attributeNameKey = ExamConfigurationService.attributeNameKey(attribute);
|
||||
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
|
||||
gridData.minimumHeight = WidgetFactory.TEXT_AREA_INPUT_MIN_HEIGHT;
|
||||
final TextListInput textListInput = new TextListInput(
|
||||
innerGrid,
|
||||
new LocTextKey(attributeNameKey),
|
||||
3,
|
||||
this.widgetFactory);
|
||||
WidgetFactory.setTestId(textListInput, attributeNameKey);
|
||||
textListInput.setLayoutData(gridData);
|
||||
|
||||
final TextListInputField textListInputField = new TextListInputField(
|
||||
attribute,
|
||||
orientation,
|
||||
textListInput,
|
||||
FieldBuilder.createErrorLabel(innerGrid));
|
||||
|
||||
if (viewContext.readonly) {
|
||||
textListInput.setEditable(false);
|
||||
|
||||
} else {
|
||||
final Listener valueChangeEventListener = event -> {
|
||||
textListInputField.clearError();
|
||||
viewContext.getValueChangeListener().valueChanged(
|
||||
viewContext,
|
||||
attribute,
|
||||
textListInputField.getValue(),
|
||||
textListInputField.listIndex);
|
||||
};
|
||||
|
||||
textListInput.addListener(valueChangeEventListener);
|
||||
}
|
||||
|
||||
return textListInputField;
|
||||
}
|
||||
|
||||
static final class TextListInputField extends AbstractInputField<TextListInput> {
|
||||
|
||||
TextListInputField(
|
||||
final ConfigurationAttribute attribute,
|
||||
final Orientation orientation,
|
||||
final TextListInput control,
|
||||
final Label errorLabel) {
|
||||
|
||||
super(attribute, orientation, control, errorLabel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setValueToControl(final String value) {
|
||||
if (value == null) {
|
||||
this.control.setValue(StringUtils.EMPTY);
|
||||
return;
|
||||
}
|
||||
|
||||
this.control.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable(final boolean group) {
|
||||
this.control.setData(RWT.CUSTOM_VARIANT, null);
|
||||
this.control.setEditable(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable(final boolean group) {
|
||||
this.control.setData(RWT.CUSTOM_VARIANT, CustomVariant.CONFIG_INPUT_READONLY.key);
|
||||
this.control.setEditable(false);
|
||||
final GridData gridData = (GridData) this.control.getLayoutData();
|
||||
gridData.heightHint = (this.attribute.type == AttributeType.TEXT_AREA)
|
||||
? WidgetFactory.TEXT_AREA_INPUT_MIN_HEIGHT
|
||||
: WidgetFactory.TEXT_INPUT_MIN_HEIGHT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return this.control.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2023 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.widget;
|
||||
|
||||
import ch.ethz.seb.sebserver.gui.widget.GridTable.ColumnDef;
|
||||
|
||||
interface ControlAdapter {
|
||||
String getValue();
|
||||
|
||||
void setValue(String value);
|
||||
|
||||
void dispose();
|
||||
|
||||
ColumnDef columnDef();
|
||||
}
|
|
@ -305,16 +305,6 @@ public class GridTable extends Composite {
|
|||
}
|
||||
}
|
||||
|
||||
interface ControlAdapter {
|
||||
String getValue();
|
||||
|
||||
void setValue(String value);
|
||||
|
||||
void dispose();
|
||||
|
||||
ColumnDef columnDef();
|
||||
}
|
||||
|
||||
private static class Dummy implements ControlAdapter {
|
||||
|
||||
private final Label label;
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Copyright (c) 2023 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.widget;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.rap.rwt.RWT;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Button;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
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 ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
|
||||
|
||||
public class TextListInput extends Composite {
|
||||
|
||||
private static final long serialVersionUID = 1361754245260627626L;
|
||||
|
||||
private static final int ACTION_COLUMN_WIDTH = 20;
|
||||
|
||||
private final LocTextKey nameKey;
|
||||
private final int initialSize;
|
||||
private final WidgetFactory widgetFactory;
|
||||
private final Button addAction;
|
||||
private final Composite content;
|
||||
private final List<Row> list = new ArrayList<>();
|
||||
|
||||
private Listener valueChangeEventListener = null;
|
||||
|
||||
public TextListInput(
|
||||
final Composite parent,
|
||||
final LocTextKey nameKey,
|
||||
final int initialSize,
|
||||
final WidgetFactory widgetFactory) {
|
||||
|
||||
super(parent, SWT.NONE);
|
||||
this.nameKey = nameKey;
|
||||
this.initialSize = initialSize;
|
||||
this.widgetFactory = widgetFactory;
|
||||
|
||||
// main grid layout
|
||||
GridLayout gridLayout = new GridLayout(1, false);
|
||||
gridLayout.verticalSpacing = 1;
|
||||
gridLayout.marginLeft = 0;
|
||||
gridLayout.marginHeight = 5;
|
||||
gridLayout.marginWidth = 0;
|
||||
gridLayout.horizontalSpacing = 0;
|
||||
this.setLayout(gridLayout);
|
||||
final GridData gridData2 = new GridData(SWT.FILL, SWT.FILL, true, true);
|
||||
this.setLayoutData(gridData2);
|
||||
|
||||
// build header
|
||||
final Composite header = new Composite(this, SWT.NONE);
|
||||
gridLayout = new GridLayout(2, false);
|
||||
header.setLayout(gridLayout);
|
||||
GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, true);
|
||||
header.setLayoutData(gridData);
|
||||
|
||||
final Label label = widgetFactory.labelLocalized(header, this.nameKey);
|
||||
gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
|
||||
label.setLayoutData(gridData);
|
||||
|
||||
final LocTextKey toolTipKey = new LocTextKey(nameKey.name + ".tooltip");
|
||||
if (widgetFactory.getI18nSupport().hasText(toolTipKey)) {
|
||||
label.setToolTipText(Utils.formatLineBreaks(
|
||||
widgetFactory.getI18nSupport().getText(toolTipKey)));
|
||||
}
|
||||
|
||||
this.addAction = widgetFactory.imageButton(
|
||||
ImageIcon.ADD_BOX,
|
||||
header,
|
||||
new LocTextKey(this.nameKey.name + ".addAction"),
|
||||
this::addRow);
|
||||
gridData = new GridData(SWT.RIGHT, SWT.CENTER, false, true);
|
||||
gridData.widthHint = ACTION_COLUMN_WIDTH;
|
||||
this.addAction.setLayoutData(gridData);
|
||||
|
||||
this.content = header;
|
||||
for (int i = 0; i < initialSize; i++) {
|
||||
addRow(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(final Listener valueChangeEventListener) {
|
||||
this.valueChangeEventListener = valueChangeEventListener;
|
||||
this.list.stream().forEach(row -> row.addListener());
|
||||
}
|
||||
|
||||
void addRow(final Event event) {
|
||||
this.list.add(new Row(this.list.size()));
|
||||
this.content.getParent().getParent().layout(true, true);
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.list.stream()
|
||||
.map(row -> row.textInput.getText())
|
||||
.reduce("", (acc, val) -> {
|
||||
if (StringUtils.isNotBlank(val)) {
|
||||
if (StringUtils.isNotBlank(acc)) {
|
||||
acc += Constants.LIST_SEPARATOR;
|
||||
}
|
||||
acc += val;
|
||||
}
|
||||
return acc;
|
||||
});
|
||||
}
|
||||
|
||||
public void setValue(final String value) {
|
||||
System.out.println("************ value: " + value);
|
||||
if (StringUtils.isBlank(value)) {
|
||||
// clear rows
|
||||
new ArrayList<>(this.list).stream().forEach(row -> row.deleteRow());
|
||||
this.list.clear();
|
||||
// and fill with default empty
|
||||
for (int i = 0; i < this.initialSize; i++) {
|
||||
addRow(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final String[] split = StringUtils.split(value, Constants.LIST_SEPARATOR);
|
||||
int gap = this.list.size() - split.length;
|
||||
while (gap < 0) {
|
||||
addRow(null);
|
||||
gap++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
this.list.get(i).textInput.setText(split[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void setEditable(final boolean b) {
|
||||
this.addAction.setEnabled(b);
|
||||
this.list.stream().forEach(row -> row.setEditable(b));
|
||||
}
|
||||
|
||||
private final class Row {
|
||||
|
||||
public final Text textInput;
|
||||
public final Button deleteButton;
|
||||
|
||||
public Row(final int index) {
|
||||
this.textInput = TextListInput.this.widgetFactory.textInput(
|
||||
TextListInput.this.content,
|
||||
TextListInput.this.nameKey);
|
||||
GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, true);
|
||||
this.textInput.setLayoutData(gridData);
|
||||
this.addListener();
|
||||
|
||||
this.deleteButton = TextListInput.this.widgetFactory.imageButton(
|
||||
ImageIcon.REMOVE_BOX,
|
||||
TextListInput.this.content,
|
||||
new LocTextKey(TextListInput.this.nameKey.name + ".removeAction"),
|
||||
deleteEvent -> deleteRow());
|
||||
gridData = new GridData(SWT.RIGHT, SWT.CENTER, false, true);
|
||||
gridData.widthHint = ACTION_COLUMN_WIDTH;
|
||||
this.deleteButton.setLayoutData(gridData);
|
||||
}
|
||||
|
||||
private void addListener() {
|
||||
if (TextListInput.this.valueChangeEventListener != null) {
|
||||
this.textInput.addListener(SWT.FocusOut, TextListInput.this.valueChangeEventListener);
|
||||
this.textInput.addListener(SWT.Traverse, TextListInput.this.valueChangeEventListener);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteRow() {
|
||||
TextListInput.this.list.remove(this);
|
||||
this.textInput.dispose();
|
||||
this.deleteButton.dispose();
|
||||
TextListInput.this.content.getParent().getParent().layout(true, true);
|
||||
if (TextListInput.this.valueChangeEventListener != null) {
|
||||
TextListInput.this.valueChangeEventListener.handleEvent(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void setEditable(final boolean e) {
|
||||
this.textInput.setEditable(e);
|
||||
this.textInput.setData(
|
||||
RWT.CUSTOM_VARIANT,
|
||||
e ? null : CustomVariant.CONFIG_INPUT_READONLY.key);
|
||||
this.deleteButton.setEnabled(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2023 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.webservice.servicelayer.sebconfig.impl.validation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
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.sebconfig.ConfigurationAttribute;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationValueDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationValueValidator;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class SEBVersionValidator implements ConfigurationValueValidator {
|
||||
|
||||
public static final String NAME = "SEBVersionValidator";
|
||||
|
||||
private final ConfigurationValueDAO configurationValueDAO;
|
||||
|
||||
public SEBVersionValidator(final ConfigurationValueDAO configurationValueDAO) {
|
||||
this.configurationValueDAO = configurationValueDAO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(final ConfigurationValue value, final ConfigurationAttribute attribute) {
|
||||
// if validator is not specified --> skip
|
||||
if (!name().equals(attribute.validator)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(value.value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final String[] split = StringUtils.split(value.value, Constants.LIST_SEPARATOR);
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
if (!isValidSEBVersionMarker(split[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isValidSEBVersionMarker(final String versionMarker) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createErrorMessage(final ConfigurationValue value, final ConfigurationAttribute attribute) {
|
||||
if (StringUtils.isBlank(value.value)) {
|
||||
return ConfigurationValueValidator.super.createErrorMessage(value, attribute);
|
||||
}
|
||||
|
||||
final String[] split = StringUtils.split(value.value, Constants.LIST_SEPARATOR);
|
||||
final List<String> newValues = new ArrayList<>();
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
if (isValidSEBVersionMarker(split[i])) {
|
||||
newValues.add(split[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// save with the removed invalid values
|
||||
if (!newValues.isEmpty()) {
|
||||
this.configurationValueDAO.save(new ConfigurationValue(
|
||||
value.id,
|
||||
value.institutionId,
|
||||
value.configurationId,
|
||||
value.attributeId,
|
||||
value.listIndex,
|
||||
StringUtils.join(newValues, Constants.LIST_SEPARATOR)));
|
||||
}
|
||||
|
||||
return ConfigurationValueValidator.super.createErrorMessage(value, attribute);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
-- -----------------------------------------------------------------
|
||||
-- SEBSERV-376 Add SEBVersionValidator to setting
|
||||
-- -----------------------------------------------------------------
|
||||
|
||||
UPDATE configuration_attribute SET validator='SEBVersionValidator' WHERE id=1578;
|
||||
|
||||
-- -----------------------------------------------------------------
|
||||
-- SEBSERV-376 Reorder security / logging section for default template
|
||||
-- -----------------------------------------------------------------
|
||||
|
||||
UPDATE orientation SET width=6 WHERE config_attribute_id=305 AND template_id=0;
|
||||
UPDATE orientation SET width=4 WHERE config_attribute_id=306 AND template_id=0;
|
||||
UPDATE orientation SET width=4 WHERE config_attribute_id=307 AND template_id=0;
|
||||
UPDATE orientation SET width=4 WHERE config_attribute_id=317 AND template_id=0;
|
||||
UPDATE orientation SET width=4 WHERE config_attribute_id=319 AND template_id=0;
|
||||
UPDATE orientation SET width=4 WHERE config_attribute_id=320 AND template_id=0;
|
||||
|
||||
-- -----------------------------------------------------------------
|
||||
-- SEBSERV-376 add new orientation for default template
|
||||
-- -----------------------------------------------------------------
|
||||
|
||||
INSERT IGNORE INTO orientation (config_attribute_id, template_id, view_id, group_id, x_position, y_position, width, height, title) VALUES
|
||||
(1578, 0, 9, null, 7, 14, 4, 12, 'NONE');
|
|
@ -1841,7 +1841,9 @@ sebserver.examconfig.props.label.prohibitedProcesses.ignoreInAAC=Ignore in AAC
|
|||
sebserver.examconfig.props.label.prohibitedProcesses.ignoreInAAC.tooltip=When using the AAC kiosk mode (which prevents network and screen access for other processes), ignore this prohibited process
|
||||
sebserver.examconfig.props.label.sebAllowedVersions=Allowed SEB Versions
|
||||
sebserver.examconfig.props.label.sebAllowedVersions.tooltip=List of text inputs which represent either a specific or a minimal SEB version which is allowed to access the (exam) session using current settings.<br/>The version string has the following format: [OS.mayor.minor.patch(.minimal)], OS and mayor version are mandatory,<br/>"minimal" indicates if version shall be interpreted as minimal version this and all above are valid.
|
||||
|
||||
sebserver.examconfig.props.label.sebAllowedVersions.addAction=Add new allowed SEB version
|
||||
sebserver.examconfig.props.label.sebAllowedVersions.deleteAction=Delete allowed SEB version
|
||||
sebserver.examconfig.props.validation.SEBVersionValidator=At least one SEB Version has wrong format
|
||||
|
||||
################################
|
||||
# SEB Exam Configuration Template
|
||||
|
|
Loading…
Reference in a new issue