finished GUI refactoring

This commit is contained in:
anhefti 2020-02-17 16:43:08 +01:00
parent 000e8c3c7d
commit 26561288c9
31 changed files with 7533 additions and 7178 deletions

View file

@ -34,7 +34,7 @@ public final class QuizData implements GrantEntity {
public static final String ATTR_ADDITIONAL_ATTRIBUTES = "ADDITIONAL_ATTRIBUTES"; public static final String ATTR_ADDITIONAL_ATTRIBUTES = "ADDITIONAL_ATTRIBUTES";
public static final String QUIZ_ATTR_ID = "quiz_id"; public static final String QUIZ_ATTR_ID = "quiz_id";
public static final String QUIZ_ATTR_INSTITUION_ID = Domain.EXAM.ATTR_INSTITUTION_ID; public static final String QUIZ_ATTR_INSTITUTION_ID = Domain.EXAM.ATTR_INSTITUTION_ID;
public static final String QUIZ_ATTR_LMS_SETUP_ID = "lms_setup_id"; public static final String QUIZ_ATTR_LMS_SETUP_ID = "lms_setup_id";
public static final String QUIZ_ATTR_LMS_TYPE = "lms_setup_type"; public static final String QUIZ_ATTR_LMS_TYPE = "lms_setup_type";
public static final String QUIZ_ATTR_NAME = "quiz_name"; public static final String QUIZ_ATTR_NAME = "quiz_name";
@ -43,10 +43,17 @@ public final class QuizData implements GrantEntity {
public static final String QUIZ_ATTR_END_TIME = "quiz_end_time"; public static final String QUIZ_ATTR_END_TIME = "quiz_end_time";
public static final String QUIZ_ATTR_START_URL = "quiz_start_url"; public static final String QUIZ_ATTR_START_URL = "quiz_start_url";
public static final String ATTR_ADDITIONAL_CREATION_TIME = "time_created";
public static final String ATTR_ADDITIONAL_SHORT_NAME = "course_short_name";
public static final String ATTR_ADDITIONAL_FULL_NAME = "course_full_name";
public static final String ATTR_ADDITIONAL_DISPLAY_NAME = "course_display_name";
public static final String ATTR_ADDITIONAL_SUMMARY = "course_summary";
public static final String ATTR_ADDITIONAL_TIME_LIMIT = "time_limit";
@JsonProperty(QUIZ_ATTR_ID) @JsonProperty(QUIZ_ATTR_ID)
public final String id; public final String id;
@JsonProperty(QUIZ_ATTR_INSTITUION_ID) @JsonProperty(QUIZ_ATTR_INSTITUTION_ID)
public final Long institutionId; public final Long institutionId;
@JsonProperty(QUIZ_ATTR_LMS_SETUP_ID) @JsonProperty(QUIZ_ATTR_LMS_SETUP_ID)
@ -76,7 +83,7 @@ public final class QuizData implements GrantEntity {
@JsonCreator @JsonCreator
public QuizData( public QuizData(
@JsonProperty(QUIZ_ATTR_ID) final String id, @JsonProperty(QUIZ_ATTR_ID) final String id,
@JsonProperty(QUIZ_ATTR_INSTITUION_ID) final Long institutionId, @JsonProperty(QUIZ_ATTR_INSTITUTION_ID) final Long institutionId,
@JsonProperty(QUIZ_ATTR_LMS_SETUP_ID) final Long lmsSetupId, @JsonProperty(QUIZ_ATTR_LMS_SETUP_ID) final Long lmsSetupId,
@JsonProperty(QUIZ_ATTR_LMS_TYPE) final LmsType lmsType, @JsonProperty(QUIZ_ATTR_LMS_TYPE) final LmsType lmsType,
@JsonProperty(QUIZ_ATTR_NAME) final String name, @JsonProperty(QUIZ_ATTR_NAME) final String name,

View file

@ -332,7 +332,9 @@ public final class Utils {
return null; return null;
} }
return text.replace("</br>", "\n"); return text
.replace("<br/>", "\n")
.replace("<br></br>", "\n");
} }
public static String encodeFormURL_UTF_8(final String value) { public static String encodeFormURL_UTF_8(final String value) {

View file

@ -141,7 +141,7 @@ public final class InstitutionalAuthenticationEntryPoint implements Authenticati
final String requestURI = request.getRequestURI(); final String requestURI = request.getRequestURI();
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Trying to verify insitution from requested entrypoint url: {}", requestURI); log.debug("Trying to verify institution from requested entrypoint url: {}", requestURI);
} }
try { try {
@ -149,7 +149,7 @@ public final class InstitutionalAuthenticationEntryPoint implements Authenticati
requestURI.lastIndexOf(Constants.SLASH) + 1, requestURI.lastIndexOf(Constants.SLASH) + 1,
requestURI.length()); requestURI.length());
} catch (final Exception e) { } catch (final Exception e) {
log.error("Fauled to extract institutional URL suffix: {}", e.getMessage()); log.error("Failed to extract institutional URL suffix: {}", e.getMessage());
return null; return null;
} }
} }

View file

@ -14,6 +14,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Function; import java.util.function.Function;
@ -117,6 +118,8 @@ public class ExamForm implements TemplateComposer {
private final static LocTextKey CONFIG_LIST_TITLE_KEY = private final static LocTextKey CONFIG_LIST_TITLE_KEY =
new LocTextKey("sebserver.exam.configuration.list.title"); new LocTextKey("sebserver.exam.configuration.list.title");
private final static LocTextKey CONFIG_LIST_TITLE_TOOLTIP_KEY =
new LocTextKey("sebserver.exam.configuration.list.title" + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
private final static LocTextKey CONFIG_NAME_COLUMN_KEY = private final static LocTextKey CONFIG_NAME_COLUMN_KEY =
new LocTextKey("sebserver.exam.configuration.list.column.name"); new LocTextKey("sebserver.exam.configuration.list.column.name");
private final static LocTextKey CONFIG_DESCRIPTION_COLUMN_KEY = private final static LocTextKey CONFIG_DESCRIPTION_COLUMN_KEY =
@ -128,6 +131,8 @@ public class ExamForm implements TemplateComposer {
private final static LocTextKey INDICATOR_LIST_TITLE_KEY = private final static LocTextKey INDICATOR_LIST_TITLE_KEY =
new LocTextKey("sebserver.exam.indicator.list.title"); new LocTextKey("sebserver.exam.indicator.list.title");
private final static LocTextKey INDICATOR_LIST_TITLE_TOOLTIP_KEY =
new LocTextKey("sebserver.exam.indicator.list.title" + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
private final static LocTextKey INDICATOR_TYPE_COLUMN_KEY = private final static LocTextKey INDICATOR_TYPE_COLUMN_KEY =
new LocTextKey("sebserver.exam.indicator.list.column.type"); new LocTextKey("sebserver.exam.indicator.list.column.type");
private final static LocTextKey INDICATOR_NAME_COLUMN_KEY = private final static LocTextKey INDICATOR_NAME_COLUMN_KEY =
@ -326,11 +331,12 @@ public class ExamForm implements TemplateComposer {
.addField(FormBuilder.singleSelection( .addField(FormBuilder.singleSelection(
Domain.EXAM.ATTR_TYPE, Domain.EXAM.ATTR_TYPE,
FORM_TYPE_TEXT_KEY, FORM_TYPE_TEXT_KEY,
String.valueOf(exam.type), (exam.type != null) ? String.valueOf(exam.type) : Exam.ExamType.UNDEFINED.name(),
this.resourceService::examTypeResources) this.resourceService::examTypeResources)
.withLabelSpan(2) .withLabelSpan(2)
.withInputSpan(4) .withInputSpan(4)
.withEmptyCellSpan(2)) .withEmptyCellSpan(2)
.mandatory(!readonly))
.addField(FormBuilder.multiComboSelection( .addField(FormBuilder.multiComboSelection(
Domain.EXAM.ATTR_SUPPORTER, Domain.EXAM.ATTR_SUPPORTER,
@ -339,7 +345,8 @@ public class ExamForm implements TemplateComposer {
this.resourceService::examSupporterResources) this.resourceService::examSupporterResources)
.withLabelSpan(2) .withLabelSpan(2)
.withInputSpan(4) .withInputSpan(4)
.withEmptyCellSpan(2)) .withEmptyCellSpan(2)
.mandatory(!readonly))
.buildFor(importFromQuizData .buildFor(importFromQuizData
? this.restService.getRestCall(ImportAsExam.class) ? this.restService.getRestCall(ImportAsExam.class)
@ -398,7 +405,8 @@ public class ExamForm implements TemplateComposer {
this.widgetFactory.labelLocalized( this.widgetFactory.labelLocalized(
content, content,
CustomVariant.TEXT_H3, CustomVariant.TEXT_H3,
CONFIG_LIST_TITLE_KEY); CONFIG_LIST_TITLE_KEY,
CONFIG_LIST_TITLE_TOOLTIP_KEY);
this.widgetFactory.labelSeparator(content); this.widgetFactory.labelSeparator(content);
final EntityTable<ExamConfigurationMap> configurationTable = final EntityTable<ExamConfigurationMap> configurationTable =
@ -428,6 +436,13 @@ public class ExamForm implements TemplateComposer {
() -> modifyGrant, () -> modifyGrant,
this::viewExamConfigPageAction) this::viewExamConfigPageAction)
.withSelectionListener(this.pageService.getSelectionPublisher(
pageContext,
ActionDefinition.EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP,
ActionDefinition.EXAM_CONFIGURATION_DELETE_FROM_LIST,
ActionDefinition.EXAM_CONFIGURATION_EXPORT,
ActionDefinition.EXAM_CONFIGURATION_GET_CONFIG_KEY))
.compose(pageContext.copyOf(content)); .compose(pageContext.copyOf(content));
final EntityKey configMapKey = (configurationTable.hasAnyContent()) final EntityKey configMapKey = (configurationTable.hasAnyContent())
@ -447,7 +462,7 @@ public class ExamForm implements TemplateComposer {
.newAction(ActionDefinition.EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP) .newAction(ActionDefinition.EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP)
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
.withEntityKey(configMapKey) .withEntityKey(configMapKey)
.publishIf(() -> modifyGrant && configurationTable.hasAnyContent()) .publishIf(() -> modifyGrant && configurationTable.hasAnyContent(), false)
.newAction(ActionDefinition.EXAM_CONFIGURATION_DELETE_FROM_LIST) .newAction(ActionDefinition.EXAM_CONFIGURATION_DELETE_FROM_LIST)
.withEntityKey(entityKey) .withEntityKey(entityKey)
@ -461,7 +476,7 @@ public class ExamForm implements TemplateComposer {
} }
return null; return null;
}) })
.publishIf(() -> modifyGrant && configurationTable.hasAnyContent() && editable) .publishIf(() -> modifyGrant && configurationTable.hasAnyContent() && editable, false)
.newAction(ActionDefinition.EXAM_CONFIGURATION_EXPORT) .newAction(ActionDefinition.EXAM_CONFIGURATION_EXPORT)
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
@ -470,7 +485,7 @@ public class ExamForm implements TemplateComposer {
this::downloadExamConfigAction, this::downloadExamConfigAction,
CONFIG_EMPTY_SELECTION_TEXT_KEY) CONFIG_EMPTY_SELECTION_TEXT_KEY)
.noEventPropagation() .noEventPropagation()
.publishIf(() -> userGrantCheck.r() && configurationTable.hasAnyContent()) .publishIf(() -> userGrantCheck.r() && configurationTable.hasAnyContent(), false)
.newAction(ActionDefinition.EXAM_CONFIGURATION_GET_CONFIG_KEY) .newAction(ActionDefinition.EXAM_CONFIGURATION_GET_CONFIG_KEY)
.withSelect( .withSelect(
@ -478,14 +493,15 @@ public class ExamForm implements TemplateComposer {
this::getExamConfigKey, this::getExamConfigKey,
CONFIG_EMPTY_SELECTION_TEXT_KEY) CONFIG_EMPTY_SELECTION_TEXT_KEY)
.noEventPropagation() .noEventPropagation()
.publishIf(() -> userGrantCheck.r() && configurationTable.hasAnyContent()); .publishIf(() -> userGrantCheck.r() && configurationTable.hasAnyContent(), false);
// List of Indicators // List of Indicators
this.widgetFactory.label(content, ""); this.widgetFactory.label(content, StringUtils.EMPTY);
this.widgetFactory.labelLocalized( this.widgetFactory.labelLocalized(
content, content,
CustomVariant.TEXT_H3, CustomVariant.TEXT_H3,
INDICATOR_LIST_TITLE_KEY); INDICATOR_LIST_TITLE_KEY,
INDICATOR_LIST_TITLE_TOOLTIP_KEY);
this.widgetFactory.labelSeparator(content); this.widgetFactory.labelSeparator(content);
final EntityTable<Indicator> indicatorTable = final EntityTable<Indicator> indicatorTable =
@ -520,6 +536,11 @@ public class ExamForm implements TemplateComposer {
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
.create()) .create())
.withSelectionListener(this.pageService.getSelectionPublisher(
pageContext,
ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST,
ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST))
.compose(pageContext.copyOf(content)); .compose(pageContext.copyOf(content));
actionBuilder actionBuilder
@ -534,7 +555,7 @@ public class ExamForm implements TemplateComposer {
indicatorTable::getSelection, indicatorTable::getSelection,
PageAction::applySingleSelectionAsEntityKey, PageAction::applySingleSelectionAsEntityKey,
INDICATOR_EMPTY_SELECTION_TEXT_KEY) INDICATOR_EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> modifyGrant && indicatorTable.hasAnyContent()) .publishIf(() -> modifyGrant && indicatorTable.hasAnyContent(), false)
.newAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST) .newAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST)
.withEntityKey(entityKey) .withEntityKey(entityKey)
@ -542,7 +563,7 @@ public class ExamForm implements TemplateComposer {
indicatorTable::getSelection, indicatorTable::getSelection,
this::deleteSelectedIndicator, this::deleteSelectedIndicator,
INDICATOR_EMPTY_SELECTION_TEXT_KEY) INDICATOR_EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> modifyGrant && indicatorTable.hasAnyContent()); .publishIf(() -> modifyGrant && indicatorTable.hasAnyContent(), false);
} }
} }
@ -560,7 +581,7 @@ public class ExamForm implements TemplateComposer {
processFormSave, processFormSave,
true, true,
this.restService, this.restService,
t -> log.error("Failed to intially restrict the course for SEB on LMS: {}", t.getMessage())); t -> log.error("Failed to initially restrict the course for SEB on LMS: {}", t.getMessage()));
} }
return processFormSave; return processFormSave;
@ -589,7 +610,7 @@ public class ExamForm implements TemplateComposer {
result result
.stream() .stream()
.map(message -> this.consistencyMessageMapping.get(message.messageCode)) .map(message -> this.consistencyMessageMapping.get(message.messageCode))
.filter(message -> message != null) .filter(Objects::nonNull)
.forEach(message -> this.widgetFactory.labelLocalized( .forEach(message -> this.widgetFactory.labelLocalized(
warningPanel, warningPanel,
CustomVariant.MESSAGE, CustomVariant.MESSAGE,
@ -699,7 +720,7 @@ public class ExamForm implements TemplateComposer {
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.withQueryParam(QuizData.QUIZ_ATTR_LMS_SETUP_ID, parentEntityKey.modelId) .withQueryParam(QuizData.QUIZ_ATTR_LMS_SETUP_ID, parentEntityKey.modelId)
.call() .call()
.map(quizzData -> new Exam(quizzData)) .map(Exam::new)
.onError(error -> pageContext.notifyLoadError(EntityType.EXAM, error)); .onError(error -> pageContext.notifyLoadError(EntityType.EXAM, error));
} }
@ -735,7 +756,7 @@ public class ExamForm implements TemplateComposer {
.append(")") .append(")")
.append("</span>") .append("</span>")
.append(" | "), .append(" | "),
(sb1, sb2) -> sb1.append(sb2)); StringBuilder::append);
builder.delete(builder.length() - 3, builder.length() - 1); builder.delete(builder.length() - 3, builder.length() - 1);
return builder.toString(); return builder.toString();
} }

View file

@ -197,30 +197,30 @@ public class ExamList implements TemplateComposer {
.newAction(ActionDefinition.EXAM_VIEW_FROM_LIST) .newAction(ActionDefinition.EXAM_VIEW_FROM_LIST)
.create()) .create())
.withSelectionListener(this.pageService.getSelectionPublisher(
pageContext,
ActionDefinition.EXAM_VIEW_FROM_LIST,
ActionDefinition.EXAM_MODIFY_FROM_LIST))
.compose(pageContext.copyOf(content)); .compose(pageContext.copyOf(content));
// propagate content actions to action-pane // propagate content actions to action-pane
final GrantCheck userGrant = currentUser.grantCheck(EntityType.EXAM); final GrantCheck userGrant = currentUser.grantCheck(EntityType.EXAM);
actionBuilder actionBuilder
// Removed as discussed in SEBSERV-52
// .newAction(ActionDefinition.EXAM_IMPORT)
// .publishIf(userGrant::im)
.newAction(ActionDefinition.EXAM_VIEW_FROM_LIST) .newAction(ActionDefinition.EXAM_VIEW_FROM_LIST)
.withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
.publishIf(table::hasAnyContent) .publishIf(table::hasAnyContent, false)
.newAction(ActionDefinition.EXAM_MODIFY_FROM_LIST) .newAction(ActionDefinition.EXAM_MODIFY_FROM_LIST)
.withSelect( .withSelect(
table.getGrantedSelection(currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION), table.getGrantedSelection(currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION),
action -> modifyExam(action, table), action -> modifyExam(action, table),
EMPTY_SELECTION_TEXT_KEY) EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> userGrant.im() && table.hasAnyContent()); .publishIf(() -> userGrant.im() && table.hasAnyContent(), false);
} }
static final PageAction modifyExam(final PageAction action, final EntityTable<Exam> table) { static PageAction modifyExam(final PageAction action, final EntityTable<Exam> table) {
final Exam exam = table.getSingleSelectedROWData(); final Exam exam = table.getSingleSelectedROWData();
if (exam == null) { if (exam == null) {
@ -237,22 +237,20 @@ public class ExamList implements TemplateComposer {
return action.withEntityKey(action.getSingleSelection()); return action.withEntityKey(action.getSingleSelection());
} }
static final BiConsumer<TableItem, ExamConfigurationMap> decorateOnExamMapConsistency( static BiConsumer<TableItem, ExamConfigurationMap> decorateOnExamMapConsistency(
final PageService pageService) { final PageService pageService) {
return (item, examMap) -> { return (item, examMap) -> pageService.getRestService().getBuilder(GetExam.class)
pageService.getRestService().getBuilder(GetExam.class)
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(examMap.examId)) .withURIVariable(API.PARAM_MODEL_ID, String.valueOf(examMap.examId))
.call() .call()
.ifPresent(exam -> decorateOnExamConsistency(item, exam, pageService)); .ifPresent(exam -> decorateOnExamConsistency(item, exam, pageService));
};
} }
static final BiConsumer<TableItem, Exam> decorateOnExamConsistency(final PageService pageService) { static BiConsumer<TableItem, Exam> decorateOnExamConsistency(final PageService pageService) {
return (item, exam) -> decorateOnExamConsistency(item, exam, pageService); return (item, exam) -> decorateOnExamConsistency(item, exam, pageService);
} }
static final void decorateOnExamConsistency( static void decorateOnExamConsistency(
final TableItem item, final TableItem item,
final Exam exam, final Exam exam,
final PageService pageService) { final PageService pageService) {

View file

@ -49,6 +49,10 @@ public class ExamSebRestrictionSettings {
private final static LocTextKey SEB_RESTRICTION_FORM_TITLE = private final static LocTextKey SEB_RESTRICTION_FORM_TITLE =
new LocTextKey("sebserver.exam.action.sebrestriction.details"); new LocTextKey("sebserver.exam.action.sebrestriction.details");
private final static LocTextKey SEB_RESTRICTION_FORM_INFO =
new LocTextKey("sebserver.exam.form.sebrestriction.info");
private final static LocTextKey SEB_RESTRICTION_FORM_INFO_TEXT =
new LocTextKey("sebserver.exam.form.sebrestriction.info-text");
private final static LocTextKey SEB_RESTRICTION_FORM_CONFIG_KEYS = private final static LocTextKey SEB_RESTRICTION_FORM_CONFIG_KEYS =
new LocTextKey("sebserver.exam.form.sebrestriction.configKeys"); new LocTextKey("sebserver.exam.form.sebrestriction.configKeys");
private final static LocTextKey SEB_RESTRICTION_FORM_BROWSER_KEYS = private final static LocTextKey SEB_RESTRICTION_FORM_BROWSER_KEYS =
@ -95,7 +99,7 @@ public class ExamSebRestrictionSettings {
}; };
} }
private static final boolean doCreate( private static boolean doCreate(
final PageService pageService, final PageService pageService,
final PageContext pageContext, final PageContext pageContext,
final FormHandle<?> formHandle) { final FormHandle<?> formHandle) {
@ -187,6 +191,14 @@ public class ExamSebRestrictionSettings {
.withEmptyCellSeparation(false) .withEmptyCellSeparation(false)
.readonly(false) .readonly(false)
.addField(FormBuilder.text(
"Info",
SEB_RESTRICTION_FORM_INFO,
pageService.getI18nSupport().getText(SEB_RESTRICTION_FORM_INFO_TEXT))
.asArea(50)
.asHTML()
.readonly(true))
.addField(FormBuilder.text( .addField(FormBuilder.text(
SebRestriction.ATTR_CONFIG_KEYS, SebRestriction.ATTR_CONFIG_KEYS,
SEB_RESTRICTION_FORM_CONFIG_KEYS, SEB_RESTRICTION_FORM_CONFIG_KEYS,
@ -207,7 +219,7 @@ public class ExamSebRestrictionSettings {
SEB_RESTRICTION_FORM_EDX_WHITE_LIST_PATHS, SEB_RESTRICTION_FORM_EDX_WHITE_LIST_PATHS,
sebRestriction.getAdditionalProperties() sebRestriction.getAdditionalProperties()
.get(OpenEdxSebRestriction.ATTR_WHITELIST_PATHS), .get(OpenEdxSebRestriction.ATTR_WHITELIST_PATHS),
() -> resourceService.sebRestrictionWhiteListResources())) resourceService::sebRestrictionWhiteListResources))
.addFieldIf( .addFieldIf(
() -> lmsType == LmsType.OPEN_EDX, () -> lmsType == LmsType.OPEN_EDX,
@ -216,7 +228,7 @@ public class ExamSebRestrictionSettings {
SEB_RESTRICTION_FORM_EDX_PERMISSIONS, SEB_RESTRICTION_FORM_EDX_PERMISSIONS,
sebRestriction.getAdditionalProperties() sebRestriction.getAdditionalProperties()
.get(OpenEdxSebRestriction.ATTR_PERMISSION_COMPONENTS), .get(OpenEdxSebRestriction.ATTR_PERMISSION_COMPONENTS),
() -> resourceService.sebRestrictionPermissionResources())) resourceService::sebRestrictionPermissionResources))
.addFieldIf( .addFieldIf(
() -> lmsType == LmsType.OPEN_EDX, () -> lmsType == LmsType.OPEN_EDX,

View file

@ -114,7 +114,7 @@ final class ExamToConfigBindingForm {
}; };
} }
private static final boolean doCreate( private static boolean doCreate(
final PageService pageService, final PageService pageService,
final PageContext pageContext, final PageContext pageContext,
final FormHandle<ExamConfigurationMap> formHandle) { final FormHandle<ExamConfigurationMap> formHandle) {
@ -212,7 +212,8 @@ final class ExamToConfigBindingForm {
CONFIG_MAPPING_NAME_TEXT_KEY, CONFIG_MAPPING_NAME_TEXT_KEY,
String.valueOf(examConfigurationMap.configurationNodeId), String.valueOf(examConfigurationMap.configurationNodeId),
resourceService::examConfigurationSelectionResources) resourceService::examConfigurationSelectionResources)
.withSelectionListener(form -> updateFormValuesFromConfigSelection(form, resourceService))) .withSelectionListener(form -> updateFormValuesFromConfigSelection(form, resourceService))
.mandatory())
.addField(FormBuilder.text( .addField(FormBuilder.text(
Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION,

View file

@ -142,20 +142,23 @@ public class IndicatorForm implements TemplateComposer {
.addField(FormBuilder.text( .addField(FormBuilder.text(
Domain.INDICATOR.ATTR_NAME, Domain.INDICATOR.ATTR_NAME,
FORM_NAME_TEXT_KEY, FORM_NAME_TEXT_KEY,
indicator.name)) indicator.name)
.mandatory(!isReadonly))
.addField(FormBuilder.singleSelection( .addField(FormBuilder.singleSelection(
Domain.INDICATOR.ATTR_TYPE, Domain.INDICATOR.ATTR_TYPE,
FORM_TYPE_TEXT_KEY, FORM_TYPE_TEXT_KEY,
(indicator.type != null) ? indicator.type.name() : null, (indicator.type != null) ? indicator.type.name() : null,
this.resourceService::indicatorTypeResources) this.resourceService::indicatorTypeResources)
.withSelectionListener(this::updateForm)) .withSelectionListener(this::updateForm)
.mandatory(!isReadonly))
.addField(FormBuilder.text( .addField(FormBuilder.text(
TYPE_DESCRIPTION_FIELD_NAME, TYPE_DESCRIPTION_FIELD_NAME,
FORM_DESC_TEXT_KEY, FORM_DESC_TEXT_KEY,
typeDescription) typeDescription)
.asArea() .asArea()
.asHTML(true)
.readonly(true) .readonly(true)
.withInputSpan(6)) .withInputSpan(6))
@ -189,7 +192,7 @@ public class IndicatorForm implements TemplateComposer {
} }
private final void updateForm(final Form form) { private void updateForm(final Form form) {
final String typeValue = form.getFieldValue(Domain.INDICATOR.ATTR_TYPE); final String typeValue = form.getFieldValue(Domain.INDICATOR.ATTR_TYPE);
if (StringUtils.isNotBlank(typeValue)) { if (StringUtils.isNotBlank(typeValue)) {
form.setFieldValue( form.setFieldValue(

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.content;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Function; import java.util.function.Function;
import ch.ethz.seb.sebserver.gui.form.Form;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@ -84,6 +85,8 @@ public class LmsSetupForm implements TemplateComposer {
new LocTextKey("sebserver.lmssetup.form.proxy.port"); new LocTextKey("sebserver.lmssetup.form.proxy.port");
private static final LocTextKey FORM_PROXY_AUTH_CREDENTIALS_KEY = private static final LocTextKey FORM_PROXY_AUTH_CREDENTIALS_KEY =
new LocTextKey("sebserver.lmssetup.form.proxy.auth-credentials"); new LocTextKey("sebserver.lmssetup.form.proxy.auth-credentials");
public static final LocTextKey LMS_SETUP_TEST_OK =
new LocTextKey("sebserver.lmssetup.action.test.ok");
private final PageService pageService; private final PageService pageService;
private final ResourceService resourceService; private final ResourceService resourceService;
@ -160,38 +163,49 @@ public class LmsSetupForm implements TemplateComposer {
.putStaticValueIf(isNotNew, .putStaticValueIf(isNotNew,
Domain.LMS_SETUP.ATTR_LMS_TYPE, Domain.LMS_SETUP.ATTR_LMS_TYPE,
String.valueOf(lmsSetup.getLmsType())) String.valueOf(lmsSetup.getLmsType()))
.addFieldIf( .addFieldIf(
isSEBAdmin, isSEBAdmin,
() -> FormBuilder.singleSelection( () -> FormBuilder.singleSelection(
Domain.LMS_SETUP.ATTR_INSTITUTION_ID, Domain.LMS_SETUP.ATTR_INSTITUTION_ID,
FORM_INSTITUTION_TEXT_KEY, FORM_INSTITUTION_TEXT_KEY,
String.valueOf(lmsSetup.getInstitutionId()), String.valueOf(lmsSetup.getInstitutionId()),
() -> this.resourceService.institutionResource()) this.resourceService::institutionResource)
.readonly(true)) .readonly(true))
.addField(FormBuilder.text( .addField(FormBuilder.text(
Domain.LMS_SETUP.ATTR_NAME, Domain.LMS_SETUP.ATTR_NAME,
FORM_NAME_TEXT_KEY, FORM_NAME_TEXT_KEY,
lmsSetup.getName())) lmsSetup.getName())
.mandatory(!readonly))
.addField(FormBuilder.singleSelection( .addField(FormBuilder.singleSelection(
Domain.LMS_SETUP.ATTR_LMS_TYPE, Domain.LMS_SETUP.ATTR_LMS_TYPE,
FORM_TYPE_TEXT_KEY, FORM_TYPE_TEXT_KEY,
(lmsType != null) ? lmsType.name() : LmsType.MOCKUP.name(), (lmsType != null) ? lmsType.name() : LmsType.MOCKUP.name(),
this.resourceService::lmsTypeResources) this.resourceService::lmsTypeResources)
.readonlyIf(isNotNew)) .readonlyIf(isNotNew)
.mandatory(!readonly))
.addField(FormBuilder.text( .addField(FormBuilder.text(
Domain.LMS_SETUP.ATTR_LMS_URL, Domain.LMS_SETUP.ATTR_LMS_URL,
FORM_URL_TEXT_KEY, FORM_URL_TEXT_KEY,
lmsSetup.getLmsApiUrl())) lmsSetup.getLmsApiUrl())
.mandatory(!readonly))
.addField(FormBuilder.text( .addField(FormBuilder.text(
Domain.LMS_SETUP.ATTR_LMS_CLIENTNAME, Domain.LMS_SETUP.ATTR_LMS_CLIENTNAME,
FORM_CLIENTNAME_LMS_TEXT_KEY, FORM_CLIENTNAME_LMS_TEXT_KEY,
(lmsSetup.getLmsAuthName() != null) ? lmsSetup.getLmsAuthName() : null)) lmsSetup.getLmsAuthName())
.mandatory(!readonly))
.addFieldIf( .addFieldIf(
isEdit, isEdit,
() -> FormBuilder.text( () -> FormBuilder.text(
Domain.LMS_SETUP.ATTR_LMS_CLIENTSECRET, Domain.LMS_SETUP.ATTR_LMS_CLIENTSECRET,
FORM_SECRET_LMS_TEXT_KEY) FORM_SECRET_LMS_TEXT_KEY)
.asPasswordField()) .asPasswordField()
.mandatory(!readonly))
.addFieldIf( .addFieldIf(
() -> readonly, () -> readonly,
@ -230,7 +244,7 @@ public class LmsSetupForm implements TemplateComposer {
() -> FormBuilder.text( () -> FormBuilder.text(
Domain.LMS_SETUP.ATTR_LMS_PROXY_AUTH_USERNAME, Domain.LMS_SETUP.ATTR_LMS_PROXY_AUTH_USERNAME,
FORM_PROXY_AUTH_CREDENTIALS_KEY, FORM_PROXY_AUTH_CREDENTIALS_KEY,
(lmsSetup.getProxyAuthUsername() != null) ? lmsSetup.getProxyAuthUsername() : null) lmsSetup.getProxyAuthUsername())
.withInputSpan(3) .withInputSpan(3)
.withEmptyCellSpan(0)) .withEmptyCellSpan(0))
.addFieldIf( .addFieldIf(
@ -256,12 +270,6 @@ public class LmsSetupForm implements TemplateComposer {
.withEntityKey(entityKey) .withEntityKey(entityKey)
.publishIf(() -> modifyGrant && readonly && institutionActive) .publishIf(() -> modifyGrant && readonly && institutionActive)
.newAction(ActionDefinition.LMS_SETUP_TEST_AND_SAVE)
.withEntityKey(entityKey)
.withExec(action -> this.testAdHoc(action, formHandle))
.ignoreMoveAwayFromEdit()
.publishIf(() -> modifyGrant && !readonly)
.newAction(ActionDefinition.LMS_SETUP_DEACTIVATE) .newAction(ActionDefinition.LMS_SETUP_DEACTIVATE)
.withEntityKey(entityKey) .withEntityKey(entityKey)
.withSimpleRestCall(restService, DeactivateLmsSetup.class) .withSimpleRestCall(restService, DeactivateLmsSetup.class)
@ -270,7 +278,7 @@ public class LmsSetupForm implements TemplateComposer {
.newAction(ActionDefinition.LMS_SETUP_ACTIVATE) .newAction(ActionDefinition.LMS_SETUP_ACTIVATE)
.withEntityKey(entityKey) .withEntityKey(entityKey)
.withExec(action -> activate(action, formHandle)) .withExec(action -> activate(action, formHandle, restService))
.publishIf(() -> writeGrant && readonly && institutionActive && !lmsSetup.isActive()) .publishIf(() -> writeGrant && readonly && institutionActive && !lmsSetup.isActive())
.newAction(ActionDefinition.LMS_SETUP_SAVE) .newAction(ActionDefinition.LMS_SETUP_SAVE)
@ -279,6 +287,17 @@ public class LmsSetupForm implements TemplateComposer {
.ignoreMoveAwayFromEdit() .ignoreMoveAwayFromEdit()
.publishIf(() -> !readonly) .publishIf(() -> !readonly)
.newAction(ActionDefinition.LMS_SETUP_SAVE_AND_ACTIVATE)
.withEntityKey(entityKey)
.withExec(action -> {
this.testAdHoc(action, formHandle);
PageAction newAction = formHandle.saveAndActivate(action);
pageContext.publishInfo(LMS_SETUP_TEST_OK);
return newAction;
})
.ignoreMoveAwayFromEdit()
.publishIf(() -> !readonly && !lmsSetup.isActive())
.newAction(ActionDefinition.LMS_SETUP_CANCEL_MODIFY) .newAction(ActionDefinition.LMS_SETUP_CANCEL_MODIFY)
.withEntityKey(entityKey) .withEntityKey(entityKey)
.withExec(this.pageService.backToCurrentFunction()) .withExec(this.pageService.backToCurrentFunction())
@ -286,11 +305,15 @@ public class LmsSetupForm implements TemplateComposer {
} }
/** Save and test connection before activation */ /** Save and test connection before activation */
private PageAction activate(final PageAction action, final FormHandle<LmsSetup> formHandle) { public static PageAction activate(
final PageAction action,
final FormHandle<LmsSetup> formHandle,
final RestService restService) {
// first test the LMS Setup. If this fails the action execution will stops // first test the LMS Setup. If this fails the action execution will stops
final PageAction testLmsSetup = this.testLmsSetup(action, formHandle, false); final PageAction testLmsSetup = testLmsSetup(action, formHandle, restService);
// if LMS Setup test was successful, the activation action applies // if LMS Setup test was successful, the activation action applies
this.resourceService.getRestService().getBuilder(ActivateLmsSetup.class) restService.getBuilder(ActivateLmsSetup.class)
.withURIVariable( .withURIVariable(
API.PARAM_MODEL_ID, API.PARAM_MODEL_ID,
action.pageContext().getAttribute(AttributeKeys.ENTITY_ID)) action.pageContext().getAttribute(AttributeKeys.ENTITY_ID))
@ -306,7 +329,7 @@ public class LmsSetupForm implements TemplateComposer {
// reset previous errors // reset previous errors
formHandle.process( formHandle.process(
Utils.truePredicate(), Utils.truePredicate(),
fieldAccessor -> fieldAccessor.resetError()); Form.FormFieldAccessor::resetError);
// first test the connection on ad hoc object // first test the connection on ad hoc object
final Result<LmsSetupTestResult> result = this.resourceService.getRestService() final Result<LmsSetupTestResult> result = this.resourceService.getRestService()
@ -326,44 +349,29 @@ public class LmsSetupForm implements TemplateComposer {
} }
} }
return handleTestResult( return action;
action,
a -> {
// try to save the LmsSetup
final PageAction processFormSave = formHandle.processFormSave(a);
processFormSave.pageContext().publishInfo(
new LocTextKey("sebserver.lmssetup.action.test.ok"));
return processFormSave;
},
result.getOrThrow());
} }
/** LmsSetup test action implementation */ /** LmsSetup test action implementation */
private PageAction testLmsSetup( public static PageAction testLmsSetup(
final PageAction action, final PageAction action,
final FormHandle<LmsSetup> formHandle, final boolean saveFirst) { final FormHandle<LmsSetup> formHandle,
final RestService restService) {
if (saveFirst) {
final Result<LmsSetup> postResult = formHandle.doAPIPost();
if (postResult.hasError()) {
formHandle.handleError(postResult.getError());
postResult.getOrThrow();
}
}
// Call the testing endpoint with the specified data to test // Call the testing endpoint with the specified data to test
final EntityKey entityKey = action.getEntityKey(); final EntityKey entityKey = action.getEntityKey();
final RestService restService = this.resourceService.getRestService();
final Result<LmsSetupTestResult> result = restService.getBuilder(TestLmsSetup.class) final Result<LmsSetupTestResult> result = restService.getBuilder(TestLmsSetup.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.getModelId()) .withURIVariable(API.PARAM_MODEL_ID, entityKey.getModelId())
.call(); .call();
// ... and handle the response // ... and handle the response
if (result.hasError()) { if (result.hasError()) {
if (formHandle.handleError(result.getError())) { if (formHandle != null && formHandle.handleError(result.getError())) {
throw new PageMessageException( throw new PageMessageException(
new LocTextKey("sebserver.lmssetup.action.test.missingParameter")); new LocTextKey("sebserver.lmssetup.action.test.missingParameter"));
} }
result.getOrThrow();
} }
return handleTestResult( return handleTestResult(
@ -377,7 +385,7 @@ public class LmsSetupForm implements TemplateComposer {
result.getOrThrow()); result.getOrThrow());
} }
private PageAction handleTestResult( private static PageAction handleTestResult(
final PageAction action, final PageAction action,
final Function<PageAction, PageAction> onOK, final Function<PageAction, PageAction> onOK,
final LmsSetupTestResult testResult) { final LmsSetupTestResult testResult) {

View file

@ -144,9 +144,20 @@ public class LmsSetupList implements TemplateComposer {
this.pageService.getResourceService().<LmsSetup> localizedActivityFunction()) this.pageService.getResourceService().<LmsSetup> localizedActivityFunction())
.withFilter(this.activityFilter) .withFilter(this.activityFilter)
.sortable()) .sortable())
.withDefaultAction(actionBuilder .withDefaultAction(actionBuilder
.newAction(ActionDefinition.LMS_SETUP_VIEW_FROM_LIST) .newAction(ActionDefinition.LMS_SETUP_VIEW_FROM_LIST)
.create()) .create())
.withSelectionListener(this.pageService.getSelectionPublisher(
ActionDefinition.LMS_SETUP_TOGGLE_ACTIVITY,
ActionDefinition.LMS_SETUP_ACTIVATE,
ActionDefinition.LMS_SETUP_DEACTIVATE,
pageContext,
ActionDefinition.LMS_SETUP_VIEW_FROM_LIST,
ActionDefinition.LMS_SETUP_MODIFY_FROM_LIST,
ActionDefinition.LMS_SETUP_TOGGLE_ACTIVITY))
.compose(pageContext.copyOf(content)); .compose(pageContext.copyOf(content));
// propagate content actions to action-pane // propagate content actions to action-pane
@ -158,13 +169,21 @@ public class LmsSetupList implements TemplateComposer {
.newAction(ActionDefinition.LMS_SETUP_VIEW_FROM_LIST) .newAction(ActionDefinition.LMS_SETUP_VIEW_FROM_LIST)
.withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> table.hasAnyContent()) .publishIf(table::hasAnyContent, false)
.newAction(ActionDefinition.LMS_SETUP_MODIFY_FROM_LIST) .newAction(ActionDefinition.LMS_SETUP_MODIFY_FROM_LIST)
.withSelect( .withSelect(
table.getGrantedSelection(currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION), table.getGrantedSelection(currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION),
PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> userGrant.im() && table.hasAnyContent()); .publishIf(() -> userGrant.im() && table.hasAnyContent(), false)
.newAction(ActionDefinition.LMS_SETUP_TOGGLE_ACTIVITY)
.withExec(this.pageService.activationToggleActionFunction(
table,
EMPTY_SELECTION_TEXT_KEY,
action -> LmsSetupForm.testLmsSetup(action, null, restService)))
.withConfirm(this.pageService.confirmDeactivation(table))
.publishIf(() -> userGrant.im() && table.hasAnyContent(), false);
} }

View file

@ -10,6 +10,8 @@ package ch.ethz.seb.sebserver.gui.content;
import java.util.Collection; import java.util.Collection;
import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@ -82,6 +84,7 @@ public class MonitoringClientConnection implements TemplateComposer {
private final ResourceService resourceService; private final ResourceService resourceService;
private final I18nSupport i18nSupport; private final I18nSupport i18nSupport;
private final InstructionProcessor instructionProcessor; private final InstructionProcessor instructionProcessor;
private final SebClientLogDetailsPopup sebClientLogDetailsPopup;
private final long pollInterval; private final long pollInterval;
private final int pageSize; private final int pageSize;
@ -94,6 +97,7 @@ public class MonitoringClientConnection implements TemplateComposer {
final PageService pageService, final PageService pageService,
final ResourceService resourceService, final ResourceService resourceService,
final InstructionProcessor instructionProcessor, final InstructionProcessor instructionProcessor,
final SebClientLogDetailsPopup sebClientLogDetailsPopup,
@Value("${sebserver.gui.webservice.poll-interval:500}") final long pollInterval, @Value("${sebserver.gui.webservice.poll-interval:500}") final long pollInterval,
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
@ -103,6 +107,7 @@ public class MonitoringClientConnection implements TemplateComposer {
this.i18nSupport = resourceService.getI18nSupport(); this.i18nSupport = resourceService.getI18nSupport();
this.instructionProcessor = instructionProcessor; this.instructionProcessor = instructionProcessor;
this.pollInterval = pollInterval; this.pollInterval = pollInterval;
this.sebClientLogDetailsPopup = sebClientLogDetailsPopup;
this.pageSize = pageSize; this.pageSize = pageSize;
this.typeFilter = new TableFilterAttribute( this.typeFilter = new TableFilterAttribute(
@ -160,29 +165,38 @@ public class MonitoringClientConnection implements TemplateComposer {
this.serverPushService.runServerPush( this.serverPushService.runServerPush(
new ServerPushContext(content, Utils.truePredicate()), new ServerPushContext(content, Utils.truePredicate()),
this.pollInterval, this.pollInterval,
clientConnectionDetails::updateData, context1 -> clientConnectionDetails.updateData(),
clientConnectionDetails::updateGUI); context -> clientConnectionDetails.updateGUI());
widgetFactory.labelLocalized( widgetFactory.labelLocalized(
content, content,
CustomVariant.TEXT_H3, CustomVariant.TEXT_H3,
EVENT_LIST_TITLE_KEY); EVENT_LIST_TITLE_KEY);
PageService.PageActionBuilder actionBuilder = this.pageService
.pageActionBuilder(
pageContext
.clearAttributes()
.clearEntityKeys());
// client event table for this connection // client event table for this connection
this.pageService.entityTableBuilder(restService.getRestCall(GetClientEventPage.class)) this.pageService.entityTableBuilder(restService.getRestCall(GetExtendedClientEventPage.class))
.withEmptyMessage(EMPTY_LIST_TEXT_KEY) .withEmptyMessage(EMPTY_LIST_TEXT_KEY)
.withPaging(this.pageSize) .withPaging(this.pageSize)
.withRestCallAdapter(restCallBuilder -> restCallBuilder.withQueryParam( .withRestCallAdapter(restCallBuilder -> restCallBuilder.withQueryParam(
ClientEvent.FILTER_ATTR_CONECTION_ID, ClientEvent.FILTER_ATTR_CONECTION_ID,
entityKey.modelId)) entityKey.modelId))
.withColumn(new ColumnDefinition<ClientEvent>(
.withColumn(new ColumnDefinition<ExtendedClientEvent>(
Domain.CLIENT_EVENT.ATTR_TYPE, Domain.CLIENT_EVENT.ATTR_TYPE,
LIST_COLUMN_TYPE_KEY, LIST_COLUMN_TYPE_KEY,
this.resourceService::getEventTypeName) this.resourceService::getEventTypeName)
.withFilter(this.typeFilter) .withFilter(this.typeFilter)
.sortable() .sortable()
.widthProportion(2)) .widthProportion(2))
.withColumn(new ColumnDefinition<>(
.withColumn(new ColumnDefinition<ExtendedClientEvent>(
Domain.CLIENT_EVENT.ATTR_TEXT, Domain.CLIENT_EVENT.ATTR_TEXT,
LIST_COLUMN_TEXT_KEY, LIST_COLUMN_TEXT_KEY,
ClientEvent::getText) ClientEvent::getText)
@ -190,19 +204,22 @@ public class MonitoringClientConnection implements TemplateComposer {
.sortable() .sortable()
.withCellTooltip() .withCellTooltip()
.widthProportion(4)) .widthProportion(4))
.withColumn(new ColumnDefinition<>(
.withColumn(new ColumnDefinition<ExtendedClientEvent>(
Domain.CLIENT_EVENT.ATTR_NUMERIC_VALUE, Domain.CLIENT_EVENT.ATTR_NUMERIC_VALUE,
LIST_COLUMN_VALUE_KEY, LIST_COLUMN_VALUE_KEY,
ClientEvent::getValue) ClientEvent::getValue)
.widthProportion(1)) .widthProportion(1))
.withColumn(new ColumnDefinition<>(
.withColumn(new ColumnDefinition<ExtendedClientEvent>(
Domain.CLIENT_EVENT.ATTR_CLIENT_TIME, Domain.CLIENT_EVENT.ATTR_CLIENT_TIME,
new LocTextKey(LIST_COLUMN_CLIENT_TIME_KEY.name, new LocTextKey(LIST_COLUMN_CLIENT_TIME_KEY.name,
this.i18nSupport.getUsersTimeZoneTitleSuffix()), this.i18nSupport.getUsersTimeZoneTitleSuffix()),
this::getClientTime) this::getClientTime)
.sortable() .sortable()
.widthProportion(1)) .widthProportion(1))
.withColumn(new ColumnDefinition<>(
.withColumn(new ColumnDefinition<ExtendedClientEvent>(
Domain.CLIENT_EVENT.ATTR_SERVER_TIME, Domain.CLIENT_EVENT.ATTR_SERVER_TIME,
new LocTextKey(LIST_COLUMN_SERVER_TIME_KEY.name, new LocTextKey(LIST_COLUMN_SERVER_TIME_KEY.name,
this.i18nSupport.getUsersTimeZoneTitleSuffix()), this.i18nSupport.getUsersTimeZoneTitleSuffix()),
@ -210,15 +227,17 @@ public class MonitoringClientConnection implements TemplateComposer {
.sortable() .sortable()
.widthProportion(1)) .widthProportion(1))
.withDefaultAction(t -> actionBuilder
.newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS)
.withExec(action -> sebClientLogDetailsPopup.showDetails(action, t.getSingleSelectedROWData()))
.noEventPropagation()
.create())
.compose(pageContext.copyOf(content)); .compose(pageContext.copyOf(content));
this.pageService
.pageActionBuilder(
pageContext
.clearAttributes()
.clearEntityKeys())
.newAction(ActionDefinition.MONITOR_EXAM_FROM_DETAILS) actionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_BACK_TO_OVERVIEW)
.withEntityKey(parentEntityKey) .withEntityKey(parentEntityKey)
.publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER)) .publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER))
@ -237,7 +256,7 @@ public class MonitoringClientConnection implements TemplateComposer {
} }
private final String getClientTime(final ClientEvent event) { private String getClientTime(final ClientEvent event) {
if (event == null || event.getClientTime() == null) { if (event == null || event.getClientTime() == null) {
return Constants.EMPTY_NOTE; return Constants.EMPTY_NOTE;
} }
@ -246,7 +265,7 @@ public class MonitoringClientConnection implements TemplateComposer {
.formatDisplayTime(Utils.toDateTimeUTC(event.getClientTime())); .formatDisplayTime(Utils.toDateTimeUTC(event.getClientTime()));
} }
private final String getServerTime(final ClientEvent event) { private String getServerTime(final ClientEvent event) {
if (event == null || event.getServerTime() == null) { if (event == null || event.getServerTime() == null) {
return Constants.EMPTY_NOTE; return Constants.EMPTY_NOTE;
} }

View file

@ -135,12 +135,18 @@ public class MonitoringRunningExam implements TemplateComposer {
indicators, indicators,
restCall); restCall);
clientTable.withDefaultAction( clientTable
.withDefaultAction(
actionBuilder actionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION) .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION)
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
.create(), .create(),
this.pageService); this.pageService)
.withSelectionListener(this.pageService.getSelectionPublisher(
pageContext,
ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION,
ActionDefinition.MONITOR_EXAM_QUIT_SELECTED,
ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION));
this.serverPushService.runServerPush( this.serverPushService.runServerPush(
new ServerPushContext(content, Utils.truePredicate()), new ServerPushContext(content, Utils.truePredicate()),
@ -170,7 +176,7 @@ public class MonitoringRunningExam implements TemplateComposer {
return copyOfPageAction; return copyOfPageAction;
}) })
.publishIf(privilege) .publishIf(privilege, false)
.newAction(ActionDefinition.MONITOR_EXAM_QUIT_ALL) .newAction(ActionDefinition.MONITOR_EXAM_QUIT_ALL)
.withEntityKey(entityKey) .withEntityKey(entityKey)
@ -187,7 +193,7 @@ public class MonitoringRunningExam implements TemplateComposer {
action -> this.quitSebClients(action, clientTable, false), action -> this.quitSebClients(action, clientTable, false),
EMPTY_ACTIVE_SELECTION_TEXT_KEY) EMPTY_ACTIVE_SELECTION_TEXT_KEY)
.noEventPropagation() .noEventPropagation()
.publishIf(privilege) .publishIf(privilege, false)
.newAction(ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION) .newAction(ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION)
.withEntityKey(entityKey) .withEntityKey(entityKey)
@ -197,7 +203,7 @@ public class MonitoringRunningExam implements TemplateComposer {
action -> this.disableSebClients(action, clientTable, false), action -> this.disableSebClients(action, clientTable, false),
EMPTY_SELECTION_TEXT_KEY) EMPTY_SELECTION_TEXT_KEY)
.noEventPropagation() .noEventPropagation()
.publishIf(privilege); .publishIf(privilege, false);
if (privilege.getAsBoolean()) { if (privilege.getAsBoolean()) {
@ -272,7 +278,7 @@ public class MonitoringRunningExam implements TemplateComposer {
} }
} }
private static final Function<PageAction, PageAction> showStateViewAction( private static Function<PageAction, PageAction> showStateViewAction(
final ClientConnectionTable clientTable, final ClientConnectionTable clientTable,
final ConnectionStatus status) { final ConnectionStatus status) {
@ -283,7 +289,7 @@ public class MonitoringRunningExam implements TemplateComposer {
}; };
} }
private static final Function<PageAction, PageAction> hideStateViewAction( private static Function<PageAction, PageAction> hideStateViewAction(
final ClientConnectionTable clientTable, final ClientConnectionTable clientTable,
final ConnectionStatus status) { final ConnectionStatus status) {
@ -337,7 +343,7 @@ public class MonitoringRunningExam implements TemplateComposer {
return action; return action;
} }
private final Consumer<ServerPushContext> updateTableGUI(final ClientConnectionTable clientTable) { private Consumer<ServerPushContext> updateTableGUI(final ClientConnectionTable clientTable) {
return context -> { return context -> {
if (!context.isDisposed()) { if (!context.isDisposed()) {
try { try {

View file

@ -131,13 +131,17 @@ public class MonitoringRunningExamList implements TemplateComposer {
.newAction(ActionDefinition.MONITOR_EXAM_FROM_LIST) .newAction(ActionDefinition.MONITOR_EXAM_FROM_LIST)
.create()) .create())
.withSelectionListener(this.pageService.getSelectionPublisher(
pageContext,
ActionDefinition.MONITOR_EXAM_FROM_LIST))
.compose(pageContext.copyOf(content)); .compose(pageContext.copyOf(content));
actionBuilder actionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_FROM_LIST) .newAction(ActionDefinition.MONITOR_EXAM_FROM_LIST)
.withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER)); .publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER), false);
} }

View file

@ -13,6 +13,7 @@ import java.util.Collection;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Function; import java.util.function.Function;
import ch.ethz.seb.sebserver.gbl.Constants;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
@ -57,21 +58,14 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
public class QuizDiscoveryList implements TemplateComposer { public class QuizDiscoveryList implements TemplateComposer {
// localized text keys // localized text keys
private static final LocTextKey QUIZ_DETAILS_URL_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.quiz.details.url");
private static final LocTextKey QUIZ_DETAILS_DESCRIPTION_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.quiz.details.description");
private static final LocTextKey QUIZ_DETAILS_START_TIME_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.quiz.details.starttime");
private static final LocTextKey QUIZ_DETAILS_END_TIME_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.quiz.details.endtime");
private static final LocTextKey TITLE_TEXT_KEY = private static final LocTextKey TITLE_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.list.title"); new LocTextKey("sebserver.quizdiscovery.list.title");
private static final LocTextKey EMPTY_LIST_TEXT_KEY = private static final LocTextKey EMPTY_LIST_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.list.empty"); new LocTextKey("sebserver.quizdiscovery.list.empty");
private final static LocTextKey EMPTY_SELECTION_TEXT = private final static LocTextKey EMPTY_SELECTION_TEXT =
new LocTextKey("sebserver.quizdiscovery.info.pleaseSelect"); new LocTextKey("sebserver.quizdiscovery.info.pleaseSelect");
private final static LocTextKey INSTITUION_TEXT_KEY = private final static LocTextKey INSTITUTION_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.list.column.institution"); new LocTextKey("sebserver.quizdiscovery.list.column.institution");
private final static LocTextKey LMS_TEXT_KEY = private final static LocTextKey LMS_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.list.column.lmssetup"); new LocTextKey("sebserver.quizdiscovery.list.column.lmssetup");
@ -83,6 +77,20 @@ public class QuizDiscoveryList implements TemplateComposer {
new LocTextKey("sebserver.quizdiscovery.list.column.endtime"); new LocTextKey("sebserver.quizdiscovery.list.column.endtime");
private final static LocTextKey DETAILS_TITLE_TEXT_KEY = private final static LocTextKey DETAILS_TITLE_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.quiz.details.title"); new LocTextKey("sebserver.quizdiscovery.quiz.details.title");
private static final LocTextKey QUIZ_DETAILS_URL_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.quiz.details.url");
private static final LocTextKey QUIZ_DETAILS_INSTITUTION_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.quiz.details.institution");
private static final LocTextKey QUIZ_DETAILS_LMS_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.quiz.details.lmssetup");
private static final LocTextKey QUIZ_DETAILS_NAME_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.quiz.details.name");
private static final LocTextKey QUIZ_DETAILS_DESCRIPTION_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.quiz.details.description");
private static final LocTextKey QUIZ_DETAILS_START_TIME_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.quiz.details.starttime");
private static final LocTextKey QUIZ_DETAILS_END_TIME_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.quiz.details.endtime");
private final static LocTextKey NO_IMPORT_OF_OUT_DATED_QUIZ = private final static LocTextKey NO_IMPORT_OF_OUT_DATED_QUIZ =
new LocTextKey("sebserver.quizdiscovery.quiz.import.out.dated"); new LocTextKey("sebserver.quizdiscovery.quiz.import.out.dated");
@ -151,8 +159,8 @@ public class QuizDiscoveryList implements TemplateComposer {
.withColumnIf( .withColumnIf(
isSebAdmin, isSebAdmin,
() -> new ColumnDefinition<QuizData>( () -> new ColumnDefinition<QuizData>(
QuizData.QUIZ_ATTR_INSTITUION_ID, QuizData.QUIZ_ATTR_INSTITUTION_ID,
INSTITUION_TEXT_KEY, INSTITUTION_TEXT_KEY,
quiz -> institutionNameFunction quiz -> institutionNameFunction
.apply(String.valueOf(quiz.institutionId))) .apply(String.valueOf(quiz.institutionId)))
.withFilter(this.institutionFilter)) .withFilter(this.institutionFilter))
@ -202,16 +210,16 @@ public class QuizDiscoveryList implements TemplateComposer {
.noEventPropagation() .noEventPropagation()
.create()) .create())
.withSelectionListener(this.pageService.getSelectionPublisher(
pageContext,
ActionDefinition.QUIZ_DISCOVERY_SHOW_DETAILS,
ActionDefinition.QUIZ_DISCOVERY_EXAM_IMPORT))
.compose(pageContext.copyOf(content)); .compose(pageContext.copyOf(content));
// propagate content actions to action-pane // propagate content actions to action-pane
final GrantCheck examGrant = currentUser.grantCheck(EntityType.EXAM); final GrantCheck examGrant = currentUser.grantCheck(EntityType.EXAM);
actionBuilder actionBuilder
// Removed as discussed in SEBSERV-52
// .newAction(ActionDefinition.LMS_SETUP_NEW)
// .publishIf(lmsSetupGrant::iw)
.newAction(ActionDefinition.QUIZ_DISCOVERY_SHOW_DETAILS) .newAction(ActionDefinition.QUIZ_DISCOVERY_SHOW_DETAILS)
.withSelect( .withSelect(
table::getSelection, table::getSelection,
@ -221,14 +229,14 @@ public class QuizDiscoveryList implements TemplateComposer {
institutionNameFunction), institutionNameFunction),
EMPTY_SELECTION_TEXT) EMPTY_SELECTION_TEXT)
.noEventPropagation() .noEventPropagation()
.publishIf(table::hasAnyContent) .publishIf(table::hasAnyContent, false)
.newAction(ActionDefinition.QUIZ_DISCOVERY_EXAM_IMPORT) .newAction(ActionDefinition.QUIZ_DISCOVERY_EXAM_IMPORT)
.withSelect( .withSelect(
table::getSelection, table::getSelection,
action -> this.importQuizData(action, table), action -> this.importQuizData(action, table),
EMPTY_SELECTION_TEXT) EMPTY_SELECTION_TEXT)
.publishIf(() -> examGrant.im() && table.hasAnyContent()); .publishIf(() -> examGrant.im() && table.hasAnyContent(), false);
} }
private static Function<QuizData, String> quizDataLmsSetupNameFunction(final ResourceService resourceService) { private static Function<QuizData, String> quizDataLmsSetupNameFunction(final ResourceService resourceService) {
@ -250,7 +258,7 @@ public class QuizDiscoveryList implements TemplateComposer {
return action return action
.withEntityKey(action.getSingleSelection()) .withEntityKey(action.getSingleSelection())
.withParentEntityKey(new EntityKey(selectedROWData.lmsSetupId, EntityType.LMS_SETUP)) .withParentEntityKey(new EntityKey(selectedROWData.lmsSetupId, EntityType.LMS_SETUP))
.withAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA, "true"); .withAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA, Constants.TRUE_STRING);
} }
private PageAction showDetails( private PageAction showDetails(
@ -291,17 +299,17 @@ public class QuizDiscoveryList implements TemplateComposer {
.addFieldIf( .addFieldIf(
() -> this.resourceService.getCurrentUser().get().hasRole(UserRole.SEB_SERVER_ADMIN), () -> this.resourceService.getCurrentUser().get().hasRole(UserRole.SEB_SERVER_ADMIN),
() -> FormBuilder.text( () -> FormBuilder.text(
QuizData.QUIZ_ATTR_INSTITUION_ID, QuizData.QUIZ_ATTR_INSTITUTION_ID,
INSTITUION_TEXT_KEY, QUIZ_DETAILS_INSTITUTION_TEXT_KEY,
institutionNameFunction.apply(quizData.getModelId()))) institutionNameFunction.apply(quizData.getModelId())))
.addField(FormBuilder.singleSelection( .addField(FormBuilder.singleSelection(
QuizData.QUIZ_ATTR_LMS_SETUP_ID, QuizData.QUIZ_ATTR_LMS_SETUP_ID,
LMS_TEXT_KEY, QUIZ_DETAILS_LMS_TEXT_KEY,
String.valueOf(quizData.lmsSetupId), String.valueOf(quizData.lmsSetupId),
() -> this.resourceService.lmsSetupResource())) this.resourceService::lmsSetupResource))
.addField(FormBuilder.text( .addField(FormBuilder.text(
QuizData.QUIZ_ATTR_NAME, QuizData.QUIZ_ATTR_NAME,
NAME_TEXT_KEY, QUIZ_DETAILS_NAME_TEXT_KEY,
quizData.name)) quizData.name))
.addField(FormBuilder.text( .addField(FormBuilder.text(
QuizData.QUIZ_ATTR_DESCRIPTION, QuizData.QUIZ_ATTR_DESCRIPTION,
@ -323,19 +331,17 @@ public class QuizDiscoveryList implements TemplateComposer {
if (!quizData.additionalAttributes.isEmpty()) { if (!quizData.additionalAttributes.isEmpty()) {
quizData.additionalAttributes quizData.additionalAttributes
.entrySet() .forEach((key, value) -> {
.stream() LocTextKey titleKey = new LocTextKey(TEXT_KEY_ADDITIONAL_ATTR_PREFIX + key);
.forEach(entry -> {
LocTextKey titleKey = new LocTextKey(TEXT_KEY_ADDITIONAL_ATTR_PREFIX + entry.getKey());
if (!this.pageService.getI18nSupport().hasText(titleKey)) { if (!this.pageService.getI18nSupport().hasText(titleKey)) {
titleKey = new LocTextKey(entry.getKey()); titleKey = new LocTextKey(key);
} }
formbuilder formbuilder
.addField(FormBuilder.text( .addField(FormBuilder.text(
entry.getKey(), key,
titleKey, titleKey,
toAdditionalValue(entry.getKey(), entry.getValue())) toAdditionalValue(key, value))
.asHTML(ADDITIONAL_HTML_ATTRIBUTES.contains(entry.getKey()))); .asHTML(ADDITIONAL_HTML_ATTRIBUTES.contains(key)));
}); });
} }
@ -343,7 +349,7 @@ public class QuizDiscoveryList implements TemplateComposer {
} }
private String toAdditionalValue(final String name, final String value) { private String toAdditionalValue(final String name, final String value) {
if ("timecreated".equals(name)) { if (QuizData.ATTR_ADDITIONAL_CREATION_TIME.equals(name)) {
try { try {
return this.pageService return this.pageService
.getI18nSupport() .getI18nSupport()
@ -351,7 +357,7 @@ public class QuizDiscoveryList implements TemplateComposer {
} catch (final Exception e) { } catch (final Exception e) {
return value; return value;
} }
} else if ("timelimit".equals(name)) { } else if (QuizData.ATTR_ADDITIONAL_TIME_LIMIT.equals(name)) {
try { try {
return this.pageService return this.pageService
.getI18nSupport() .getI18nSupport()

View file

@ -0,0 +1,235 @@
/*
* 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 ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
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.impl.ModalInputDialog;
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnection;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Lazy
@Component
@GuiProfile
public class SebClientLogDetailsPopup {
private static final Logger log = LoggerFactory.getLogger(SebClientLogDetailsPopup.class);
private static final LocTextKey DETAILS_TITLE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.details.title");
private static final LocTextKey DETAILS_EVENT_TILE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.details.event.title");
private static final LocTextKey DETAILS_CONNECTION_TILE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.details.connection.title");
private static final LocTextKey DETAILS_EXAM_TILE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.details.exam.title");
private static final LocTextKey FORM_TYPE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.type");
private static final LocTextKey FORM_SERVERTIME_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.server-time");
private static final LocTextKey FORM_CLIENTTIME_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.client-time");
private static final LocTextKey FORM_VALUE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.value");
private static final LocTextKey FORM_MESSAGE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.message");
private static final LocTextKey FORM_SESSION_ID_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.connection.session-id");
private static final LocTextKey FORM_ADDRESS_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.connection.address");
private static final LocTextKey FORM_TOKEN_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.connection.token");
private static final LocTextKey FORM_STATUS_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.connection.status");
private static final LocTextKey FORM_EXAM_NAME_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.exam.name");
private static final LocTextKey FORM_DESC_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.exam.description");
private static final LocTextKey FORM_EXAM_TYPE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.exam.type");
private static final LocTextKey FORM_START_TIME_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.exam.startTime");
private static final LocTextKey FORM_END_TIME_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.exam.endTime");
private final PageService pageService;
private final ResourceService resourceService;
private final RestService restService;
private final I18nSupport i18nSupport;
private final WidgetFactory widgetFactory;
public SebClientLogDetailsPopup(
final PageService pageService,
final WidgetFactory widgetFactory) {
this.pageService = pageService;
this.widgetFactory = widgetFactory;
this.resourceService = pageService.getResourceService();
this.restService = pageService.getRestService();
this.i18nSupport = pageService.getI18nSupport();
}
PageAction showDetails(final PageAction action, final ExtendedClientEvent clientEvent) {
action.getSingleSelection();
final ModalInputDialog<Void> dialog = new ModalInputDialog<>(
action.pageContext().getParent().getShell(),
this.widgetFactory);
dialog.setDialogWidth(600);
dialog.open(
DETAILS_TITLE_TEXT_KEY,
action.pageContext(),
pc -> createDetailsForm(clientEvent, pc));
return action;
}
private void createDetailsForm(final ExtendedClientEvent clientEvent, final PageContext pc) {
final Composite parent = pc.getParent();
final Composite content = this.widgetFactory.createPopupScrollComposite(parent);
// Event Details Title
this.widgetFactory.labelLocalized(
content,
WidgetFactory.CustomVariant.TEXT_H3,
DETAILS_EVENT_TILE_TEXT_KEY);
PageContext formContext = pc.copyOf(content);
this.pageService.formBuilder(formContext)
.withDefaultSpanInput(6)
.withEmptyCellSeparation(false)
.readonly(true)
.addField(FormBuilder.text(
Domain.CLIENT_EVENT.TYPE_NAME,
FORM_TYPE_TEXT_KEY,
this.resourceService.getEventTypeName(clientEvent)))
.addField(FormBuilder.text(
Domain.CLIENT_EVENT.ATTR_CLIENT_TIME,
FORM_CLIENTTIME_TEXT_KEY,
this.i18nSupport.formatDisplayDateTime(clientEvent.clientTime) + " " +
this.i18nSupport.getUsersTimeZoneTitleSuffix()))
.addField(FormBuilder.text(
Domain.CLIENT_EVENT.ATTR_SERVER_TIME,
FORM_SERVERTIME_TEXT_KEY,
this.i18nSupport.formatDisplayDateTime(clientEvent.serverTime) + " " +
this.i18nSupport.getUsersTimeZoneTitleSuffix()))
.addField(FormBuilder.text(
Domain.CLIENT_EVENT.ATTR_NUMERIC_VALUE,
FORM_VALUE_TEXT_KEY,
(clientEvent.numValue != null)
? String.valueOf(clientEvent.numValue)
: Constants.EMPTY_NOTE))
.addField(FormBuilder.text(
Domain.CLIENT_EVENT.ATTR_TEXT,
FORM_MESSAGE_TEXT_KEY,
clientEvent.text)
.asArea())
.build();
// SEB Client Connection Title
this.widgetFactory.labelLocalized(
content,
WidgetFactory.CustomVariant.TEXT_H3,
DETAILS_CONNECTION_TILE_TEXT_KEY);
final ClientConnection connection = this.restService.getBuilder(GetClientConnection.class)
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(clientEvent.connectionId))
.call()
.get(
error -> log.error("Failed to get ClientConnection for id {}", clientEvent.connectionId, error),
() -> ClientConnection.EMPTY_CLIENT_CONNECTION);
this.pageService.formBuilder(formContext)
.withDefaultSpanInput(6)
.withEmptyCellSeparation(false)
.readonly(true)
.addField(FormBuilder.text(
Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID,
FORM_SESSION_ID_TEXT_KEY,
connection.userSessionId))
.addField(FormBuilder.text(
Domain.CLIENT_CONNECTION.ATTR_CLIENT_ADDRESS,
FORM_ADDRESS_TEXT_KEY,
connection.clientAddress))
.addField(FormBuilder.text(
Domain.CLIENT_CONNECTION.ATTR_CONNECTION_TOKEN,
FORM_TOKEN_TEXT_KEY,
connection.connectionToken))
.addField(FormBuilder.text(
Domain.CLIENT_CONNECTION.ATTR_STATUS,
FORM_STATUS_TEXT_KEY,
this.resourceService.localizedClientConnectionStatusName(connection.status)))
.build();
// Exam Details Title
this.widgetFactory.labelLocalized(
content,
WidgetFactory.CustomVariant.TEXT_H3,
DETAILS_EXAM_TILE_TEXT_KEY);
final Exam exam = this.restService.getBuilder(GetExam.class)
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(clientEvent.examId))
.call()
.get(
error -> log.error("Failed to get Exam for id {}", clientEvent.examId, error),
() -> Exam.EMPTY_EXAM);
this.pageService.formBuilder(formContext)
.withDefaultSpanInput(6)
.withEmptyCellSeparation(false)
.readonly(true)
.addField(FormBuilder.text(
QuizData.QUIZ_ATTR_NAME,
FORM_EXAM_NAME_TEXT_KEY,
exam.name))
.addField(FormBuilder.text(
QuizData.QUIZ_ATTR_DESCRIPTION,
FORM_DESC_TEXT_KEY,
exam.description))
.addField(FormBuilder.text(
Domain.EXAM.ATTR_TYPE,
FORM_EXAM_TYPE_TEXT_KEY,
this.resourceService.localizedExamTypeName(exam)))
.addField(FormBuilder.text(
QuizData.QUIZ_ATTR_START_TIME,
FORM_START_TIME_TEXT_KEY,
this.i18nSupport.formatDisplayDateWithTimeZone(exam.startTime)))
.addField(FormBuilder.text(
QuizData.QUIZ_ATTR_END_TIME,
FORM_END_TIME_TEXT_KEY,
this.i18nSupport.formatDisplayDateWithTimeZone(exam.endTime)))
.build();
}
}

View file

@ -8,28 +8,14 @@
package ch.ethz.seb.sebserver.gui.content; package ch.ethz.seb.sebserver.gui.content;
import java.util.Map;
import java.util.function.Function;
import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent; import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
import ch.ethz.seb.sebserver.gui.service.ResourceService; import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
@ -37,28 +23,26 @@ 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;
import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; 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.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog;
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnection;
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute;
import ch.ethz.seb.sebserver.gui.table.EntityTable; import ch.ethz.seb.sebserver.gui.table.EntityTable;
import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType; import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; 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 java.util.Map;
import java.util.function.Function;
@Lazy @Lazy
@Component @Component
@GuiProfile @GuiProfile
public class SebClientLogs implements TemplateComposer { public class SebClientLogs implements TemplateComposer {
private static final Logger log = LoggerFactory.getLogger(SebClientLogs.class);
private static final LocTextKey DETAILS_TITLE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.details.title");
private static final LocTextKey TITLE_TEXT_KEY = private static final LocTextKey TITLE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.list.title"); new LocTextKey("sebserver.seblogs.list.title");
private static final LocTextKey EMPTY_TEXT_KEY = private static final LocTextKey EMPTY_TEXT_KEY =
@ -74,45 +58,6 @@ public class SebClientLogs implements TemplateComposer {
new LocTextKey("sebserver.seblogs.list.column.time"); new LocTextKey("sebserver.seblogs.list.column.time");
private static final LocTextKey VALUE_TEXT_KEY = private static final LocTextKey VALUE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.list.column.value"); new LocTextKey("sebserver.seblogs.list.column.value");
private static final LocTextKey DETAILS_EVENT_TILE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.details.event.title");
private static final LocTextKey DETAILS_CONNECTION_TILE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.details.connection.title");
private static final LocTextKey DETAILS_EXAM_TILE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.details.exam.title");
private static final LocTextKey FORM_TYPE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.type");
private static final LocTextKey FORM_SERVERTIME_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.server-time");
private static final LocTextKey FORM_CLIENTTIME_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.client-time");
private static final LocTextKey FORM_VALUE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.value");
private static final LocTextKey FORM_MESSAGE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.message");
private static final LocTextKey FORM_SESSION_ID_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.connection.session-id");
private static final LocTextKey FORM_ADDRESS_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.connection.address");
private static final LocTextKey FORM_TOKEN_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.connection.token");
private static final LocTextKey FORM_STATUS_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.connection.status");
private static final LocTextKey FORM_EXAM_NAME_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.exam.name");
private static final LocTextKey FORM_DESC_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.exam.description");
private static final LocTextKey FORM_EXAM_TYPE_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.exam.type");
private static final LocTextKey FORM_START_TIME_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.exam.startTime");
private static final LocTextKey FORM_END_TIME_TEXT_KEY =
new LocTextKey("sebserver.seblogs.form.column.exam.endTime");
private final static LocTextKey EMPTY_SELECTION_TEXT = private final static LocTextKey EMPTY_SELECTION_TEXT =
new LocTextKey("sebserver.seblogs.info.pleaseSelect"); new LocTextKey("sebserver.seblogs.info.pleaseSelect");
@ -124,18 +69,19 @@ public class SebClientLogs implements TemplateComposer {
private final ResourceService resourceService; private final ResourceService resourceService;
private final RestService restService; private final RestService restService;
private final I18nSupport i18nSupport; private final I18nSupport i18nSupport;
private final WidgetFactory widgetFactory; private final SebClientLogDetailsPopup sebClientLogDetailsPopup;
private final int pageSize; private final int pageSize;
public SebClientLogs( public SebClientLogs(
final PageService pageService, final PageService pageService,
final SebClientLogDetailsPopup sebClientLogDetailsPopup,
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
this.pageService = pageService; this.pageService = pageService;
this.resourceService = pageService.getResourceService(); this.resourceService = pageService.getResourceService();
this.restService = this.resourceService.getRestService(); this.restService = this.resourceService.getRestService();
this.i18nSupport = this.resourceService.getI18nSupport(); this.i18nSupport = this.resourceService.getI18nSupport();
this.widgetFactory = pageService.getWidgetFactory(); this.sebClientLogDetailsPopup = sebClientLogDetailsPopup;
this.pageSize = pageSize; this.pageSize = pageSize;
this.examFilter = new TableFilterAttribute( this.examFilter = new TableFilterAttribute(
@ -220,162 +166,34 @@ public class SebClientLogs implements TemplateComposer {
.withDefaultAction(t -> actionBuilder .withDefaultAction(t -> actionBuilder
.newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS) .newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS)
.withExec(action -> this.showDetails(action, t.getSingleSelectedROWData())) .withExec(action -> sebClientLogDetailsPopup.showDetails(action, t.getSingleSelectedROWData()))
.noEventPropagation() .noEventPropagation()
.create()) .create())
.withSelectionListener(this.pageService.getSelectionPublisher(
pageContext,
ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS))
.compose(pageContext.copyOf(content)); .compose(pageContext.copyOf(content));
actionBuilder actionBuilder
.newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS) .newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS)
.withSelect( .withSelect(
table::getSelection, table::getSelection,
action -> this.showDetails(action, table.getSingleSelectedROWData()), action -> sebClientLogDetailsPopup.showDetails(action, table.getSingleSelectedROWData()),
EMPTY_SELECTION_TEXT) EMPTY_SELECTION_TEXT)
.noEventPropagation() .noEventPropagation()
.publishIf(table::hasAnyContent); .publishIf(table::hasAnyContent, false);
} }
private PageAction showDetails(final PageAction action, final ExtendedClientEvent clientEvent) {
action.getSingleSelection();
final ModalInputDialog<Void> dialog = new ModalInputDialog<>(
action.pageContext().getParent().getShell(),
this.widgetFactory);
dialog.setDialogWidth(600);
dialog.open(
DETAILS_TITLE_TEXT_KEY,
action.pageContext(),
pc -> createDetailsForm(clientEvent, pc));
return action;
}
private void createDetailsForm(final ExtendedClientEvent clientEvent, final PageContext pc) {
final Composite parent = pc.getParent();
final Composite content = this.widgetFactory.createPopupScrollComposite(parent);
// Event Details Title
this.widgetFactory.labelLocalized(
content,
CustomVariant.TEXT_H3,
DETAILS_EVENT_TILE_TEXT_KEY);
this.pageService.formBuilder(pc.copyOf(content))
.withDefaultSpanInput(6)
.withEmptyCellSeparation(false)
.readonly(true)
.addField(FormBuilder.text(
Domain.CLIENT_EVENT.TYPE_NAME,
FORM_TYPE_TEXT_KEY,
this.resourceService.getEventTypeName(clientEvent)))
.addField(FormBuilder.text(
Domain.CLIENT_EVENT.ATTR_CLIENT_TIME,
FORM_CLIENTTIME_TEXT_KEY,
this.i18nSupport.formatDisplayDateTime(clientEvent.clientTime) + " " +
this.i18nSupport.getUsersTimeZoneTitleSuffix()))
.addField(FormBuilder.text(
Domain.CLIENT_EVENT.ATTR_SERVER_TIME,
FORM_SERVERTIME_TEXT_KEY,
this.i18nSupport.formatDisplayDateTime(clientEvent.serverTime) + " " +
this.i18nSupport.getUsersTimeZoneTitleSuffix()))
.addField(FormBuilder.text(
Domain.CLIENT_EVENT.ATTR_NUMERIC_VALUE,
FORM_VALUE_TEXT_KEY,
(clientEvent.numValue != null)
? String.valueOf(clientEvent.numValue)
: Constants.EMPTY_NOTE))
.addField(FormBuilder.text(
Domain.CLIENT_EVENT.ATTR_TEXT,
FORM_MESSAGE_TEXT_KEY,
clientEvent.text)
.asArea())
.build();
// SEB Client Connection Title
this.widgetFactory.labelLocalized(
content,
CustomVariant.TEXT_H3,
DETAILS_CONNECTION_TILE_TEXT_KEY);
final ClientConnection connection = this.restService.getBuilder(GetClientConnection.class)
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(clientEvent.connectionId))
.call()
.get(
error -> log.error("Failed to get ClientConnection for id {}", clientEvent.connectionId, error),
() -> ClientConnection.EMPTY_CLIENT_CONNECTION);
this.pageService.formBuilder(pc.copyOf(content))
.withDefaultSpanInput(6)
.withEmptyCellSeparation(false)
.readonly(true)
.addField(FormBuilder.text(
Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID,
FORM_SESSION_ID_TEXT_KEY,
connection.userSessionId))
.addField(FormBuilder.text(
Domain.CLIENT_CONNECTION.ATTR_CLIENT_ADDRESS,
FORM_ADDRESS_TEXT_KEY,
connection.clientAddress))
.addField(FormBuilder.text(
Domain.CLIENT_CONNECTION.ATTR_CONNECTION_TOKEN,
FORM_TOKEN_TEXT_KEY,
connection.connectionToken))
.addField(FormBuilder.text(
Domain.CLIENT_CONNECTION.ATTR_STATUS,
FORM_STATUS_TEXT_KEY,
this.resourceService.localizedClientConnectionStatusName(connection.status)))
.build();
// Exam Details Title
this.widgetFactory.labelLocalized(
content,
CustomVariant.TEXT_H3,
DETAILS_EXAM_TILE_TEXT_KEY);
final Exam exam = this.restService.getBuilder(GetExam.class)
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(clientEvent.examId))
.call()
.get(
error -> log.error("Failed to get Exam for id {}", clientEvent.examId, error),
() -> Exam.EMPTY_EXAM);
this.pageService.formBuilder(pc.copyOf(content))
.withDefaultSpanInput(6)
.withEmptyCellSeparation(false)
.readonly(true)
.addField(FormBuilder.text(
QuizData.QUIZ_ATTR_NAME,
FORM_EXAM_NAME_TEXT_KEY,
exam.name))
.addField(FormBuilder.text(
QuizData.QUIZ_ATTR_DESCRIPTION,
FORM_DESC_TEXT_KEY,
exam.description))
.addField(FormBuilder.text(
Domain.EXAM.ATTR_TYPE,
FORM_EXAM_TYPE_TEXT_KEY,
this.resourceService.localizedExamTypeName(exam)))
.addField(FormBuilder.text(
QuizData.QUIZ_ATTR_START_TIME,
FORM_START_TIME_TEXT_KEY,
this.i18nSupport.formatDisplayDateWithTimeZone(exam.startTime)))
.addField(FormBuilder.text(
QuizData.QUIZ_ATTR_END_TIME,
FORM_END_TIME_TEXT_KEY,
this.i18nSupport.formatDisplayDateWithTimeZone(exam.endTime)))
.build();
}
private Function<ExtendedClientEvent, String> examNameFunction() { private Function<ExtendedClientEvent, String> examNameFunction() {
final Map<Long, String> examNameMapping = this.resourceService.getExamNameMapping(); final Map<Long, String> examNameMapping = this.resourceService.getExamNameMapping();
return event -> examNameMapping.get(event.examId); return event -> examNameMapping.get(event.examId);
} }
private final String getEventTime(final ExtendedClientEvent event) { private String getEventTime(final ExtendedClientEvent event) {
if (event == null || event.serverTime == null) { if (event == null || event.serverTime == null) {
return Constants.EMPTY_NOTE; return Constants.EMPTY_NOTE;
} }

View file

@ -90,7 +90,7 @@ final class SebExamConfigCreationPopup {
}; };
} }
private static final boolean doCreate( private static boolean doCreate(
final PageService pageService, final PageService pageService,
final PageContext pageContext, final PageContext pageContext,
final boolean copyAsTemplate, final boolean copyAsTemplate,

View file

@ -183,6 +183,11 @@ public enum ActionDefinition {
ImageIcon.SAVE, ImageIcon.SAVE,
PageStateDefinitionImpl.LMS_SETUP_VIEW, PageStateDefinitionImpl.LMS_SETUP_VIEW,
ActionCategory.FORM), ActionCategory.FORM),
LMS_SETUP_SAVE_AND_ACTIVATE(
new LocTextKey("sebserver.form.action.save.activate"),
ImageIcon.ACTIVE,
PageStateDefinitionImpl.LMS_SETUP_VIEW,
ActionCategory.FORM),
LMS_SETUP_ACTIVATE( LMS_SETUP_ACTIVATE(
new LocTextKey("sebserver.lmssetup.action.activate"), new LocTextKey("sebserver.lmssetup.action.activate"),
ImageIcon.TOGGLE_OFF, ImageIcon.TOGGLE_OFF,
@ -193,6 +198,11 @@ public enum ActionDefinition {
ImageIcon.TOGGLE_ON, ImageIcon.TOGGLE_ON,
PageStateDefinitionImpl.LMS_SETUP_VIEW, PageStateDefinitionImpl.LMS_SETUP_VIEW,
ActionCategory.FORM), ActionCategory.FORM),
LMS_SETUP_TOGGLE_ACTIVITY(
new LocTextKey("sebserver.overall.action.toggle-activity"),
ImageIcon.SWITCH,
PageStateDefinitionImpl.LMS_SETUP_LIST,
ActionCategory.LMS_SETUP_LIST),
QUIZ_DISCOVERY_VIEW_LIST( QUIZ_DISCOVERY_VIEW_LIST(
new LocTextKey("sebserver.quizdiscovery.action.list"), new LocTextKey("sebserver.quizdiscovery.action.list"),
@ -593,7 +603,7 @@ public enum ActionDefinition {
ImageIcon.SEND_QUIT, ImageIcon.SEND_QUIT,
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
ActionCategory.FORM), ActionCategory.FORM),
MONITOR_EXAM_FROM_DETAILS( MONITOR_EXAM_BACK_TO_OVERVIEW(
new LocTextKey("sebserver.monitoring.exam.action.detail.view"), new LocTextKey("sebserver.monitoring.exam.action.detail.view"),
ImageIcon.SHOW, ImageIcon.SHOW,
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,

View file

@ -132,7 +132,7 @@ public class FormHandle<T extends Entity> {
return resultAction; return resultAction;
}) })
.onError(this::handleError) .onError(this::handleError)
.getOrThrow(error -> new FormPostException(error)); .getOrThrow(FormPostException::new);
} }
public boolean handleError(final Exception error) { public boolean handleError(final Exception error) {

View file

@ -36,7 +36,7 @@ public final class TextFieldBuilder extends FieldBuilder<String> {
Consumer<String> numberCheck = null; Consumer<String> numberCheck = null;
boolean isArea = false; boolean isArea = false;
int areaMinHeight = WidgetFactory.TEXT_AREA_INPUT_MIN_HEIGHT; int areaMinHeight = WidgetFactory.TEXT_AREA_INPUT_MIN_HEIGHT;
boolean isColorbox = false; boolean isColorBox = false;
boolean isHTML = false; boolean isHTML = false;
TextFieldBuilder(final String name, final LocTextKey label, final String value) { TextFieldBuilder(final String name, final LocTextKey label, final String value) {
@ -80,8 +80,8 @@ public final class TextFieldBuilder extends FieldBuilder<String> {
return this; return this;
} }
public TextFieldBuilder asColorbox() { public TextFieldBuilder asColorBox() {
this.isColorbox = true; this.isColorBox = true;
return this; return this;
} }
@ -115,7 +115,7 @@ public final class TextFieldBuilder extends FieldBuilder<String> {
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, true); final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, true);
if (this.isArea) { if (this.isArea) {
gridData.minimumHeight = this.areaMinHeight; gridData.minimumHeight = this.areaMinHeight;
} else if (this.isColorbox) { } else if (this.isColorBox) {
gridData.minimumHeight = WidgetFactory.TEXT_INPUT_MIN_HEIGHT; gridData.minimumHeight = WidgetFactory.TEXT_INPUT_MIN_HEIGHT;
textInput.setData(RWT.CUSTOM_VARIANT, "colorbox"); textInput.setData(RWT.CUSTOM_VARIANT, "colorbox");
} }

View file

@ -164,8 +164,7 @@ public class ResourceService {
} }
public List<Tuple<String>> lmsTypeResources() { public List<Tuple<String>> lmsTypeResources() {
return Arrays.asList(LmsType.values()) return Arrays.stream(LmsType.values())
.stream()
.filter(lmsType -> lmsType != LmsType.MOCKUP || this.mock_lms_enabled) .filter(lmsType -> lmsType != LmsType.MOCKUP || this.mock_lms_enabled)
.map(lmsType -> new Tuple<>( .map(lmsType -> new Tuple<>(
lmsType.name(), lmsType.name(),
@ -175,8 +174,7 @@ public class ResourceService {
} }
public List<Tuple<String>> clientEventTypeResources() { public List<Tuple<String>> clientEventTypeResources() {
return Arrays.asList(EventType.values()) return Arrays.stream(EventType.values())
.stream()
.filter(eventType -> !CLIENT_EVENT_TYPE_EXCLUDE_MAP.contains(eventType)) .filter(eventType -> !CLIENT_EVENT_TYPE_EXCLUDE_MAP.contains(eventType))
.map(eventType -> new Tuple<>( .map(eventType -> new Tuple<>(
eventType.name(), eventType.name(),
@ -200,8 +198,7 @@ public class ResourceService {
} }
public List<Tuple<String>> indicatorTypeResources() { public List<Tuple<String>> indicatorTypeResources() {
return Arrays.asList(IndicatorType.values()) return Arrays.stream(IndicatorType.values())
.stream()
.map(type -> new Tuple3<>( .map(type -> new Tuple3<>(
type.name(), type.name(),
this.i18nSupport.getText(EXAM_INDICATOR_TYPE_PREFIX + type.name(), type.name()), this.i18nSupport.getText(EXAM_INDICATOR_TYPE_PREFIX + type.name(), type.name()),
@ -295,8 +292,7 @@ public class ResourceService {
} }
public List<Tuple<String>> entityTypeResources() { public List<Tuple<String>> entityTypeResources() {
return Arrays.asList(EntityType.values()) return Arrays.stream(EntityType.values())
.stream()
.filter(type -> !ENTITY_TYPE_EXCLUDE_MAP.contains(type)) .filter(type -> !ENTITY_TYPE_EXCLUDE_MAP.contains(type))
.map(type -> new Tuple<>(type.name(), getEntityTypeName(type))) .map(type -> new Tuple<>(type.name(), getEntityTypeName(type)))
.sorted(RESOURCE_COMPARATOR) .sorted(RESOURCE_COMPARATOR)
@ -319,8 +315,7 @@ public class ResourceService {
} }
public List<Tuple<String>> userActivityTypeResources() { public List<Tuple<String>> userActivityTypeResources() {
return Arrays.asList(UserLogActivityType.values()) return Arrays.stream(UserLogActivityType.values())
.stream()
.map(type -> new Tuple<>(type.name(), getUserActivityTypeName(type))) .map(type -> new Tuple<>(type.name(), getUserActivityTypeName(type)))
.sorted(RESOURCE_COMPARATOR) .sorted(RESOURCE_COMPARATOR)
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -363,16 +358,13 @@ public class ResourceService {
} }
public List<Tuple<String>> examTypeResources() { public List<Tuple<String>> examTypeResources() {
return Arrays.asList(ExamType.values()) return Arrays.stream(ExamType.values())
.stream()
.filter(type -> type != ExamType.UNDEFINED)
.map(type -> new Tuple3<>( .map(type -> new Tuple3<>(
type.name(), type.name(),
this.i18nSupport.getText(EXAM_TYPE_PREFIX + type.name()), this.i18nSupport.getText(EXAM_TYPE_PREFIX + type.name()),
Utils.formatLineBreaks(this.i18nSupport.getText( Utils.formatLineBreaks(this.i18nSupport.getText(
this.i18nSupport.getText(EXAM_TYPE_PREFIX + type.name()) + Constants.TOOLTIP_TEXT_KEY_SUFFIX, EXAM_INDICATOR_TYPE_PREFIX + type.name() + Constants.TOOLTIP_TEXT_KEY_SUFFIX,
StringUtils.EMPTY)) StringUtils.EMPTY))))
))
.sorted(RESOURCE_COMPARATOR) .sorted(RESOURCE_COMPARATOR)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -382,8 +374,7 @@ public class ResourceService {
} }
public List<Tuple<String>> examConfigStatusResources(final boolean isAttachedToExam) { public List<Tuple<String>> examConfigStatusResources(final boolean isAttachedToExam) {
return Arrays.asList(ConfigurationStatus.values()) return Arrays.stream(ConfigurationStatus.values())
.stream()
.filter(status -> { .filter(status -> {
if (isAttachedToExam) { if (isAttachedToExam) {
return status != ConfigurationStatus.READY_TO_USE; return status != ConfigurationStatus.READY_TO_USE;
@ -579,8 +570,7 @@ public class ResourceService {
} }
public List<Tuple<String>> getAttributeTypeResources() { public List<Tuple<String>> getAttributeTypeResources() {
return Arrays.asList(AttributeType.values()) return Arrays.stream(AttributeType.values())
.stream()
.filter(type -> !ATTRIBUTE_TYPES_NOT_DISPLAYED.contains(type)) .filter(type -> !ATTRIBUTE_TYPES_NOT_DISPLAYED.contains(type))
.map(type -> new Tuple<>(getAttributeTypeFilterName(type), getAttributeTypeName(type))) .map(type -> new Tuple<>(getAttributeTypeFilterName(type), getAttributeTypeName(type)))
.sorted(RESOURCE_COMPARATOR) .sorted(RESOURCE_COMPARATOR)
@ -629,21 +619,25 @@ public class ResourceService {
} }
public List<Tuple<String>> sebRestrictionWhiteListResources() { public List<Tuple<String>> sebRestrictionWhiteListResources() {
return Arrays.asList(WhiteListPath.values()) return Arrays.stream(WhiteListPath.values())
.stream() .map(type -> new Tuple3<>(
.map(type -> new Tuple<>(
type.key, type.key,
this.i18nSupport.getText(SEB_RESTRICTION_WHITE_LIST_PREFIX + type.name(), type.key))) this.i18nSupport.getText(SEB_RESTRICTION_WHITE_LIST_PREFIX + type.name(), type.key),
Utils.formatLineBreaks(this.i18nSupport.getText(
SEB_RESTRICTION_WHITE_LIST_PREFIX + type.name() + Constants.TOOLTIP_TEXT_KEY_SUFFIX,
StringUtils.EMPTY))))
.sorted(RESOURCE_COMPARATOR) .sorted(RESOURCE_COMPARATOR)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public List<Tuple<String>> sebRestrictionPermissionResources() { public List<Tuple<String>> sebRestrictionPermissionResources() {
return Arrays.asList(PermissionComponent.values()) return Arrays.stream(PermissionComponent.values())
.stream() .map(type -> new Tuple3<>(
.map(type -> new Tuple<>(
type.key, type.key,
this.i18nSupport.getText(SEB_RESTRICTION_PERMISSIONS_PREFIX + type.name(), type.key))) this.i18nSupport.getText(SEB_RESTRICTION_PERMISSIONS_PREFIX + type.name(), type.key),
Utils.formatLineBreaks(this.i18nSupport.getText(
SEB_RESTRICTION_PERMISSIONS_PREFIX + type.name() + Constants.TOOLTIP_TEXT_KEY_SUFFIX,
StringUtils.EMPTY))))
.sorted(RESOURCE_COMPARATOR) .sorted(RESOURCE_COMPARATOR)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }

View file

@ -118,10 +118,25 @@ public interface PageService {
* *
* @param table the entity table * @param table the entity table
* @param noSelectionText LocTextKey for missing selection message * @param noSelectionText LocTextKey for missing selection message
* @param testBeforeActivation a function to test before activation. This function shall throw an error if test fails.
* My be null if no specific test is needed before activation
* @return page action execution function for switching the activity */ * @return page action execution function for switching the activity */
<T extends Entity & Activatable> Function<PageAction, PageAction> activationToggleActionFunction( <T extends Entity & Activatable> Function<PageAction, PageAction> activationToggleActionFunction(
EntityTable<T> table, EntityTable<T> table,
LocTextKey noSelectionText); LocTextKey noSelectionText,
Function<PageAction, PageAction> testBeforeActivation);
/** Get a page action execution function for switching the activity of currently selected
* entities from a given entity-table.
*
* @param table the entity table
* @param noSelectionText LocTextKey for missing selection message
* @return page action execution function for switching the activity */
default <T extends Entity & Activatable> Function<PageAction, PageAction> activationToggleActionFunction(
EntityTable<T> table,
LocTextKey noSelectionText) {
return this.activationToggleActionFunction(table, noSelectionText, null);
}
/** Get a message supplier to notify deactivation dependencies to the user for all given entities /** Get a message supplier to notify deactivation dependencies to the user for all given entities
* *
@ -142,14 +157,12 @@ public interface PageService {
* @param table the entity table * @param table the entity table
* @return a message supplier to notify deactivation dependencies to the user */ * @return a message supplier to notify deactivation dependencies to the user */
default <T extends Entity & Activatable> Supplier<LocTextKey> confirmDeactivation(final EntityTable<T> table) { default <T extends Entity & Activatable> Supplier<LocTextKey> confirmDeactivation(final EntityTable<T> table) {
return () -> { return () -> confirmDeactivation(table
return confirmDeactivation(table
.getSelectedROWData() .getSelectedROWData()
.stream() .stream()
.filter(e -> e.isActive()) // NOTE: Activatable::isActive leads to an error here!? .filter(Activatable::isActive) // NOTE: Activatable::isActive leads to an error here!?
.collect(Collectors.toSet())) .collect(Collectors.toSet()))
.get(); .get();
};
} }
/** Use this to get an table selection action publisher that processes the action /** Use this to get an table selection action publisher that processes the action
@ -158,15 +171,13 @@ public interface PageService {
* @param pageContext the current PageContext * @param pageContext the current PageContext
* @param actionDefinitions list of action definitions that activity should be toggled on table selection * @param actionDefinitions list of action definitions that activity should be toggled on table selection
* @return the selection publisher that handles this defines action activation on table selection */ * @return the selection publisher that handles this defines action activation on table selection */
default <T extends Entity> Consumer<Set<T>> getSelectionPublisher( default <T> Consumer<Set<T>> getSelectionPublisher(
final PageContext pageContext, final PageContext pageContext,
final ActionDefinition... actionDefinitions) { final ActionDefinition... actionDefinitions) {
return rows -> { return rows -> firePageEvent(
firePageEvent(
new ActionActivationEvent(!rows.isEmpty(), actionDefinitions), new ActionActivationEvent(!rows.isEmpty(), actionDefinitions),
pageContext); pageContext);
};
} }
/** Use this to get an table selection action publisher that processes the action /** Use this to get an table selection action publisher that processes the action
@ -268,7 +279,7 @@ public interface PageService {
/** Get an new TableBuilder for specified page based RestCall. /** Get an new TableBuilder for specified page based RestCall.
* *
* @param The name of the table to build * @param name The name of the table to build
* @param apiCall the SEB Server API RestCall that feeds the table with data * @param apiCall the SEB Server API RestCall that feeds the table with data
* @param <T> the type of the Entity of the table * @param <T> the type of the Entity of the table
* @return TableBuilder of specified type */ * @return TableBuilder of specified type */
@ -298,7 +309,7 @@ public interface PageService {
void clearState(); void clearState();
/** Key to store the ScrolledComposite update function within Control data map */ /** Key to store the ScrolledComposite update function within Control data map */
static String SCROLLED_COMPOSITE_UPDATE = "SCROLLED_COMPOSITE_UPDATE"; String SCROLLED_COMPOSITE_UPDATE = "SCROLLED_COMPOSITE_UPDATE";
/** Creates a ScrolledComposite with content supplied the given content creation function. /** Creates a ScrolledComposite with content supplied the given content creation function.
* The content creation function is used to create the content Composite as a child of the * The content creation function is used to create the content Composite as a child of the
@ -371,7 +382,7 @@ public interface PageService {
} }
} }
public class PageActionBuilder { class PageActionBuilder {
private final PageService pageService; private final PageService pageService;
private final PageContext originalPageContext; private final PageContext originalPageContext;

View file

@ -17,7 +17,6 @@ import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
@ -215,7 +214,7 @@ public class PageServiceImpl implements PageService {
} }
try { try {
final int dependencies = entities.stream() final int dependencies = (int) entities.stream()
.flatMap(entity -> { .flatMap(entity -> {
final RestCall<Set<EntityKey>>.RestCallBuilder builder = final RestCall<Set<EntityKey>>.RestCallBuilder builder =
restService.<Set<EntityKey>>getBuilder( restService.<Set<EntityKey>>getBuilder(
@ -227,9 +226,7 @@ public class PageServiceImpl implements PageService {
.withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.DEACTIVATE.name()) .withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.DEACTIVATE.name())
.call() .call()
.getOrThrow().stream(); .getOrThrow().stream();
}) }).count();
.collect(Collectors.toList())
.size();
if (dependencies > 0) { if (dependencies > 0) {
return new LocTextKey(CONFIRM_DEACTIVATION_KEY, String.valueOf(dependencies)); return new LocTextKey(CONFIRM_DEACTIVATION_KEY, String.valueOf(dependencies));
} else { } else {
@ -237,7 +234,7 @@ public class PageServiceImpl implements PageService {
} }
} catch (final Exception e) { } catch (final Exception e) {
log.warn("Failed to get dependencyies. Error: {}", e.getMessage()); log.warn("Failed to get dependencies. Error: {}", e.getMessage());
return new LocTextKey(CONFIRM_DEACTIVATION_KEY, ""); return new LocTextKey(CONFIRM_DEACTIVATION_KEY, "");
} }
}; };
@ -246,7 +243,8 @@ public class PageServiceImpl implements PageService {
@Override @Override
public <T extends Entity & Activatable> Function<PageAction, PageAction> activationToggleActionFunction( public <T extends Entity & Activatable> Function<PageAction, PageAction> activationToggleActionFunction(
final EntityTable<T> table, final EntityTable<T> table,
final LocTextKey noSelectionText) { final LocTextKey noSelectionText,
Function<PageAction, PageAction> testBeforeActivation) {
return action -> { return action -> {
final Set<T> selectedROWData = table.getSelectedROWData(); final Set<T> selectedROWData = table.getSelectedROWData();
@ -259,13 +257,29 @@ public class PageServiceImpl implements PageService {
final Collection<Exception> errors = new ArrayList<>(); final Collection<Exception> errors = new ArrayList<>();
for (final T entity : selectedROWData) { for (final T entity : selectedROWData) {
if (entity.isActive()) {
restService.getBuilder(entityType, CallType.ACTIVATION_DEACTIVATE) if (!entity.isActive()) {
.withURIVariable(API.PARAM_MODEL_ID, entity.getModelId()) RestCall<T>.RestCallBuilder restCallBuilder = restService.<T>getBuilder(
entityType,
CallType.ACTIVATION_ACTIVATE)
.withURIVariable(API.PARAM_MODEL_ID, entity.getModelId());
if (testBeforeActivation != null) {
try {
action.withEntityKey(entity.getEntityKey());
testBeforeActivation.apply(action);
restCallBuilder
.call() .call()
.onError(errors::add); .onError(errors::add);
} catch (Exception e) {
errors.add(e);
}
} else { } else {
restService.getBuilder(entityType, CallType.ACTIVATION_ACTIVATE) restCallBuilder
.call()
.onError(errors::add);
}
} else {
restService.<T>getBuilder(entityType, CallType.ACTIVATION_DEACTIVATE)
.withURIVariable(API.PARAM_MODEL_ID, entity.getModelId()) .withURIVariable(API.PARAM_MODEL_ID, entity.getModelId())
.call() .call()
.onError(errors::add); .onError(errors::add);
@ -375,9 +389,7 @@ public class PageServiceImpl implements PageService {
@Override @Override
public int compare(final PageEventListener<?> o1, final PageEventListener<?> o2) { public int compare(final PageEventListener<?> o1, final PageEventListener<?> o2) {
final int x = o1.priority(); return Integer.compare(o1.priority(), o2.priority());
final int y = o2.priority();
return (x < y) ? -1 : ((x == y) ? 0 : 1);
} }
} }

View file

@ -40,13 +40,13 @@ public class ClientConnectionDetails {
private static final Logger log = LoggerFactory.getLogger(ClientConnectionDetails.class); private static final Logger log = LoggerFactory.getLogger(ClientConnectionDetails.class);
private final static LocTextKey EXAM_NAME_TEXT_KEY = private final static LocTextKey EXAM_NAME_TEXT_KEY =
new LocTextKey("sebserver.monitoring.connection.list.column.examname"); new LocTextKey("sebserver.monitoring.connection.form.exam");
private final static LocTextKey CONNECTION_ID_TEXT_KEY = private final static LocTextKey CONNECTION_ID_TEXT_KEY =
new LocTextKey("sebserver.monitoring.connection.list.column.id"); new LocTextKey("sebserver.monitoring.connection.form.id");
private final static LocTextKey CONNECTION_ADDRESS_TEXT_KEY = private final static LocTextKey CONNECTION_ADDRESS_TEXT_KEY =
new LocTextKey("sebserver.monitoring.connection.list.column.address"); new LocTextKey("sebserver.monitoring.connection.form.address");
private final static LocTextKey CONNECTION_STATUS_TEXT_KEY = private final static LocTextKey CONNECTION_STATUS_TEXT_KEY =
new LocTextKey("sebserver.monitoring.connection.list.column.status"); new LocTextKey("sebserver.monitoring.connection.form.status");
private static final int NUMBER_OF_NONE_INDICATOR_ROWS = 3; private static final int NUMBER_OF_NONE_INDICATOR_ROWS = 3;
@ -100,26 +100,23 @@ public class ClientConnectionDetails {
Domain.CLIENT_CONNECTION.ATTR_STATUS, Domain.CLIENT_CONNECTION.ATTR_STATUS,
CONNECTION_STATUS_TEXT_KEY, CONNECTION_STATUS_TEXT_KEY,
Constants.EMPTY_NOTE) Constants.EMPTY_NOTE)
.asColorbox()) .asColorBox())
.addEmptyCell(); .addEmptyCell();
this.indicatorMapping this.indicatorMapping
.values() .values()
.stream() .forEach(indData -> formBuilder.addField(FormBuilder.text(
.forEach(indData -> {
formBuilder.addField(FormBuilder.text(
indData.indicator.name, indData.indicator.name,
new LocTextKey(indData.indicator.name), new LocTextKey(indData.indicator.name),
Constants.EMPTY_NOTE) Constants.EMPTY_NOTE)
.asColorbox() .asColorBox()
.withDefaultLabel(indData.indicator.name)) .withDefaultLabel(indData.indicator.name))
.addEmptyCell(); .addEmptyCell());
});
this.formhandle = formBuilder.build(); this.formhandle = formBuilder.build();
} }
public void updateData(final ServerPushContext context) { public void updateData() {
final ClientConnectionData connectionData = this.restCallBuilder final ClientConnectionData connectionData = this.restCallBuilder
.call() .call()
.get(error -> { .get(error -> {
@ -135,7 +132,7 @@ public class ClientConnectionDetails {
this.connectionData = connectionData; this.connectionData = connectionData;
} }
public void updateGUI(final ServerPushContext context) { public void updateGUI() {
if (this.connectionData == null) { if (this.connectionData == null) {
return; return;
} }
@ -162,7 +159,6 @@ public class ClientConnectionDetails {
// update indicators // update indicators
this.connectionData.getIndicatorValues() this.connectionData.getIndicatorValues()
.stream()
.forEach(indValue -> { .forEach(indValue -> {
final IndicatorData indData = this.indicatorMapping.get(indValue.getType()); final IndicatorData indData = this.indicatorMapping.get(indValue.getType());
final double value = indValue.getValue(); final double value = indValue.getValue();

View file

@ -21,10 +21,12 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.bcel.Const;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.Rectangle;
@ -70,13 +72,19 @@ public final class ClientConnectionTable {
private static final String USER_SESSION_STATUS_FILTER_ATTRIBUTE = "USER_SESSION_STATUS_FILTER_ATTRIBUTE"; private static final String USER_SESSION_STATUS_FILTER_ATTRIBUTE = "USER_SESSION_STATUS_FILTER_ATTRIBUTE";
private static final String INDICATOR_NAME_TEXT_KEY_PREFIX = private static final String INDICATOR_NAME_TEXT_KEY_PREFIX =
"sebserver.monitoring.connection.list.column.indicator."; "sebserver.exam.indicator.type.description.";
private final static LocTextKey CONNECTION_ID_TEXT_KEY = private final static LocTextKey CONNECTION_ID_TEXT_KEY =
new LocTextKey("sebserver.monitoring.connection.list.column.id"); new LocTextKey("sebserver.monitoring.connection.list.column.id");
private final static LocTextKey CONNECTION_ID_TOOLTIP_TEXT_KEY =
new LocTextKey("sebserver.monitoring.connection.list.column.id" + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
private final static LocTextKey CONNECTION_ADDRESS_TEXT_KEY = private final static LocTextKey CONNECTION_ADDRESS_TEXT_KEY =
new LocTextKey("sebserver.monitoring.connection.list.column.address"); new LocTextKey("sebserver.monitoring.connection.list.column.address");
private final static LocTextKey CONNECTION_ADDRESS_TOOLTIP_TEXT_KEY =
new LocTextKey("sebserver.monitoring.connection.list.column.address" + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
private final static LocTextKey CONNECTION_STATUS_TEXT_KEY = private final static LocTextKey CONNECTION_STATUS_TEXT_KEY =
new LocTextKey("sebserver.monitoring.connection.list.column.status"); new LocTextKey("sebserver.monitoring.connection.list.column.status");
private final static LocTextKey CONNECTION_STATUS_TOOLTIP_TEXT_KEY =
new LocTextKey("sebserver.monitoring.connection.list.column.status" + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
private final WidgetFactory widgetFactory; private final WidgetFactory widgetFactory;
private final ResourceService resourceService; private final ResourceService resourceService;
@ -88,6 +96,7 @@ public final class ClientConnectionTable {
private final EnumSet<ConnectionStatus> statusFilter; private final EnumSet<ConnectionStatus> statusFilter;
private String statusFilterParam = ""; private String statusFilterParam = "";
private boolean statusFilterChanged = false; private boolean statusFilterChanged = false;
private Consumer<Set<EntityKey>> selectionListener;
private int tableWidth; private int tableWidth;
private boolean needsSort = false; private boolean needsSort = false;
@ -136,21 +145,27 @@ public final class ClientConnectionTable {
this.table.setHeaderVisible(true); this.table.setHeaderVisible(true);
this.table.setLinesVisible(true); this.table.setLinesVisible(true);
this.table.addListener(SWT.Selection, event -> this.notifySelectionChange());
this.widgetFactory.tableColumnLocalized( this.widgetFactory.tableColumnLocalized(
this.table, this.table,
CONNECTION_ID_TEXT_KEY); CONNECTION_ID_TEXT_KEY,
CONNECTION_ID_TOOLTIP_TEXT_KEY);
this.widgetFactory.tableColumnLocalized( this.widgetFactory.tableColumnLocalized(
this.table, this.table,
CONNECTION_ADDRESS_TEXT_KEY); CONNECTION_ADDRESS_TEXT_KEY,
CONNECTION_ADDRESS_TOOLTIP_TEXT_KEY);
this.widgetFactory.tableColumnLocalized( this.widgetFactory.tableColumnLocalized(
this.table, this.table,
CONNECTION_STATUS_TEXT_KEY); CONNECTION_STATUS_TEXT_KEY,
CONNECTION_STATUS_TOOLTIP_TEXT_KEY);
for (final Indicator indDef : indicators) { for (final Indicator indDef : indicators) {
final TableColumn tc = new TableColumn(this.table, SWT.NONE); TableColumn tableColumn = widgetFactory.tableColumnLocalized(
final String indicatorName = this.widgetFactory.getI18nSupport().getText( this.table,
INDICATOR_NAME_TEXT_KEY_PREFIX + indDef.name, new LocTextKey(INDICATOR_NAME_TEXT_KEY_PREFIX + indDef.name),
indDef.name); new LocTextKey(INDICATOR_NAME_TEXT_KEY_PREFIX + indDef.type.name)
tc.setText(indicatorName); );
tableColumn.setText(indDef.name);
} }
this.tableMapping = new LinkedHashMap<>(); this.tableMapping = new LinkedHashMap<>();
@ -184,7 +199,7 @@ public final class ClientConnectionTable {
saveStatusFilter(); saveStatusFilter();
} }
public void withDefaultAction(final PageAction pageAction, final PageService pageService) { public ClientConnectionTable withDefaultAction(final PageAction pageAction, final PageService pageService) {
this.table.addListener(SWT.MouseDoubleClick, event -> { this.table.addListener(SWT.MouseDoubleClick, event -> {
final Tuple<String> selection = getSingleSelection(); final Tuple<String> selection = getSingleSelection();
if (selection == null) { if (selection == null) {
@ -200,7 +215,7 @@ public final class ClientConnectionTable {
selection._2); selection._2);
pageService.executePageAction(copyOfPageAction); pageService.executePageAction(copyOfPageAction);
}); });
return this;
} }
public Set<String> getConnectionTokens( public Set<String> getConnectionTokens(
@ -237,9 +252,15 @@ public final class ClientConnectionTable {
public void removeSelection() { public void removeSelection() {
if (this.table != null) { if (this.table != null) {
this.table.deselectAll(); this.table.deselectAll();
this.notifySelectionChange();
} }
} }
public ClientConnectionTable withSelectionListener(Consumer<Set<EntityKey>> selectionListener) {
this.selectionListener = selectionListener;
return this;
}
public Set<EntityKey> getSelection() { public Set<EntityKey> getSelection() {
final int[] selectionIndices = this.table.getSelectionIndices(); final int[] selectionIndices = this.table.getSelectionIndices();
if (selectionIndices == null || selectionIndices.length < 1) { if (selectionIndices == null || selectionIndices.length < 1) {
@ -282,11 +303,10 @@ public final class ClientConnectionTable {
log.error("Error poll connection data: ", error); log.error("Error poll connection data: ", error);
return Collections.emptyList(); return Collections.emptyList();
}) })
.stream()
.forEach(data -> { .forEach(data -> {
final UpdatableTableItem tableItem = this.tableMapping.computeIfAbsent( final UpdatableTableItem tableItem = this.tableMapping.computeIfAbsent(
data.getConnectionId(), data.getConnectionId(),
connectionId -> new UpdatableTableItem(connectionId)); UpdatableTableItem::new);
tableItem.push(data); tableItem.push(data);
if (this.statusFilterChanged) { if (this.statusFilterChanged) {
this.toDelete.remove(data.getConnectionId()); this.toDelete.remove(data.getConnectionId());
@ -294,7 +314,7 @@ public final class ClientConnectionTable {
}); });
if (this.statusFilterChanged && !this.toDelete.isEmpty()) { if (this.statusFilterChanged && !this.toDelete.isEmpty()) {
this.toDelete.stream().forEach(id -> { this.toDelete.forEach(id -> {
final UpdatableTableItem item = this.tableMapping.remove(id); final UpdatableTableItem item = this.tableMapping.remove(id);
final List<Long> list = this.sessionIds.get(item.connectionData.clientConnection.userSessionId); final List<Long> list = this.sessionIds.get(item.connectionData.clientConnection.userSessionId);
if (list != null) { if (list != null) {
@ -399,6 +419,14 @@ public final class ClientConnectionTable {
} }
} }
private void notifySelectionChange() {
if (this.selectionListener == null) {
return;
}
this.selectionListener.accept(this.getSelection());
}
private final class UpdatableTableItem implements Comparable<UpdatableTableItem> { private final class UpdatableTableItem implements Comparable<UpdatableTableItem> {
final Long connectionId; final Long connectionId;

View file

@ -104,7 +104,6 @@ public final class MultiSelectionCheckbox extends Composite implements Selection
} }
Arrays.asList(StringUtils.split(keys, Constants.LIST_SEPARATOR)) Arrays.asList(StringUtils.split(keys, Constants.LIST_SEPARATOR))
.stream()
.forEach(key -> { .forEach(key -> {
final Button button = this.checkboxes.get(key); final Button button = this.checkboxes.get(key);
if (button != null) { if (button != null) {
@ -120,7 +119,7 @@ public final class MultiSelectionCheckbox extends Composite implements Selection
this.checkboxes this.checkboxes
.values() .values()
.stream() .stream()
.filter(button -> button.getSelection()) .filter(Button::getSelection)
.map(button -> (String) button.getData(OPTION_VALUE)) .map(button -> (String) button.getData(OPTION_VALUE))
.collect(Collectors.toList()).toArray()); .collect(Collectors.toList()).toArray());
} }
@ -129,7 +128,6 @@ public final class MultiSelectionCheckbox extends Composite implements Selection
public void clear() { public void clear() {
this.checkboxes this.checkboxes
.values() .values()
.stream()
.forEach(button -> button.setSelection(false)); .forEach(button -> button.setSelection(false));
} }

View file

@ -14,6 +14,7 @@ import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import ch.ethz.seb.sebserver.gbl.Constants;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridData;
@ -41,10 +42,18 @@ public final class ThresholdList extends Composite {
private static final int ACTION_COLUMN_WIDTH = 20; private static final int ACTION_COLUMN_WIDTH = 20;
private static final String COLOR_SELECTION_TEXT_KEY = "sebserver.exam.indicator.thresholds.select.color"; private static final String COLOR_SELECTION_TEXT_KEY = "sebserver.exam.indicator.thresholds.select.color";
private static final LocTextKey VALUE_TEXT_KEY = new LocTextKey("sebserver.exam.indicator.thresholds.list.value"); private static final LocTextKey VALUE_TEXT_KEY =
private static final LocTextKey COLOR_TEXT_KEY = new LocTextKey("sebserver.exam.indicator.thresholds.list.color"); new LocTextKey("sebserver.exam.indicator.thresholds.list.value");
private static final LocTextKey ADD_TEXT_KEY = new LocTextKey("sebserver.exam.indicator.thresholds.list.add"); private static final LocTextKey VALUE_TOOLTIP_TEXT_KEY =
private static final LocTextKey REMOVE_TEXT_KEY = new LocTextKey("sebserver.exam.indicator.thresholds.list.remove"); new LocTextKey("sebserver.exam.indicator.thresholds.list.value" + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
private static final LocTextKey COLOR_TEXT_KEY =
new LocTextKey("sebserver.exam.indicator.thresholds.list.color");
private static final LocTextKey COLOR_TOOLTIP_TEXT_KEY =
new LocTextKey("sebserver.exam.indicator.thresholds.list.color" + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
private static final LocTextKey ADD_TEXT_KEY =
new LocTextKey("sebserver.exam.indicator.thresholds.list.add");
private static final LocTextKey REMOVE_TEXT_KEY =
new LocTextKey("sebserver.exam.indicator.thresholds.list.remove");
private final WidgetFactory widgetFactory; private final WidgetFactory widgetFactory;
private final Supplier<IndicatorType> indicatorTypeSupplier; private final Supplier<IndicatorType> indicatorTypeSupplier;
@ -80,14 +89,16 @@ public final class ThresholdList extends Composite {
final Label valueTitle = widgetFactory.labelLocalized( final Label valueTitle = widgetFactory.labelLocalized(
this, this,
CustomVariant.TITLE_LABEL, CustomVariant.TITLE_LABEL,
VALUE_TEXT_KEY); VALUE_TEXT_KEY,
VALUE_TOOLTIP_TEXT_KEY);
this.valueCell = new GridData(SWT.FILL, SWT.CENTER, true, false); this.valueCell = new GridData(SWT.FILL, SWT.CENTER, true, false);
valueTitle.setLayoutData(this.valueCell); valueTitle.setLayoutData(this.valueCell);
final Label colorTitle = widgetFactory.labelLocalized( final Label colorTitle = widgetFactory.labelLocalized(
this, this,
CustomVariant.TITLE_LABEL, CustomVariant.TITLE_LABEL,
COLOR_TEXT_KEY); COLOR_TEXT_KEY,
COLOR_TOOLTIP_TEXT_KEY);
this.colorCell = new GridData(SWT.FILL, SWT.CENTER, true, false); this.colorCell = new GridData(SWT.FILL, SWT.CENTER, true, false);
colorTitle.setLayoutData(this.colorCell); colorTitle.setLayoutData(this.colorCell);
@ -103,9 +114,7 @@ public final class ThresholdList extends Composite {
public void setThresholds(final Collection<Threshold> thresholds) { public void setThresholds(final Collection<Threshold> thresholds) {
clearList(); clearList();
if (thresholds != null) { if (thresholds != null) {
thresholds thresholds.forEach(this::addThreshold);
.stream()
.forEach(this::addThreshold);
} }
} }
@ -122,13 +131,11 @@ public final class ThresholdList extends Composite {
.stream() .stream()
.filter(entry -> entry.getValue() == null || StringUtils.isBlank(entry.getColor())) .filter(entry -> entry.getValue() == null || StringUtils.isBlank(entry.getColor()))
.collect(Collectors.toList()) .collect(Collectors.toList())
.stream() .forEach(this::removeThreshold);
.forEach(entry -> removeThreshold(entry));
} }
private void clearList() { private void clearList() {
this.thresholds.stream() this.thresholds.forEach(Entry::dispose);
.forEach(e -> e.dispose());
this.thresholds.clear(); this.thresholds.clear();
} }

View file

@ -43,9 +43,10 @@ public class MoodleCourseAccess extends CourseAccess {
private static final Logger log = LoggerFactory.getLogger(MoodleCourseAccess.class); private static final Logger log = LoggerFactory.getLogger(MoodleCourseAccess.class);
private static final String MOOLDE_QUIZ_START_URL_PATH = "/mod/quiz/view.php?id="; private static final String MOODLE_QUIZ_START_URL_PATH = "/mod/quiz/view.php?id=";
private static final String MOODLE_COURSE_API_FUNCTION_NAME = "core_course_get_courses"; private static final String MOODLE_COURSE_API_FUNCTION_NAME = "core_course_get_courses";
private static final String MOODLE_QUIZ_API_FUNCTION_NAME = "mod_quiz_get_quizzes_by_courses"; private static final String MOODLE_QUIZ_API_FUNCTION_NAME = "mod_quiz_get_quizzes_by_courses";
private static final String MOODLE_COURSE_API_COURSE_IDS = "courseids";
private final JSONMapper jsonMapper; private final JSONMapper jsonMapper;
private final LmsSetup lmsSetup; private final LmsSetup lmsSetup;
@ -96,20 +97,18 @@ public class MoodleCourseAccess extends CourseAccess {
@Override @Override
protected Supplier<List<QuizData>> allQuizzesSupplier() { protected Supplier<List<QuizData>> allQuizzesSupplier() {
return () -> { return () -> getRestTemplate()
return getRestTemplate()
.map(this::collectAllQuizzes) .map(this::collectAllQuizzes)
.getOrThrow(); .getOrThrow();
};
} }
private ArrayList<QuizData> collectAllQuizzes(final MoodleAPIRestTemplate restTemplate) { private ArrayList<QuizData> collectAllQuizzes(final MoodleAPIRestTemplate restTemplate) {
final String urlPrefix = this.lmsSetup.lmsApiUrl + MOOLDE_QUIZ_START_URL_PATH; final String urlPrefix = this.lmsSetup.lmsApiUrl + MOODLE_QUIZ_START_URL_PATH;
return collectAllCourses( return collectAllCourses(
restTemplate) restTemplate)
.stream() .stream()
.reduce( .reduce(
new ArrayList<QuizData>(), new ArrayList<>(),
(list, courseData) -> { (list, courseData) -> {
list.addAll(quizDataOf( list.addAll(quizDataOf(
this.lmsSetup, this.lmsSetup,
@ -138,7 +137,7 @@ public class MoodleCourseAccess extends CourseAccess {
// then get all quizzes of courses and filter // then get all quizzes of courses and filter
final LinkedMultiValueMap<String, String> attributes = new LinkedMultiValueMap<>(); final LinkedMultiValueMap<String, String> attributes = new LinkedMultiValueMap<>();
attributes.put("courseids", new ArrayList<>(courseData.keySet())); attributes.put(MOODLE_COURSE_API_COURSE_IDS, new ArrayList<>(courseData.keySet()));
final String quizzesJSON = restTemplate.callMoodleAPIFunction( final String quizzesJSON = restTemplate.callMoodleAPIFunction(
MOODLE_QUIZ_API_FUNCTION_NAME, MOODLE_QUIZ_API_FUNCTION_NAME,
@ -149,7 +148,6 @@ public class MoodleCourseAccess extends CourseAccess {
CourseQuizData.class); CourseQuizData.class);
courseQuizData.quizzes courseQuizData.quizzes
.stream()
.forEach(quiz -> { .forEach(quiz -> {
final CourseData course = courseData.get(quiz.course); final CourseData course = courseData.get(quiz.course);
if (course != null) { if (course != null) {
@ -166,24 +164,24 @@ public class MoodleCourseAccess extends CourseAccess {
} }
} }
static Map<String, String> additionalAttrs = new HashMap<>();
private static List<QuizData> quizDataOf( private static List<QuizData> quizDataOf(
final LmsSetup lmsSetup, final LmsSetup lmsSetup,
final CourseData courseData, final CourseData courseData,
final String uriPrefix) { final String uriPrefix) {
final Map<String, String> additionalAttrs = new HashMap<>();
additionalAttrs.clear(); additionalAttrs.clear();
additionalAttrs.put("timecreated", String.valueOf(courseData.timecreated)); additionalAttrs.put(QuizData.ATTR_ADDITIONAL_CREATION_TIME, String.valueOf(courseData.time_created));
additionalAttrs.put("course_shortname", courseData.shortname); additionalAttrs.put(QuizData.ATTR_ADDITIONAL_SHORT_NAME, courseData.short_name);
additionalAttrs.put("course_fullname", courseData.fullname); additionalAttrs.put(QuizData.ATTR_ADDITIONAL_FULL_NAME, courseData.full_name);
additionalAttrs.put("course_displayname", courseData.displayname); additionalAttrs.put(QuizData.ATTR_ADDITIONAL_DISPLAY_NAME, courseData.display_name);
additionalAttrs.put("course_summary", courseData.summary); additionalAttrs.put(QuizData.ATTR_ADDITIONAL_SUMMARY, courseData.summary);
return courseData.quizzes return courseData.quizzes
.stream() .stream()
.map(courseQuizData -> { .map(courseQuizData -> {
final String startURI = uriPrefix + courseData.id; final String startURI = uriPrefix + courseData.id;
additionalAttrs.put("timelimit", String.valueOf(courseQuizData.timelimit)); additionalAttrs.put(QuizData.ATTR_ADDITIONAL_TIME_LIMIT, String.valueOf(courseQuizData.time_limit));
return new QuizData( return new QuizData(
courseQuizData.id, courseQuizData.id,
lmsSetup.getInstitutionId(), lmsSetup.getInstitutionId(),
@ -191,13 +189,12 @@ public class MoodleCourseAccess extends CourseAccess {
lmsSetup.getLmsType(), lmsSetup.getLmsType(),
courseQuizData.name, courseQuizData.name,
courseQuizData.intro, courseQuizData.intro,
Utils.toDateTimeUTCUnix(courseData.startdate), Utils.toDateTimeUTCUnix(courseData.start_date),
Utils.toDateTimeUTCUnix(courseData.enddate), Utils.toDateTimeUTCUnix(courseData.end_date),
startURI, startURI,
additionalAttrs); additionalAttrs);
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private Result<MoodleAPIRestTemplate> getRestTemplate() { private Result<MoodleAPIRestTemplate> getRestTemplate() {
@ -218,34 +215,34 @@ public class MoodleCourseAccess extends CourseAccess {
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
static final class CourseData { static final class CourseData {
final String id; final String id;
final String shortname; final String short_name;
final String fullname; final String full_name;
final String displayname; final String display_name;
final String summary; final String summary;
final Long startdate; // unixtime milliseconds UTC final Long start_date; // unix-time milliseconds UTC
final Long enddate; // unixtime milliseconds UTC final Long end_date; // unix-time milliseconds UTC
final Long timecreated; // unixtime milliseconds UTC final Long time_created; // unix-time milliseconds UTC
final Collection<CourseQuiz> quizzes = new ArrayList<>(); final Collection<CourseQuiz> quizzes = new ArrayList<>();
@JsonCreator @JsonCreator
protected CourseData( protected CourseData(
@JsonProperty(value = "id") final String id, @JsonProperty(value = "id") final String id,
@JsonProperty(value = "shortname") final String shortname, @JsonProperty(value = "shortname") final String short_name,
@JsonProperty(value = "fullname") final String fullname, @JsonProperty(value = "fullname") final String full_name,
@JsonProperty(value = "displayname") final String displayname, @JsonProperty(value = "displayname") final String display_name,
@JsonProperty(value = "summary") final String summary, @JsonProperty(value = "summary") final String summary,
@JsonProperty(value = "startdate") final Long startdate, @JsonProperty(value = "startdate") final Long start_date,
@JsonProperty(value = "enddate") final Long enddate, @JsonProperty(value = "enddate") final Long end_date,
@JsonProperty(value = "timecreated") final Long timecreated) { @JsonProperty(value = "timecreated") final Long time_created) {
this.id = id; this.id = id;
this.shortname = shortname; this.short_name = short_name;
this.fullname = fullname; this.full_name = full_name;
this.displayname = displayname; this.display_name = display_name;
this.summary = summary; this.summary = summary;
this.startdate = startdate; this.start_date = start_date;
this.enddate = enddate; this.end_date = end_date;
this.timecreated = timecreated; this.time_created = time_created;
} }
} }
@ -266,26 +263,26 @@ public class MoodleCourseAccess extends CourseAccess {
static final class CourseQuiz { static final class CourseQuiz {
final String id; final String id;
final String course; final String course;
final String coursemodule; final String course_module;
final String name; final String name;
final String intro; // HTML final String intro; // HTML
final Long timelimit; // unixtime milliseconds UTC final Long time_limit; // unix-time milliseconds UTC
@JsonCreator @JsonCreator
protected CourseQuiz( protected CourseQuiz(
@JsonProperty(value = "id") final String id, @JsonProperty(value = "id") final String id,
@JsonProperty(value = "course") final String course, @JsonProperty(value = "course") final String course,
@JsonProperty(value = "coursemodule") final String coursemodule, @JsonProperty(value = "coursemodule") final String course_module,
@JsonProperty(value = "name") final String name, @JsonProperty(value = "name") final String name,
@JsonProperty(value = "intro") final String intro, @JsonProperty(value = "intro") final String intro,
@JsonProperty(value = "timelimit") final Long timelimit) { @JsonProperty(value = "timelimit") final Long time_limit) {
this.id = id; this.id = id;
this.course = course; this.course = course;
this.coursemodule = coursemodule; this.course_module = course_module;
this.name = name; this.name = name;
this.intro = intro; this.intro = intro;
this.timelimit = timelimit; this.time_limit = time_limit;
} }
} }

View file

@ -192,7 +192,7 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService {
// this processing returns immediately with the error // this processing returns immediately with the error
final T result = changeAction final T result = changeAction
.apply(mapping) .apply(mapping)
.onError(t -> log.error("Fauled to save exam configuration: {}", .onError(t -> log.error("Failed to save exam configuration: {}",
mapping.configurationNodeId)) mapping.configurationNodeId))
.getOrThrow(); .getOrThrow();

View file

@ -144,6 +144,7 @@ public class ExamConfigurationMappingController extends EntityController<ExamCon
final ExamConfigurationMap requestModel = this.createNew(postMap); final ExamConfigurationMap requestModel = this.createNew(postMap);
return this.checkCreateAccess(requestModel) return this.checkCreateAccess(requestModel)
.flatMap(this::validForCreate)
.map(this::checkPasswordMatch) .map(this::checkPasswordMatch)
.flatMap(entity -> this.examConfigUpdateService.processExamConfigurationMappingChange( .flatMap(entity -> this.examConfigUpdateService.processExamConfigurationMappingChange(
entity, entity,

View file

@ -9,7 +9,7 @@ sebserver.overall.about.markup=<span style='font-family: Arial, Helvetica,sans-s
sebserver.overall.help=Documentation sebserver.overall.help=Documentation
sebserver.overall.help.link=https://www.safeexambrowser.org/news_en.html sebserver.overall.help.link=https://www.safeexambrowser.org/news_en.html
sebserver.overall.message.leave.without.save=You have unsaved changes!</br>Are you sure you want to leave the page? The changes will be lost. sebserver.overall.message.leave.without.save=You have unsaved changes!<br/>Are you sure you want to leave the page? The changes will be lost.
sebserver.overall.upload=Please select a file sebserver.overall.upload=Please select a file
sebserver.overall.upload.unsupported.file=This file type is not supported. Supported files are: {0} sebserver.overall.upload.unsupported.file=This file type is not supported. Supported files are: {0}
sebserver.overall.action.modify.cancel=Cancel sebserver.overall.action.modify.cancel=Cancel
@ -77,6 +77,7 @@ sebserver.form.validation.fieldError.size=The size must be between {3} and {4}
sebserver.form.validation.fieldError.name=The Name is mandatory and must have a size between {3} and {4} character sebserver.form.validation.fieldError.name=The Name is mandatory and must have a size between {3} and {4} character
sebserver.form.validation.fieldError.urlSuffix=The URL Suffix must have a size between {3} and {4} character sebserver.form.validation.fieldError.urlSuffix=The URL Suffix must have a size between {3} and {4} character
sebserver.form.validation.fieldError.notNull=This field is mandatory sebserver.form.validation.fieldError.notNull=This field is mandatory
sebserver.form.validation.fieldError.name.notunique=This name is already in use. Please choose another one.
sebserver.form.validation.fieldError.username.notunique=This Username is already in use. Please choose another one. sebserver.form.validation.fieldError.username.notunique=This Username is already in use. Please choose another one.
sebserver.form.validation.fieldError.password.wrong=The old password is wrong sebserver.form.validation.fieldError.password.wrong=The old password is wrong
sebserver.form.validation.fieldError.password.mismatch=The re-typed password doesn't match the new password sebserver.form.validation.fieldError.password.mismatch=The re-typed password doesn't match the new password
@ -112,7 +113,7 @@ sebserver.login.failed.title=Login failed
sebserver.login.failed.message=Access denied: wrong username or password sebserver.login.failed.message=Access denied: wrong username or password
sebserver.logout=Sign out sebserver.logout=Sign out
sebserver.logout.success.message=You have been successfully signed out. sebserver.logout.success.message=You have been successfully signed out.
sebserver.logout.invalid-session.message=You have been signed out because of a user session invalidation.</br>Please sign in again sebserver.logout.invalid-session.message=You have been signed out because of a user session invalidation.<br/>Please sign in again
sebserver.login.password.change=Information sebserver.login.password.change=Information
sebserver.login.password.change.success=The password was successfully changed. Please sign in with your new password sebserver.login.password.change.success=The password was successfully changed. Please sign in with your new password
@ -139,11 +140,11 @@ sebserver.institution.list.actions=
sebserver.institution.list.empty=No institution has been found. Please adapt the filter or create a new institution sebserver.institution.list.empty=No institution has been found. Please adapt the filter or create a new institution
sebserver.institution.list.title=Institutions sebserver.institution.list.title=Institutions
sebserver.institution.list.column.name=Name sebserver.institution.list.column.name=Name
sebserver.institution.list.column.name.tooltip=The name of the institution.</br></br>Use the filter above to narrow down a specific name.</br>{0} sebserver.institution.list.column.name.tooltip=The name of the institution.<br/><br/>Use the filter above to narrow down a specific name.<br/>{0}
sebserver.institution.list.column.urlSuffix=URL Suffix sebserver.institution.list.column.urlSuffix=URL Suffix
sebserver.institution.list.column.urlSuffix.tooltip=The URL suffix to the institutional login page.</br></br>Use the filter above to narrow down a specific URL suffix.</br>{0} sebserver.institution.list.column.urlSuffix.tooltip=The URL suffix to the institutional login page.<br/><br/>Use the filter above to narrow down a specific URL suffix.<br/>{0}
sebserver.institution.list.column.active=Status sebserver.institution.list.column.active=Status
sebserver.institution.list.column.active.tooltip=The activity of the institution.</br></br>Use the filter above to specify the activity.</br>{0} sebserver.institution.list.column.active.tooltip=The activity of the institution.<br/><br/>Use the filter above to specify the activity.<br/>{0}
sebserver.institution.action.list=Institution sebserver.institution.action.list=Institution
sebserver.institution.action.form=Institution sebserver.institution.action.form=Institution
@ -164,9 +165,9 @@ sebserver.institution.form.title=Institution
sebserver.institution.form.name=Name sebserver.institution.form.name=Name
sebserver.institution.form.name.tooltip=The name of the institution sebserver.institution.form.name.tooltip=The name of the institution
sebserver.institution.form.urlSuffix=URL Suffix sebserver.institution.form.urlSuffix=URL Suffix
sebserver.institution.form.urlSuffix.tooltip=The URL suffix to the institutional login page.</br> Institutional URL is: http(s)://<seb-server-name>/<suffix> sebserver.institution.form.urlSuffix.tooltip=The URL suffix to the institutional login page.<br/> Institutional URL is: http(s)://<seb-server-name>/<suffix>
sebserver.institution.form.logoImage=Logo Image sebserver.institution.form.logoImage=Logo Image
sebserver.institution.form.logoImage.tooltip=The Image that is shown as a logo in the specified institutional login page.</br>In edit mode, use the arrow sign to open a upload dialog. sebserver.institution.form.logoImage.tooltip=The Image that is shown as a logo in the specified institutional login page.<br/>In edit mode, use the arrow sign to open a upload dialog.
sebserver.institution.form.logoImage.unsupportedFileType=The selected file is not supported. Supported are: PNG and JPG sebserver.institution.form.logoImage.unsupportedFileType=The selected file is not supported. Supported are: PNG and JPG
@ -176,27 +177,27 @@ sebserver.institution.form.logoImage.unsupportedFileType=The selected file is no
sebserver.useraccount.list.actions= sebserver.useraccount.list.actions=
sebserver.useraccount.role.SEB_SERVER_ADMIN=SEB Server Administrator sebserver.useraccount.role.SEB_SERVER_ADMIN=SEB Server Administrator
sebserver.useraccount.role.SEB_SERVER_ADMIN.tooltip=A SEB server administrator has overall read privileges</br>and is able to create new institutions and user accounts sebserver.useraccount.role.SEB_SERVER_ADMIN.tooltip=A SEB server administrator has overall read privileges<br/>and is able to create new institutions and user accounts
sebserver.useraccount.role.INSTITUTIONAL_ADMIN=Institutional Administrator sebserver.useraccount.role.INSTITUTIONAL_ADMIN=Institutional Administrator
sebserver.useraccount.role.INSTITUTIONAL_ADMIN.tooltip=An institutional administrator has overall read privileges on the assigned institution</br>and is able to edit the institution and create new user accounts for the institution.</br>Furthermore an institutional administrator is able to create new LMS bindings and SEB client configurations for the institution. sebserver.useraccount.role.INSTITUTIONAL_ADMIN.tooltip=An institutional administrator has overall read privileges on the assigned institution<br/>and is able to edit the institution and create new user accounts for the institution.<br/>Furthermore an institutional administrator is able to create new LMS bindings and SEB client configurations for the institution.
sebserver.useraccount.role.EXAM_ADMIN=Exam Administrator sebserver.useraccount.role.EXAM_ADMIN=Exam Administrator
sebserver.useraccount.role.EXAM_ADMIN.tooltip=An exam administrator has overall read privileges for the institution but cannot see or manage other user accounts.</br>An exam administrator is able to create new SEB configurations and import and setup exams. sebserver.useraccount.role.EXAM_ADMIN.tooltip=An exam administrator has overall read privileges for the institution but cannot see or manage other user accounts.<br/>An exam administrator is able to create new SEB configurations and import and setup exams.
sebserver.useraccount.role.EXAM_SUPPORTER=Exam Supporter sebserver.useraccount.role.EXAM_SUPPORTER=Exam Supporter
sebserver.useraccount.role.EXAM_SUPPORTER.tooltip=An exam supporter can only see and edit the own user account</br> and monitor exams for that he/she was attached by an exam administrator. sebserver.useraccount.role.EXAM_SUPPORTER.tooltip=An exam supporter can only see and edit the own user account<br/> and monitor exams for that he/she was attached by an exam administrator.
sebserver.useraccount.list.empty=No user account has been found. Please adapt the filter or create a new user account sebserver.useraccount.list.empty=No user account has been found. Please adapt the filter or create a new user account
sebserver.useraccount.list.title=User Accounts sebserver.useraccount.list.title=User Accounts
sebserver.useraccount.list.column.institution=Institution sebserver.useraccount.list.column.institution=Institution
sebserver.useraccount.list.column.institution.tooltip=The institution of the user account.</br></br>Use the filter above to specify the institution.</br>{0} sebserver.useraccount.list.column.institution.tooltip=The institution of the user account.<br/><br/>Use the filter above to specify the institution.<br/>{0}
sebserver.useraccount.list.column.name=First Name sebserver.useraccount.list.column.name=First Name
sebserver.useraccount.list.column.name.tooltip=The first name of the user.</br></br>Use the filter above to narrow down a specific first name.</br>{0} sebserver.useraccount.list.column.name.tooltip=The first name of the user.<br/><br/>Use the filter above to narrow down a specific first name.<br/>{0}
sebserver.useraccount.list.column.username=User Name sebserver.useraccount.list.column.username=User Name
sebserver.useraccount.list.column.username.tooltip=The internal user name of the user.</br></br>Use the filter above to narrow down a specific user name.</br>{0} sebserver.useraccount.list.column.username.tooltip=The internal user name of the user.<br/><br/>Use the filter above to narrow down a specific user name.<br/>{0}
sebserver.useraccount.list.column.email=Mail sebserver.useraccount.list.column.email=Mail
sebserver.useraccount.list.column.email.tooltip=The e-mail address of the user.</br></br>Use the filter above to narrow down a specific e-mail address.</br>{0} sebserver.useraccount.list.column.email.tooltip=The e-mail address of the user.<br/><br/>Use the filter above to narrow down a specific e-mail address.<br/>{0}
sebserver.useraccount.list.column.language=Language sebserver.useraccount.list.column.language=Language
sebserver.useraccount.list.column.active=Active sebserver.useraccount.list.column.active=Active
sebserver.useraccount.list.column.active.tooltip=The activity of the user.</br></br>Use the filter above to specify the activity.</br>{0} sebserver.useraccount.list.column.active.tooltip=The activity of the user.<br/><br/>Use the filter above to specify the activity.<br/>{0}
sebserver.useraccount.action.list=User Account sebserver.useraccount.action.list=User Account
sebserver.useraccount.action.form=User Account of {0} sebserver.useraccount.action.form=User Account of {0}
@ -231,9 +232,9 @@ sebserver.useraccount.form.mail.tooltip=The e-mail address of the user.
sebserver.useraccount.form.language=Language sebserver.useraccount.form.language=Language
sebserver.useraccount.form.language.tooltip=The users language. sebserver.useraccount.form.language.tooltip=The users language.
sebserver.useraccount.form.timezone=Time Zone sebserver.useraccount.form.timezone=Time Zone
sebserver.useraccount.form.timezone.tooltip=The time-zone of the user.</br></br>Note that this also defines the exact time and date that is displayed to the user. sebserver.useraccount.form.timezone.tooltip=The time-zone of the user.<br/><br/>Note that this also defines the exact time and date that is displayed to the user.
sebserver.useraccount.form.roles=User Roles sebserver.useraccount.form.roles=User Roles
sebserver.useraccount.form.roles.tooltip=Select the roles for the user.</br>A user can have more then one role but must have at least one.</br></br>Please use the tooltip on the role name for more information about a specific role. sebserver.useraccount.form.roles.tooltip=Select the roles for the user.<br/>A user can have more then one role but must have at least one.<br/><br/>Please use the tooltip on the role name for more information about a specific role.
sebserver.useraccount.form.password=Password sebserver.useraccount.form.password=Password
sebserver.useraccount.form.password.tooltip=The password of the user account sebserver.useraccount.form.password.tooltip=The password of the user account
sebserver.useraccount.form.password.confirm=Confirm Password sebserver.useraccount.form.password.confirm=Confirm Password
@ -257,9 +258,13 @@ sebserver.lmssetup.list.action.no.modify.privilege=No Access: A LMS Setup from o
sebserver.lmssetup.list.empty=No LMS Setup has been found. Please adapt the filter or create a new LMS Setup sebserver.lmssetup.list.empty=No LMS Setup has been found. Please adapt the filter or create a new LMS Setup
sebserver.lmssetup.list.title=Learning Management System Setups sebserver.lmssetup.list.title=Learning Management System Setups
sebserver.lmssetup.list.column.institution=Institution sebserver.lmssetup.list.column.institution=Institution
sebserver.lmssetup.list.column.institution.tooltip=The institution of the LMS setup.<br/><br/>Use the filter above to specify the institution.<br/>{0}
sebserver.lmssetup.list.column.name=Name sebserver.lmssetup.list.column.name=Name
sebserver.lmssetup.list.column.name.tooltip=The name of the LMS setup.<br/><br/>Use the filter above to narrow down a specific LMS by name.<br/>{0}
sebserver.lmssetup.list.column.type=LMS Type sebserver.lmssetup.list.column.type=LMS Type
sebserver.lmssetup.list.column.type.tooltip=The type of the LMS.<br/><br/>Use the filter above to specify the LMS type.<br/>{0}
sebserver.lmssetup.list.column.active=Active sebserver.lmssetup.list.column.active=Active
sebserver.lmssetup.list.column.active.tooltip=The activity of the LMS Setup.<br/><br/>Use the filter above to specify the activity.<br/>{0}
sebserver.lmssetup.action.list=LMS Connection Settings sebserver.lmssetup.action.list=LMS Connection Settings
sebserver.lmssetup.action.form=LMS Setup sebserver.lmssetup.action.form=LMS Setup
@ -270,7 +275,7 @@ sebserver.lmssetup.action.modify=Edit
sebserver.lmssetup.action.savetest=Test And Save sebserver.lmssetup.action.savetest=Test And Save
sebserver.lmssetup.action.testsave=Test And Save sebserver.lmssetup.action.testsave=Test And Save
sebserver.lmssetup.action.test.ok=Successfully connected to the course API sebserver.lmssetup.action.test.ok=Successfully connected to the course API
sebserver.lmssetup.action.test.tokenRequestError=The API access was denied: {0} sebserver.lmssetup.action.test.tokenRequestError=The API access was denied: {0}<br/>Please check the LMS connection details.
sebserver.lmssetup.action.test.quizRequestError=Unable to request courses or quizzes from the course API of the LMS. {0} sebserver.lmssetup.action.test.quizRequestError=Unable to request courses or quizzes from the course API of the LMS. {0}
sebserver.lmssetup.action.test.quizRestrictionError=Unable to access course restriction API of the LMS. {0} sebserver.lmssetup.action.test.quizRestrictionError=Unable to access course restriction API of the LMS. {0}
sebserver.lmssetup.action.test.missingParameter=There is one or more missing connection parameter.<br/>Please check the connection parameter for this LMS Setup sebserver.lmssetup.action.test.missingParameter=There is one or more missing connection parameter.<br/>Please check the connection parameter for this LMS Setup
@ -285,17 +290,25 @@ sebserver.lmssetup.info.pleaseSelect=Please select first a LMS Setup from the li
sebserver.lmssetup.form.title=Learning Management System Setup sebserver.lmssetup.form.title=Learning Management System Setup
sebserver.lmssetup.form.title.new=Add Learning Management System Setup sebserver.lmssetup.form.title.new=Add Learning Management System Setup
sebserver.lmssetup.form.institution=Institution sebserver.lmssetup.form.institution=Institution
sebserver.lmssetup.form.institution.tooltip=The institution where the LMS setup belongs to
sebserver.lmssetup.form.name=Name sebserver.lmssetup.form.name=Name
sebserver.lmssetup.form.name.tooltip=The name of the LMS setup
sebserver.lmssetup.form.type=Type sebserver.lmssetup.form.type=Type
sebserver.lmssetup.form.clientname.seb=SEB Auth. Name sebserver.lmssetup.form.type.tooltip=The type of the LMS Setup.
sebserver.lmssetup.form.secret.seb=SEB Auth. Password
sebserver.lmssetup.form.url=LMS Server Address sebserver.lmssetup.form.url=LMS Server Address
sebserver.lmssetup.form.url.tooltip=The server URL to the specific LMS server.<br/><br/>This should point to the main- or root-address of the LMS server
sebserver.lmssetup.form.clientname.lms=LMS Server Username sebserver.lmssetup.form.clientname.lms=LMS Server Username
sebserver.lmssetup.form.clientname.lms.tooltip=The client name of the API client access to the LMS.<br/><br/>This is usually provided by an LMS administrator that has created a API access account for SEB Server binding within the LMS server.
sebserver.lmssetup.form.secret.lms=LMS Server Password sebserver.lmssetup.form.secret.lms=LMS Server Password
sebserver.lmssetup.form.secret.lms.tooltip=The secret or password of the API client access to the LMS.<br/><br/>This is usually provided by an LMS administrator that has created a API access account for SEB Server binding within the LMS server.
sebserver.lmssetup.form.proxy=Proxy sebserver.lmssetup.form.proxy=Proxy
sebserver.lmssetup.form.proxy.tooltip=The proxy details of a proxy is needed to connect to a LMS server
sebserver.lmssetup.form.proxy.host=Proxy Host sebserver.lmssetup.form.proxy.host=Proxy Host
sebserver.lmssetup.form.proxy.host.tooltip=The server name of the proxy host to connect to
sebserver.lmssetup.form.proxy.port=Proxy Port sebserver.lmssetup.form.proxy.port=Proxy Port
sebserver.lmssetup.form.proxy.port.tooltip=The proxy server port to connect to
sebserver.lmssetup.form.proxy.auth-credentials=Proxy Name/Password sebserver.lmssetup.form.proxy.auth-credentials=Proxy Name/Password
sebserver.lmssetup.form.proxy.auth-credentials.tooltip=The proxy authentication credentials (name and password)<br/>to authenticate the connection within the proxy server
################################ ################################
# Quiz Discovery # Quiz Discovery
@ -306,10 +319,15 @@ sebserver.quizdiscovery.list.actions=
sebserver.quizdiscovery.list.title=Quizzes sebserver.quizdiscovery.list.title=Quizzes
sebserver.quizdiscovery.list.empty=No Quiz has been found. Please adapt the filter or create a new LMS Setup sebserver.quizdiscovery.list.empty=No Quiz has been found. Please adapt the filter or create a new LMS Setup
sebserver.quizdiscovery.list.column.institution=Institution sebserver.quizdiscovery.list.column.institution=Institution
sebserver.quizdiscovery.list.column.institution.tooltip=The institution of the LMS setup that defines LMS of the quiz.<br/><br/>Use the filter above to specify the institution.<br/>{0}
sebserver.quizdiscovery.list.column.lmssetup=LMS sebserver.quizdiscovery.list.column.lmssetup=LMS
sebserver.quizdiscovery.list.column.lmssetup.tooltip=The LMS setup that defines the LMS of the quiz<br/><br/>Use the filter above to specify the LMS setup.<br/>{0}
sebserver.quizdiscovery.list.column.name=Name sebserver.quizdiscovery.list.column.name=Name
sebserver.quizdiscovery.list.column.name.tooltip=The name of the quiz.<br/><br/>Use the filter above to narrow down a specific quiz name.<br/>{0}
sebserver.quizdiscovery.list.column.starttime=Start Time {0} sebserver.quizdiscovery.list.column.starttime=Start Time {0}
sebserver.quizdiscovery.list.column.starttime.tooltip=The start time of the quiz.<br/><br/>Use the filter above to set a specific from date.<br/>{0}
sebserver.quizdiscovery.list.column.endtime=End Time {0} sebserver.quizdiscovery.list.column.endtime=End Time {0}
sebserver.quizdiscovery.list.column.endtime.tooltip=The end time of the quiz.<br/><br/>{0}
sebserver.quizdiscovery.info.pleaseSelect=Please select first a Quiz from the list sebserver.quizdiscovery.info.pleaseSelect=Please select first a Quiz from the list
sebserver.quizdiscovery.action.list=LMS Exam Discovery sebserver.quizdiscovery.action.list=LMS Exam Discovery
@ -318,16 +336,32 @@ sebserver.quizdiscovery.quiz.import.out.dated=The Selected Quiz is is already fi
sebserver.quizdiscovery.action.details=Show Details sebserver.quizdiscovery.action.details=Show Details
sebserver.quizdiscovery.quiz.details.title=Quiz Details sebserver.quizdiscovery.quiz.details.title=Quiz Details
sebserver.quizdiscovery.quiz.details.institution=Institution
sebserver.quizdiscovery.quiz.details.institution.tooltip=The institution of the LMS setup that defines the LMS of the quiz.
sebserver.quizdiscovery.quiz.details.lmssetup=LMS Setup
sebserver.quizdiscovery.quiz.details.lmssetup.tooltip=The LMS setup that defines the LMS of the quiz.
sebserver.quizdiscovery.quiz.details.name=Name
sebserver.quizdiscovery.quiz.details.name.tooltip=The name of the quiz.<br/><br/>This name is defined on the corresponding LMS
sebserver.quizdiscovery.quiz.details.description=Description sebserver.quizdiscovery.quiz.details.description=Description
sebserver.quizdiscovery.quiz.details.description.tooltip=The description of the quiz.<br/><br/>This description is defined on the corresponding LMS
sebserver.quizdiscovery.quiz.details.starttime=Start Time sebserver.quizdiscovery.quiz.details.starttime=Start Time
sebserver.quizdiscovery.quiz.details.starttime.tooltip=The start time of the quiz.<br/><br/>This time is set on the corresponding LMS
sebserver.quizdiscovery.quiz.details.endtime=End Time sebserver.quizdiscovery.quiz.details.endtime=End Time
sebserver.quizdiscovery.quiz.details.endtime.tooltip=The end time of the quiz.<br/><br/>This time is set on the corresponding LMS
sebserver.quizdiscovery.quiz.details.url=Start URL sebserver.quizdiscovery.quiz.details.url=Start URL
sebserver.quizdiscovery.quiz.details.url.tooltip=The start URL on the LMS for the quiz.<br/><br/>This is defined by the LMS setup and the quiz URL
sebserver.quizdiscovery.quiz.details.additional.timecreated=Creation Time sebserver.quizdiscovery.quiz.details.additional.timecreated=Creation Time
sebserver.quizdiscovery.quiz.details.additional.timecreated.tooltip=The time when the quiz was first created<br/><br/>This time is defined by the corresponding LMS
sebserver.quizdiscovery.quiz.details.additional.course_shortname=Short Name sebserver.quizdiscovery.quiz.details.additional.course_shortname=Short Name
sebserver.quizdiscovery.quiz.details.additional.course_shortname.tooltip=The short name of the quiz<br/><br/>This is defined on the corresponding LMS
sebserver.quizdiscovery.quiz.details.additional.course_fullname=Full Name sebserver.quizdiscovery.quiz.details.additional.course_fullname=Full Name
sebserver.quizdiscovery.quiz.details.additional.course_fullname.tooltip=The full name of the quiz<br/><br/>This is defined on the corresponding LMS
sebserver.quizdiscovery.quiz.details.additional.course_displayname=Display Name sebserver.quizdiscovery.quiz.details.additional.course_displayname=Display Name
sebserver.quizdiscovery.quiz.details.additional.course_displayname.tooltip=The display name of the quiz<br/><br/>This is defined on the corresponding LMS
sebserver.quizdiscovery.quiz.details.additional.course_summary=Summary sebserver.quizdiscovery.quiz.details.additional.course_summary=Summary
sebserver.quizdiscovery.quiz.details.additional.course_summary.tooltip=The summary of the quiz<br/><br/>This is defined on the corresponding LMS
sebserver.quizdiscovery.quiz.details.additional.timelimit=Time Limit sebserver.quizdiscovery.quiz.details.additional.timelimit=Time Limit
sebserver.quizdiscovery.quiz.details.additional.timelimit.toolitp=The time limit of the quiz<br/><br/>This is defined on the corresponding LMS
################################ ################################
# Exam # Exam
@ -336,10 +370,15 @@ sebserver.quizdiscovery.quiz.details.additional.timelimit=Time Limit
sebserver.exam.list.actions= sebserver.exam.list.actions=
sebserver.exam.list.title=Exams sebserver.exam.list.title=Exams
sebserver.exam.list.column.institution=Institution sebserver.exam.list.column.institution=Institution
sebserver.exam.list.column.institution.tooltip=The institution of the LMS setup that defines LMS of the exam.<br/><br/>Use the filter above to specify the institution.<br/>{0}
sebserver.exam.list.column.lmssetup=LMS sebserver.exam.list.column.lmssetup=LMS
sebserver.exam.list.column.lmssetup.tooltip=The LMS setup that defines the LMS of the exam<br/><br/>Use the filter above to specify the LMS setup.<br/>{0}
sebserver.exam.list.column.name=Name sebserver.exam.list.column.name=Name
sebserver.exam.list.column.name.tooltip=The name of the exam.<br/><br/>Use the filter above to narrow down a specific exam name.<br/>{0}
sebserver.exam.list.column.starttime=Start Time {0} sebserver.exam.list.column.starttime=Start Time {0}
sebserver.exam.list.column.starttime.tooltip=The start time of the exam.<br/><br/>Use the filter above to set a specific from date.<br/>{0}
sebserver.exam.list.column.type=Type sebserver.exam.list.column.type=Type
sebserver.exam.list.column.type.tooltip=The type of the exam.<br/><br/>Use the filter above to set a specific exam type.<br/>{0}
sebserver.exam.list.empty=No Exam has been found. Please adapt the filter or import one from Quiz sebserver.exam.list.empty=No Exam has been found. Please adapt the filter or import one from Quiz
sebserver.exam.list.modify.out.dated=Finished exams cannot be modified. sebserver.exam.list.modify.out.dated=Finished exams cannot be modified.
@ -369,43 +408,73 @@ sebserver.exam.info.pleaseSelect=Please select first an Exam from the list
sebserver.exam.form.title.import=Import Exam sebserver.exam.form.title.import=Import Exam
sebserver.exam.form.title=Exam sebserver.exam.form.title=Exam
sebserver.exam.form.lmssetup=LMS Setup sebserver.exam.form.lmssetup=LMS Setup
sebserver.exam.form.lmssetup.tooltip=The LMS setup that defines the LMS of the exam.
sebserver.exam.form.quizid=Quiz Identifier sebserver.exam.form.quizid=Quiz Identifier
sebserver.exam.form.quizid.tooltip=The identifier that identifies the quiz of the exam on the corresponding LMS
sebserver.exam.form.quizurl=Quiz URL sebserver.exam.form.quizurl=Quiz URL
sebserver.exam.form.quizurl.tooltip=The direct URL link to the quiz/exam on the LMS
sebserver.exam.form.name=Name sebserver.exam.form.name=Name
sebserver.exam.form.name.tooltip=The name of the exam.<br/><br/>This name is defined on the corresponding LMS
sebserver.exam.form.description=Description sebserver.exam.form.description=Description
sebserver.exam.form.description.tooltip=The description of the exam.<br/><br/>This description is defined on the corresponding LMS
sebserver.exam.form.starttime=Start Time sebserver.exam.form.starttime=Start Time
sebserver.exam.form.starttime.tooltip=The start time of the exam.<br/><br/>This time is set on the corresponding LMS
sebserver.exam.form.endtime=End Time sebserver.exam.form.endtime=End Time
sebserver.exam.form.endtime.tooltip=The end time of the exam.<br/><br/>This time is set on the corresponding LMS
sebserver.exam.form.status=Status sebserver.exam.form.status=Status
sebserver.exam.form.status.tooltip=The current status for the exam.<br/><br/>Either "Up Coming" for an exam that has not yet been started,<br/>"Running" for an exam that is currently running<br/>or "Finished" for an exam that has already been finished yet
sebserver.exam.form.type=Exam Type sebserver.exam.form.type=Exam Type
sebserver.exam.form.type.tooltip=The type of the exam.<br/><br/>This has only descriptive character for now and can be used to categorise exams within a type
sebserver.exam.form.supporter=Exam Supporter sebserver.exam.form.supporter=Exam Supporter
sebserver.exam.form.supporter.action.add=Add as supporter for this exam sebserver.exam.form.supporter.tooltip=A list of users that are allowed to support this exam.<br/><br/>To add a user in edit mode click into the field on right-hand and start typing the first letters of the username.<br/>A filtered choice will drop down. Click on a specific username on the drop-down to add the user to the list.<br/>To remove a user from the list, just double-click the username on the list.
sebserver.exam.form.supporter.action.remove=Remove supporter
sebserver.exam.form.sebrestriction.title=SEB Restriction Details sebserver.exam.form.sebrestriction.title=SEB Restriction Details
sebserver.exam.form.sebrestriction.info=Info
sebserver.exam.form.sebrestriction.info-text=A detailed description of the SEB restriction can be found within this page:<br/><a href="https://seb-openedx.readthedocs.io/en/latest/usage.html">https://seb-openedx.readthedocs.io/en/latest/usage.html</a>
sebserver.exam.form.sebrestriction.configKeys=Config Keys sebserver.exam.form.sebrestriction.configKeys=Config Keys
sebserver.exam.form.sebrestriction.configKeys.tooltip=A comma-separated list of SEB Config Keys that are automatically generated from the attached SEB exam configurations<br/>and are checked by the LMS for the restricted SEB access for every request
sebserver.exam.form.sebrestriction.browserExamKeys=Browser Exam Keys sebserver.exam.form.sebrestriction.browserExamKeys=Browser Exam Keys
sebserver.exam.form.sebrestriction.browserExamKeys.tooltip=A comma-separated list of SEB Browser Exam Keys<br/>that are checked by the LMS for the restricted SEB access for every request
sebserver.exam.form.sebrestriction.WHITELIST_PATHS=Component White-List sebserver.exam.form.sebrestriction.WHITELIST_PATHS=Component White-List
sebserver.exam.form.sebrestriction.WHITELIST_PATHS.tooltip=Grant no-restriction to each of the given Open edX path components by select them for white-list.
sebserver.exam.form.sebrestriction.BLACKLIST_CHAPTERS=Chapters Black-List sebserver.exam.form.sebrestriction.BLACKLIST_CHAPTERS=Chapters Black-List
sebserver.exam.form.sebrestriction.BLACKLIST_CHAPTERS.tooltip=Explicitly restrict a course chapter by adding the course-chapter-identifier to this comma-separated list
sebserver.exam.form.sebrestriction.PERMISSION_COMPONENTS=Permissions sebserver.exam.form.sebrestriction.PERMISSION_COMPONENTS=Permissions
sebserver.exam.form.sebrestriction.PERMISSION_COMPONENTS.tooltip=Define the additional SEB restriction permissions
sebserver.exam.form.sebrestriction.USER_BANNING_ENABLED=User Banning sebserver.exam.form.sebrestriction.USER_BANNING_ENABLED=User Banning
sebserver.exam.form.sebrestriction.USER_BANNING_ENABLED.tooltip=Indicates whether the user of a restricted access shall be banned on authentication failure or not.
sebserver.exam.form.sebrestriction.whiteListPaths.ABOUT=About sebserver.exam.form.sebrestriction.whiteListPaths.ABOUT=About
sebserver.exam.form.sebrestriction.whiteListPaths.ABOUT.tooltip=The "About" section of the Open edX course
sebserver.exam.form.sebrestriction.whiteListPaths.COURSE_OUTLINE=Course Outline sebserver.exam.form.sebrestriction.whiteListPaths.COURSE_OUTLINE=Course Outline
sebserver.exam.form.sebrestriction.whiteListPaths.COURSE_OUTLINE.tooltip=The outline section of the Open edX course
sebserver.exam.form.sebrestriction.whiteListPaths.COURSE_WARE=Course Ware sebserver.exam.form.sebrestriction.whiteListPaths.COURSE_WARE=Course Ware
sebserver.exam.form.sebrestriction.whiteListPaths.COURSE_WARE.tooltip=The actual course and course content
sebserver.exam.form.sebrestriction.whiteListPaths.DISCUSSION=Discussion sebserver.exam.form.sebrestriction.whiteListPaths.DISCUSSION=Discussion
sebserver.exam.form.sebrestriction.whiteListPaths.DISCUSSION.tooltip=The discussion section of the Open edX course
sebserver.exam.form.sebrestriction.whiteListPaths.PROGRESS=Progress sebserver.exam.form.sebrestriction.whiteListPaths.PROGRESS=Progress
sebserver.exam.form.sebrestriction.whiteListPaths.PROGRESS.tooltip=The progress overview of the Open edX course
sebserver.exam.form.sebrestriction.whiteListPaths.WIKI=Description (Wiki) sebserver.exam.form.sebrestriction.whiteListPaths.WIKI=Description (Wiki)
sebserver.exam.form.sebrestriction.whiteListPaths.WIKI.tooltip=The wikipedia section of the Open edX course
sebserver.exam.form.sebrestriction.permissions.ALWAYS_ALLOW_STUFF=Stuff Role Always Allowed sebserver.exam.form.sebrestriction.permissions.ALWAYS_ALLOW_STUFF=Stuff Role Always Allowed
sebserver.exam.form.sebrestriction.permissions.ALWAYS_ALLOW_STUFF.tooltip=Set this to always allow none-restricted access for a user that has "stuff" privileges.
sebserver.exam.form.sebrestriction.permissions.CHECK_BROWSER_EXAM_KEY=Check Browser-Exam-Key sebserver.exam.form.sebrestriction.permissions.CHECK_BROWSER_EXAM_KEY=Check Browser-Exam-Key
sebserver.exam.form.sebrestriction.permissions.CHECK_BROWSER_EXAM_KEY.tooltip=Always check received SEB Browser Exam Key with the defined ones for every request.
sebserver.exam.form.sebrestriction.permissions.CHECK_CONFIG_KEY=Check Config-Key sebserver.exam.form.sebrestriction.permissions.CHECK_CONFIG_KEY=Check Config-Key
sebserver.exam.form.sebrestriction.permissions.CHECK_CONFIG_KEY.tooltip=Always check received SEB Config Key with the defined ones for every request.
sebserver.exam.form.sebrestriction.permissions.CHECK_BROWSER_EXAM_OR_CONFIG_KEY=Check Browser-Exam- Or Config-Key sebserver.exam.form.sebrestriction.permissions.CHECK_BROWSER_EXAM_OR_CONFIG_KEY=Check Browser-Exam- Or Config-Key
sebserver.exam.form.sebrestriction.permissions.CHECK_BROWSER_EXAM_OR_CONFIG_KEY.tooltip=Always check either SEB Browser Exam Key or SEB Config Key with the defined ones for every request.
sebserver.exam.type.UNDEFINED=Not Defined sebserver.exam.type.UNDEFINED=Not Defined
sebserver.exam.type.UNDEFINED.tooltip=No exam type specified
sebserver.exam.type.MANAGED=Managed Devices sebserver.exam.type.MANAGED=Managed Devices
sebserver.exam.type.MANAGED.tooltip=Exam type specified for managed devices
sebserver.exam.type.BYOD=Bring Your Own Device sebserver.exam.type.BYOD=Bring Your Own Device
sebserver.exam.type.BYOD.tooltip=Exam type specified for bring your own devices
sebserver.exam.type.VDI=VDI (Virtual Desktop Infrastructure) sebserver.exam.type.VDI=VDI (Virtual Desktop Infrastructure)
sebserver.exam.type.VDI.tooltip=Exam type specified for Virtual Desktop Infrastructure
sebserver.exam.status.UP_COMING=Up Coming sebserver.exam.status.UP_COMING=Up Coming
sebserver.exam.status.RUNNING=Running sebserver.exam.status.RUNNING=Running
@ -413,9 +482,13 @@ sebserver.exam.status.FINISHED=Finished
sebserver.exam.configuration.list.actions= sebserver.exam.configuration.list.actions=
sebserver.exam.configuration.list.title=SEB Exam Configuration sebserver.exam.configuration.list.title=SEB Exam Configuration
sebserver.exam.configuration.list.title.tooltip=A list of all attached SEB exam configuration for this exam.
sebserver.exam.configuration.list.column.name=Name sebserver.exam.configuration.list.column.name=Name
sebserver.exam.configuration.list.column.name.tooltip=The name of the attached SEB exam configuration.
sebserver.exam.configuration.list.column.description=Description sebserver.exam.configuration.list.column.description=Description
sebserver.exam.configuration.list.column.description.tooltip=The description of the attached SEB exam configuration.
sebserver.exam.configuration.list.column.status=Status sebserver.exam.configuration.list.column.status=Status
sebserver.exam.configuration.list.column.status.tooltip=The current status of the attached SEB exam configuration.
sebserver.exam.configuration.list.empty=There is currently no SEB Configuration defined for this Exam. Please add one sebserver.exam.configuration.list.empty=There is currently no SEB Configuration defined for this Exam. Please add one
sebserver.exam.configuration.list.pleaseSelect=Please select first a SEB Configuration from the list sebserver.exam.configuration.list.pleaseSelect=Please select first a SEB Configuration from the list
sebserver.exam.configuration.action.noconfig.message=There is currently no SEB exam configuration to select.<br/>Please create one in SEB Configuration / Exam Configuration sebserver.exam.configuration.action.noconfig.message=There is currently no SEB exam configuration to select.<br/>Please create one in SEB Configuration / Exam Configuration
@ -431,25 +504,34 @@ sebserver.exam.configuration.action.get-config-key=Export Config-Key
sebserver.exam.configuration.form.title.new=Add SEB Configuration Mapping sebserver.exam.configuration.form.title.new=Add SEB Configuration Mapping
sebserver.exam.configuration.form.title=SEB Configuration Mapping sebserver.exam.configuration.form.title=SEB Configuration Mapping
sebserver.exam.configuration.form.name=SEB Configuration sebserver.exam.configuration.form.name=SEB Configuration
sebserver.exam.configuration.form.name.tooltip=Please select a SEB exam configuration to attach to the exam
sebserver.exam.configuration.form.encryptSecret=Encryption Password sebserver.exam.configuration.form.encryptSecret=Encryption Password
sebserver.exam.configuration.form.encryptSecret.tooltip=Define a encryption password if the SEB exam configuration should be encrypted by password
sebserver.exam.configuration.form.description=Description sebserver.exam.configuration.form.description=Description
sebserver.exam.configuration.form.description.tooltip=The description of the selected SEB exam configuration
sebserver.exam.configuration.form.status=Status sebserver.exam.configuration.form.status=Status
sebserver.exam.configuration.form.status.tooltip=The current status of the selected SEB exam configuration
sebserver.exam.configuration.form.encryptSecret.confirm=Confirm Password sebserver.exam.configuration.form.encryptSecret.confirm=Confirm Password
sebserver.exam.configuration.form.encryptSecret.confirm.tooltip=Please confirm the encryption password if there is one
sebserver.exam.indicator.list.actions= sebserver.exam.indicator.list.actions=
sebserver.exam.indicator.list.title=Indicators sebserver.exam.indicator.list.title=Indicators
sebserver.exam.indicator.list.title.tooltip=A list of indicators that are shown on the exam monitoring view
sebserver.exam.indicator.list.column.type=Type sebserver.exam.indicator.list.column.type=Type
sebserver.exam.indicator.list.column.type.tooltip=The type of indicator
sebserver.exam.indicator.list.column.name=Name sebserver.exam.indicator.list.column.name=Name
sebserver.exam.indicator.list.column.name.tooltip=The name of the indicator
sebserver.exam.indicator.list.column.thresholds=Thresholds sebserver.exam.indicator.list.column.thresholds=Thresholds
sebserver.exam.indicator.list.column.thresholds.tooltip=The thresholds of the indicator
sebserver.exam.indicator.list.empty=There is currently no indicator defined for this exam. Please create a new one sebserver.exam.indicator.list.empty=There is currently no indicator defined for this exam. Please create a new one
sebserver.exam.indicator.list.pleaseSelect=Please select first an indicator from the list sebserver.exam.indicator.list.pleaseSelect=Please select first an indicator from the list
sebserver.exam.indicator.type.LAST_PING=Last Ping Time sebserver.exam.indicator.type.LAST_PING=Last Ping Time
sebserver.exam.indicator.type.ERROR_COUNT=Errors sebserver.exam.indicator.type.ERROR_COUNT=Errors
sebserver.exam.indicator.type.WARN_COUNT=Warnings sebserver.exam.indicator.type.WARN_COUNT=Warnings
sebserver.exam.indicator.type.description.LAST_PING=This indicator shows the time in milliseconds since</br> the last ping has been received from a SEB Client.</br>This indicator can be used to track a SEB Client connection and indicate connection losse.</br></br>Thresholds are defined in milliseconds. sebserver.exam.indicator.type.description.LAST_PING=This indicator shows the time in milliseconds since<br/> the last ping has been received from a SEB Client.<br/>This indicator can be used to track a SEB Client connection and indicate connection loss.<br/><br/>The value is in milliseconds.
sebserver.exam.indicator.type.description.ERROR_COUNT=This indicator shows the number of error log messages that</br> has been received from a SEB Client.</br>This indicator can be used to track errors of connected SEB Clients</br></br>Thresholds are defined by natural numbers. sebserver.exam.indicator.type.description.ERROR_COUNT=This indicator shows the number of error log messages that<br/> has been received from a SEB Client.<br/>This indicator can be used to track errors of connected SEB Clients<br/><br/>The value is natural numbers.
sebserver.exam.indicator.type.description.WARN_COUNT=This indicator shows the number of warn log messages that</br> has been received from a SEB Client.</br>This indicator can be used to track warnings of connected SEB Clients</br></br>Thresholds are defined by natural numbers. sebserver.exam.indicator.type.description.WARN_COUNT=This indicator shows the number of warn log messages that<br/> has been received from a SEB Client.<br/>This indicator can be used to track warnings of connected SEB Clients<br/><br/>The value is natural numbers.
sebserver.exam.indicator.info.pleaseSelect=Please select first an indicator from the list sebserver.exam.indicator.info.pleaseSelect=Please select first an indicator from the list
@ -462,19 +544,27 @@ sebserver.exam.indicator.action.save=Save
sebserver.exam.indicator.form.title=Indicator sebserver.exam.indicator.form.title=Indicator
sebserver.exam.indicator.form.title.new=Add Indicator sebserver.exam.indicator.form.title.new=Add Indicator
sebserver.exam.indicator.form.exam=Exam sebserver.exam.indicator.form.exam=Exam
sebserver.exam.indicator.form.exam.tooltip=The exam this indicator belongs to.
sebserver.exam.indicator.form.name=Name sebserver.exam.indicator.form.name=Name
sebserver.exam.indicator.form.name.tooltip=The name of the indicator.<br/><br/>This name is also displayed as the column title of the indicator on the exam monitoring view
sebserver.exam.indicator.form.type=Type sebserver.exam.indicator.form.type=Type
sebserver.exam.indicator.form.type.tooltip=The type of the indicator.<br/><br/>There are only a set of defined indicators to choose from.<br/>Choose one to see a detailed description for each indicator below.
sebserver.exam.indicator.form.description=Type Description sebserver.exam.indicator.form.description=Type Description
sebserver.exam.indicator.form.description.tooltip=A detailed description of the selected indicator.
sebserver.exam.indicator.form.color=Default Color sebserver.exam.indicator.form.color=Default Color
sebserver.exam.indicator.form.color.tooltip=The default color that is displayed on the exam monitoring for this indicator.
sebserver.exam.indicator.form.color.action=Please select a color sebserver.exam.indicator.form.color.action=Please select a color
sebserver.exam.indicator.form.thresholds=Thresholds sebserver.exam.indicator.form.thresholds=Thresholds
sebserver.exam.indicator.form.thresholds.tooltip=A list of value / color pairs that defines the thresholds of the indicator.<br/><br/>On the exam monitoring view a cell of the indicator is displayed in the specified color when the defined threshold value is reached
sebserver.exam.indicator.thresholds.select.color=Please select a color sebserver.exam.indicator.thresholds.select.color=Please select a color
sebserver.exam.indicator.thresholds.list.title=Thresholds sebserver.exam.indicator.thresholds.list.title=Thresholds
sebserver.exam.indicator.thresholds.list.value=Value sebserver.exam.indicator.thresholds.list.value=Value
sebserver.exam.indicator.thresholds.list.value.tooltip=The threshold value.
sebserver.exam.indicator.thresholds.list.color=Color sebserver.exam.indicator.thresholds.list.color=Color
sebserver.exam.indicator.thresholds.list.add=Add Threshold sebserver.exam.indicator.thresholds.list.color.tooltip=The color that is displayed on the exam monitoring view when indicator value has reached the defined threshold value.
sebserver.exam.indicator.thresholds.list.remove=Delete Threshold sebserver.exam.indicator.thresholds.list.add=Add a new threshold
sebserver.exam.indicator.thresholds.list.remove=Delete this threshold
################################ ################################
# SEB Client Configuration # SEB Client Configuration
@ -489,28 +579,40 @@ sebserver.clientconfig.list.empty=There is currently no SEB-Client configuration
sebserver.clientconfig.list.title=SEB Client Configurations sebserver.clientconfig.list.title=SEB Client Configurations
sebserver.clientconfig.list.actions= sebserver.clientconfig.list.actions=
sebserver.clientconfig.list.column.institution=Institution sebserver.clientconfig.list.column.institution=Institution
sebserver.clientconfig.list.column.institution.tooltip=The institution of the SEB client configuration.</br></br>Use the filter above to specify the institution.</br>{0} sebserver.clientconfig.list.column.institution.tooltip=The institution of the SEB client configuration.<br/><br/>Use the filter above to specify the institution.<br/>{0}
sebserver.clientconfig.list.column.name=Name sebserver.clientconfig.list.column.name=Name
sebserver.clientconfig.list.column.name.tooltip=The name of the SEB client configuration.</br></br>Use the filter above to narrow down a specific name.</br>{0} sebserver.clientconfig.list.column.name.tooltip=The name of the SEB client configuration.<br/><br/>Use the filter above to narrow down a specific name.<br/>{0}
sebserver.clientconfig.list.column.date=Creation Date {0} sebserver.clientconfig.list.column.date=Creation Date {0}
sebserver.clientconfig.list.column.date.tooltip=The date when the SEB client configuration was first created.</br></br>Use the filter above to specify a from-date.</br>{0} sebserver.clientconfig.list.column.date.tooltip=The date when the SEB client configuration was first created.<br/><br/>Use the filter above to specify a from-date.<br/>{0}
sebserver.clientconfig.list.column.active=Active sebserver.clientconfig.list.column.active=Active
sebserver.clientconfig.list.column.active.tooltip=The activity of SEB client configuration.</br></br>Use the filter above to specify the activity.</br>{0} sebserver.clientconfig.list.column.active.tooltip=The activity of SEB client configuration.<br/><br/>Use the filter above to specify the activity.<br/>{0}
sebserver.clientconfig.info.pleaseSelect=Please select first a Client Configuration from the list sebserver.clientconfig.info.pleaseSelect=Please select first a Client Configuration from the list
sebserver.clientconfig.list.action.no.modify.privilege=No Access: A SEB Client Configuration from other institution cannot be modified. sebserver.clientconfig.list.action.no.modify.privilege=No Access: A SEB Client Configuration from other institution cannot be modified.
sebserver.clientconfig.form.title.new=Add Client Configuration sebserver.clientconfig.form.title.new=Add Client Configuration
sebserver.clientconfig.form.title=SEB Client Configuration sebserver.clientconfig.form.title=SEB Client Configuration
sebserver.clientconfig.form.name=Name sebserver.clientconfig.form.name=Name
sebserver.clientconfig.form.name.tooltip=The name of the SEB Client Configuration.</br>Can be any name that not already exists for another SEB Client Configuration sebserver.clientconfig.form.name.tooltip=The name of the SEB Client Configuration.<br/>Can be any name that not already exists for another SEB Client Configuration
sebserver.clientconfig.form.fallback=With Fallback
sebserver.clientconfig.form.fallback.tooltip=Indicates whether this SEB Client Configuration has a fallback definition or not
sebserver.clientconfig.form.fallback-url=Fallback Start URL sebserver.clientconfig.form.fallback-url=Fallback Start URL
sebserver.clientconfig.form.fallback-url.tooltip=A fallback URL that tells the SEB where to go when the SEB Server service is unavailable. sebserver.clientconfig.form.fallback-url.tooltip=A fallback URL that tells the SEB where to go when the SEB Server service is unavailable.
sebserver.clientconfig.form.sebServerFallbackTimeout=Fallback Timeout
sebserver.clientconfig.form.sebServerFallbackTimeout.tooltip=Defines the fallback timeout for the SEB Client in milli-seconds.
sebserver.clientconfig.form.sebServerFallbackAttempts=Fallback Attempts
sebserver.clientconfig.form.sebServerFallbackAttempts.tooltip=The number of connection attempts a SEB Client is trying before switching to fallback case.
sebserver.clientconfig.form.sebServerFallbackAttemptInterval=Attempt Interval
sebserver.clientconfig.form.sebServerFallbackAttemptInterval.tooltip=The interval (in milli-seconds) between connection attempts a SEB Client shall use.
sebserver.clientconfig.form.sebServerFallbackPasswordHash=Fallback Password
sebserver.clientconfig.form.sebServerFallbackPasswordHash.tooltip=A password if set, a SEB Client user must give before the SEB Client starts the fallback procedure.
sebserver.clientconfig.form.date=Creation Date sebserver.clientconfig.form.date=Creation Date
sebserver.clientconfig.form.date.tooltip=The date when the SEB client configuration was first created. sebserver.clientconfig.form.date.tooltip=The date when the SEB client configuration was first created.
sebserver.clientconfig.form.encryptSecret=Configuration Password sebserver.clientconfig.form.encryptSecret=Configuration Password
sebserver.clientconfig.form.encryptSecret.tooltip=Define a password if the SEB client configuration shall be password-encrypted sebserver.clientconfig.form.encryptSecret.tooltip=Define a password if the SEB client configuration shall be password-encrypted
sebserver.clientconfig.form.encryptSecret.confirm=Confirm Password sebserver.clientconfig.form.encryptSecret.confirm=Confirm Password
sebserver.clientconfig.form.encryptSecret.confirm.tooltip=Please retype the given password for configrmation sebserver.clientconfig.form.encryptSecret.confirm.tooltip=Please retype the given password for confirmation
sebserver.clientconfig.form.sebConfigPurpose=Configuration Purpose
sebserver.clientconfig.form.sebConfigPurpose.tooltip=This indicates whether this client configuration shall be used to configure the SEB Client or to start an exam
sebserver.clientconfig.action.list.new=Add Configuration sebserver.clientconfig.action.list.new=Add Configuration
sebserver.clientconfig.action.list.view=View Configuration sebserver.clientconfig.action.list.view=View Configuration
@ -527,13 +629,13 @@ sebserver.clientconfig.action.deactivate=Deactivate Configuration
sebserver.examconfig.action.list=Exam Configuration sebserver.examconfig.action.list=Exam Configuration
sebserver.examconfig.list.title=Exam Configurations sebserver.examconfig.list.title=Exam Configurations
sebserver.examconfig.list.column.institution=Institution sebserver.examconfig.list.column.institution=Institution
sebserver.examconfig.list.column.institution.tooltip=The institution of the SEB exam configuration.</br></br>Use the filter above to specify the institution.</br>{0} sebserver.examconfig.list.column.institution.tooltip=The institution of the SEB exam configuration.<br/><br/>Use the filter above to specify the institution.<br/>{0}
sebserver.examconfig.list.column.name=Name sebserver.examconfig.list.column.name=Name
sebserver.examconfig.list.column.name.tooltip=The name of the SEB exam configuration.</br></br>Use the filter above to narrow down a specific name.</br>{0} sebserver.examconfig.list.column.name.tooltip=The name of the SEB exam configuration.<br/><br/>Use the filter above to narrow down a specific name.<br/>{0}
sebserver.examconfig.list.column.description=Description sebserver.examconfig.list.column.description=Description
sebserver.examconfig.list.column.description.tooltip=The description of the SEB exam configuration.</br></br>Use the filter above to find configurations that contains specific words or phrases within the description.</br>{0} sebserver.examconfig.list.column.description.tooltip=The description of the SEB exam configuration.<br/><br/>Use the filter above to find configurations that contains specific words or phrases within the description.<br/>{0}
sebserver.examconfig.list.column.status=Status sebserver.examconfig.list.column.status=Status
sebserver.examconfig.list.column.status.tooltip=The status of the SEB exam configuration.</br></br>Use the filter above to specify a status.</br>{0} sebserver.examconfig.list.column.status.tooltip=The status of the SEB exam configuration.<br/><br/>Use the filter above to specify a status.<br/>{0}
sebserver.examconfig.list.actions= sebserver.examconfig.list.actions=
@ -577,10 +679,10 @@ sebserver.examconfig.form.with-history=With History
sebserver.examconfig.form.template=Template sebserver.examconfig.form.template=Template
sebserver.examconfig.form.template.tooltip=The template this SEB exam configuration depends on. sebserver.examconfig.form.template.tooltip=The template this SEB exam configuration depends on.
sebserver.examconfig.form.status=Status sebserver.examconfig.form.status=Status
sebserver.examconfig.form.status.tooltip=The status of this SEB exam configuration.</br></br>Under Construction marks a SEB exam configuration to not be able to attach to an exam so far.</br></br>Ready to use marks an SEB exam configuration to be able to attach to an exam.</br></br>In Use marks a SEB exam configuration is already been used from one or more exam(s) sebserver.examconfig.form.status.tooltip=The status of this SEB exam configuration.<br/><br/>Under Construction marks a SEB exam configuration to not be able to attach to an exam so far.<br/><br/>Ready to use marks an SEB exam configuration to be able to attach to an exam.<br/><br/>In Use marks a SEB exam configuration is already been used from one or more exam(s)
sebserver.examconfig.form.config-key.title=Config Key sebserver.examconfig.form.config-key.title=Config Key
sebserver.examconfig.form.attached-to=Attached To Exam sebserver.examconfig.form.attached-to=Attached To Exam
sebserver.examconfig.form.attached-to.tooltip=This SEB exam configuration is currently attached to the following exams.</br></br>Select an exam from the list and use the "View Exam" or Double-Click on the list to go to a specific exam. sebserver.examconfig.form.attached-to.tooltip=This SEB exam configuration is currently attached to the following exams.<br/><br/>Select an exam from the list and use the "View Exam" or Double-Click on the list to go to a specific exam.
sebserver.examconfig.status.CONSTRUCTION=Under Construction sebserver.examconfig.status.CONSTRUCTION=Under Construction
sebserver.examconfig.status.READY_TO_USE=Ready To Use sebserver.examconfig.status.READY_TO_USE=Ready To Use
@ -603,9 +705,9 @@ sebserver.examconfig.props.form.views.hooked_keys=Hooked Keys
sebserver.examconfig.props.label.hashedAdminPassword=Administrator password sebserver.examconfig.props.label.hashedAdminPassword=Administrator password
sebserver.examconfig.props.label.hashedAdminPassword.confirm=Confirm password sebserver.examconfig.props.label.hashedAdminPassword.confirm=Confirm password
sebserver.examconfig.props.label.allowQuit=Allow user to quit SEB sebserver.examconfig.props.label.allowQuit=Allow user to quit SEB
sebserver.examconfig.props.label.allowQuit.tooltip=Users can quit SEB with Control-Q, window close or quit button.</br>Otherwise use a quit link in your exam system or shutdown/restart the computer. sebserver.examconfig.props.label.allowQuit.tooltip=Users can quit SEB with Control-Q, window close or quit button.<br/>Otherwise use a quit link in your exam system or shutdown/restart the computer.
sebserver.examconfig.props.label.ignoreExitKeys=Ignore exit keys sebserver.examconfig.props.label.ignoreExitKeys=Ignore exit keys
sebserver.examconfig.props.label.ignoreExitKeys.tooltip=SEB ignores the exit keys and can only be quit manually by entering the quit password.</br>(click Quit button in SEB taskbar, press Ctrl-Q or click the main browser window close button) sebserver.examconfig.props.label.ignoreExitKeys.tooltip=SEB ignores the exit keys and can only be quit manually by entering the quit password.<br/>(click Quit button in SEB taskbar, press Ctrl-Q or click the main browser window close button)
sebserver.examconfig.props.label.hashedQuitPassword=Quit/unlock password sebserver.examconfig.props.label.hashedQuitPassword=Quit/unlock password
sebserver.examconfig.props.label.hashedQuitPassword.confirm=Confirm password sebserver.examconfig.props.label.hashedQuitPassword.confirm=Confirm password
sebserver.examconfig.props.group.exitSequence=Exit Sequence sebserver.examconfig.props.group.exitSequence=Exit Sequence
@ -645,15 +747,15 @@ sebserver.examconfig.props.label.mainBrowserWindowPositioning.2=Right
sebserver.examconfig.props.group.wintoolbar=Browser Window Toolbar sebserver.examconfig.props.group.wintoolbar=Browser Window Toolbar
sebserver.examconfig.props.label.enableBrowserWindowToolbar=Enable browser window toolbar sebserver.examconfig.props.label.enableBrowserWindowToolbar=Enable browser window toolbar
sebserver.examconfig.props.label.enableBrowserWindowToolbar.tooltip=Displays a toolbar on top of the browser window</br>which can also be hidden by the user. sebserver.examconfig.props.label.enableBrowserWindowToolbar.tooltip=Displays a toolbar on top of the browser window<br/>which can also be hidden by the user.
sebserver.examconfig.props.label.hideBrowserWindowToolbar=Hide toolbar as default (Mac) sebserver.examconfig.props.label.hideBrowserWindowToolbar=Hide toolbar as default (Mac)
sebserver.examconfig.props.label.hideBrowserWindowToolbar.tooltip=Hide browser window toolbar by default.</br>It can be shown again by using the View menu or Alt-Command-T. sebserver.examconfig.props.label.hideBrowserWindowToolbar.tooltip=Hide browser window toolbar by default.<br/>It can be shown again by using the View menu or Alt-Command-T.
sebserver.examconfig.props.label.showMenuBar=Show menu bar (Mac) sebserver.examconfig.props.label.showMenuBar=Show menu bar (Mac)
sebserver.examconfig.props.label.showMenuBar.tooltip=Show the OS X menu bar to allow to access settings like Wi-Fi. sebserver.examconfig.props.label.showMenuBar.tooltip=Show the OS X menu bar to allow to access settings like Wi-Fi.
sebserver.examconfig.props.group.taskbar=SEB Taskbar/Dock sebserver.examconfig.props.group.taskbar=SEB Taskbar/Dock
sebserver.examconfig.props.label.showTaskBar=Show SEB taskbar sebserver.examconfig.props.label.showTaskBar=Show SEB taskbar
sebserver.examconfig.props.label.showTaskBar.tooltip=The SEB task bar shows and switches between open browser windows,</br> allowed resources and applications and displays additional controls sebserver.examconfig.props.label.showTaskBar.tooltip=The SEB task bar shows and switches between open browser windows,<br/> allowed resources and applications and displays additional controls
sebserver.examconfig.props.label.taskBarHeight=Taskbar/dock height sebserver.examconfig.props.label.taskBarHeight=Taskbar/dock height
sebserver.examconfig.props.label.taskBarHeight.tooltip=Height of SEB dock/task bar in points/pixels sebserver.examconfig.props.label.taskBarHeight.tooltip=Height of SEB dock/task bar in points/pixels
sebserver.examconfig.props.label.showReloadButton=Show reload button sebserver.examconfig.props.label.showReloadButton=Show reload button
@ -665,9 +767,9 @@ sebserver.examconfig.props.label.showInputLanguage.tooltip=Shows current keyboar
sebserver.examconfig.props.group.zoom=Enable Zoom (Win/Mac) sebserver.examconfig.props.group.zoom=Enable Zoom (Win/Mac)
sebserver.examconfig.props.label.enableZoomPage=Enable page zoom sebserver.examconfig.props.label.enableZoomPage=Enable page zoom
sebserver.examconfig.props.label.enableZoomPage.tooltip=Pages can be zoomed with ctrl - cmd +/-</br> or the commands in the view menu and browser window toolbar (Mac) sebserver.examconfig.props.label.enableZoomPage.tooltip=Pages can be zoomed with ctrl - cmd +/-<br/> or the commands in the view menu and browser window toolbar (Mac)
sebserver.examconfig.props.label.enableZoomText=Enable text zoom sebserver.examconfig.props.label.enableZoomText=Enable text zoom
sebserver.examconfig.props.label.enableZoomText.tooltip=Text in browser windows can be zoomed with cmd +/-</br> or the commands in the view menu and browser window toolbar (Mac) sebserver.examconfig.props.label.enableZoomText.tooltip=Text in browser windows can be zoomed with cmd +/-<br/> or the commands in the view menu and browser window toolbar (Mac)
sebserver.examconfig.props.group.zoomMode=Zoom Mode Win (Ctrl-Mousewheel) sebserver.examconfig.props.group.zoomMode=Zoom Mode Win (Ctrl-Mousewheel)
sebserver.examconfig.props.label.zoomMode.0=Use page zoom sebserver.examconfig.props.label.zoomMode.0=Use page zoom
sebserver.examconfig.props.label.zoomMode.0.tooltip=Zoom whole web pages using Ctrl-Mousewheel (Win)" sebserver.examconfig.props.label.zoomMode.0.tooltip=Zoom whole web pages using Ctrl-Mousewheel (Win)"
@ -687,7 +789,7 @@ sebserver.examconfig.props.label.allowSpellCheck=Allow spell checking
sebserver.examconfig.props.label.allowSpellCheck.tooltip=Allow to use "Check spelling" in the SEB browser sebserver.examconfig.props.label.allowSpellCheck.tooltip=Allow to use "Check spelling" in the SEB browser
sebserver.examconfig.props.label.allowDictionaryLookup=Allow dictionary lookup (Mac) sebserver.examconfig.props.label.allowDictionaryLookup=Allow dictionary lookup (Mac)
sebserver.examconfig.props.label.allowDictionaryLookup.tooltip=Allow to use the OS X dictionary lookup using a 3 finger tap sebserver.examconfig.props.label.allowDictionaryLookup.tooltip=Allow to use the OS X dictionary lookup using a 3 finger tap
sebserver.examconfig.props.label.allowSpellCheckDictionary=The list below shows all dictionaries currently available for spell checking.</br>SEB comes with a list of standard dictionaries that can be activated/deactivated here. sebserver.examconfig.props.label.allowSpellCheckDictionary=The list below shows all dictionaries currently available for spell checking.<br/>SEB comes with a list of standard dictionaries that can be activated/deactivated here.
sebserver.examconfig.props.label.allowSpellCheckDictionary.da-DK=Danish (Denmark) (da-DK) sebserver.examconfig.props.label.allowSpellCheckDictionary.da-DK=Danish (Denmark) (da-DK)
sebserver.examconfig.props.label.allowSpellCheckDictionary.en-AU=English (Australia) (en-AU) sebserver.examconfig.props.label.allowSpellCheckDictionary.en-AU=English (Australia) (en-AU)
sebserver.examconfig.props.label.allowSpellCheckDictionary.en-GB=English (United Kingdom) (en-GB) sebserver.examconfig.props.label.allowSpellCheckDictionary.en-GB=English (United Kingdom) (en-GB)
@ -702,8 +804,8 @@ sebserver.examconfig.props.group.newBrowserWindow=Links requesting to be opened
sebserver.examconfig.props.label.newBrowserWindowByLinkPolicy.0=get generally blocked sebserver.examconfig.props.label.newBrowserWindowByLinkPolicy.0=get generally blocked
sebserver.examconfig.props.label.newBrowserWindowByLinkPolicy.1=open in same window sebserver.examconfig.props.label.newBrowserWindowByLinkPolicy.1=open in same window
sebserver.examconfig.props.label.newBrowserWindowByLinkPolicy.2=open in new window sebserver.examconfig.props.label.newBrowserWindowByLinkPolicy.2=open in new window
sebserver.examconfig.props.label.newBrowserWindowByLinkBlockForeign=Block when directing</br>to a different server sebserver.examconfig.props.label.newBrowserWindowByLinkBlockForeign=Block when directing<br/>to a different server
sebserver.examconfig.props.label.newBrowserWindowByLinkBlockForeign.tooltip=USE WITH CARE: Hyperlinks invoked by JavaScript/plug-ins</br> which direct to a different host than the one of the current main page will be ignored. sebserver.examconfig.props.label.newBrowserWindowByLinkBlockForeign.tooltip=USE WITH CARE: Hyperlinks invoked by JavaScript/plug-ins<br/> which direct to a different host than the one of the current main page will be ignored.
sebserver.examconfig.props.group.newwinsize=New browser window size and position sebserver.examconfig.props.group.newwinsize=New browser window size and position
sebserver.examconfig.props.label.newBrowserWindowByLinkWidth=Width sebserver.examconfig.props.label.newBrowserWindowByLinkWidth=Width
@ -717,19 +819,19 @@ sebserver.examconfig.props.label.newBrowserWindowByLinkPositioning.2=Right
sebserver.examconfig.props.group.browserSecurity=Browser security sebserver.examconfig.props.group.browserSecurity=Browser security
sebserver.examconfig.props.label.enablePlugIns=Enable plug-ins (Win: only Flash) sebserver.examconfig.props.label.enablePlugIns=Enable plug-ins (Win: only Flash)
sebserver.examconfig.props.label.enablePlugIns.tooltip=Enables web plugins (Mac) or just Flash (Win).</br> For security reasons it\'s recommended to disable this option if you don\'t use any plugin/Flash content. sebserver.examconfig.props.label.enablePlugIns.tooltip=Enables web plugins (Mac) or just Flash (Win).<br/> For security reasons it\'s recommended to disable this option if you don\'t use any plugin/Flash content.
sebserver.examconfig.props.label.enableJavaScript=Enable JavaScript sebserver.examconfig.props.label.enableJavaScript=Enable JavaScript
sebserver.examconfig.props.label.enableJavaScript.tooltip=Enables JavaScript.</br> Please note that most modern web-sites need JavaScript for full functionality. sebserver.examconfig.props.label.enableJavaScript.tooltip=Enables JavaScript.<br/> Please note that most modern web-sites need JavaScript for full functionality.
sebserver.examconfig.props.label.enableJava=Enable Java sebserver.examconfig.props.label.enableJava=Enable Java
sebserver.examconfig.props.label.enableJava.tooltip=Enables Java applets.</br> Note: Only applets with the highest Java security level will run in SEB. sebserver.examconfig.props.label.enableJava.tooltip=Enables Java applets.<br/> Note: Only applets with the highest Java security level will run in SEB.
sebserver.examconfig.props.label.blockPopUpWindows=Block pop-up windows sebserver.examconfig.props.label.blockPopUpWindows=Block pop-up windows
sebserver.examconfig.props.label.blockPopUpWindows.tooltip=Disables pop-up windows</br> (often advertisement) opened by JavaScript without an user action such as a button click. sebserver.examconfig.props.label.blockPopUpWindows.tooltip=Disables pop-up windows<br/> (often advertisement) opened by JavaScript without an user action such as a button click.
sebserver.examconfig.props.label.allowVideoCapture=Allow video capture (webcam) sebserver.examconfig.props.label.allowVideoCapture=Allow video capture (webcam)
sebserver.examconfig.props.label.allowVideoCapture.tooltip=Allow web applications to access camera sebserver.examconfig.props.label.allowVideoCapture.tooltip=Allow web applications to access camera
sebserver.examconfig.props.label.allowAudioCapture=Allow audio capture (microphone) sebserver.examconfig.props.label.allowAudioCapture=Allow audio capture (microphone)
sebserver.examconfig.props.label.allowAudioCapture.tooltip=Allow web applications to access microphone sebserver.examconfig.props.label.allowAudioCapture.tooltip=Allow web applications to access microphone
sebserver.examconfig.props.label.allowBrowsingBackForward=Allow navigating back/forward in exam sebserver.examconfig.props.label.allowBrowsingBackForward=Allow navigating back/forward in exam
sebserver.examconfig.props.label.allowBrowsingBackForward.tooltip=Disabling browsing to previously visited pages may increase security,</br> because browsing back might allow to leave an exam sebserver.examconfig.props.label.allowBrowsingBackForward.tooltip=Disabling browsing to previously visited pages may increase security,<br/> because browsing back might allow to leave an exam
sebserver.examconfig.props.label.newBrowserWindowNavigation=Allow navigating in additional windows sebserver.examconfig.props.label.newBrowserWindowNavigation=Allow navigating in additional windows
sebserver.examconfig.props.label.browserWindowAllowReload=Allow reload exam sebserver.examconfig.props.label.browserWindowAllowReload=Allow reload exam
sebserver.examconfig.props.label.browserWindowAllowReload.tooltip=Allow reload in the exam window with F5 reload button (if displayed) sebserver.examconfig.props.label.browserWindowAllowReload.tooltip=Allow reload in the exam window with F5 reload button (if displayed)
@ -742,7 +844,7 @@ sebserver.examconfig.props.label.newBrowserWindowShowReloadWarning.tooltip=User
sebserver.examconfig.props.label.removeBrowserProfile=Remove profile (Win) sebserver.examconfig.props.label.removeBrowserProfile=Remove profile (Win)
sebserver.examconfig.props.label.removeBrowserProfile.tooltip=Remove XULRunner browser profile (containing caches and also local storage) when quitting SEB sebserver.examconfig.props.label.removeBrowserProfile.tooltip=Remove XULRunner browser profile (containing caches and also local storage) when quitting SEB
sebserver.examconfig.props.label.removeLocalStorage=Disable local storage (Mac) sebserver.examconfig.props.label.removeLocalStorage=Disable local storage (Mac)
sebserver.examconfig.props.label.removeLocalStorage.tooltip=If your web application uses local storage, you have to be sure data is saved encrypted</br> and removed when no longer needed as SEB doesn't remove local storage sebserver.examconfig.props.label.removeLocalStorage.tooltip=If your web application uses local storage, you have to be sure data is saved encrypted<br/> and removed when no longer needed as SEB doesn't remove local storage
sebserver.examconfig.props.label.browserUserAgent=Suffix to be added to any user agent sebserver.examconfig.props.label.browserUserAgent=Suffix to be added to any user agent
sebserver.examconfig.props.group.userAgentDesktop=User agent for desktop mode sebserver.examconfig.props.group.userAgentDesktop=User agent for desktop mode
@ -750,7 +852,7 @@ sebserver.examconfig.props.label.browserUserAgentWinDesktopMode.0=Desktop defaul
sebserver.examconfig.props.label.browserUserAgentWinDesktopMode.0.tooltip=Zoom whole web pages using Ctrl-Mousewheel (Win) sebserver.examconfig.props.label.browserUserAgentWinDesktopMode.0.tooltip=Zoom whole web pages using Ctrl-Mousewheel (Win)
sebserver.examconfig.props.label.browserUserAgentWinDesktopMode.1=Custom sebserver.examconfig.props.label.browserUserAgentWinDesktopMode.1=Custom
sebserver.examconfig.props.label.browserUserAgentWinDesktopMode.1.tooltip=Zoom only text on web pages using Ctrl-Mousewheel (Win) sebserver.examconfig.props.label.browserUserAgentWinDesktopMode.1.tooltip=Zoom only text on web pages using Ctrl-Mousewheel (Win)
sebserver.examconfig.props.label.browserUserAgentWinDesktopModeCustom.tooltip=Custom desktop user agent string</br>(SEB appends its version number automatically) sebserver.examconfig.props.label.browserUserAgentWinDesktopModeCustom.tooltip=Custom desktop user agent string<br/>(SEB appends its version number automatically)
sebserver.examconfig.props.group.userAgentTouch=User agent for touch/table mode sebserver.examconfig.props.group.userAgentTouch=User agent for touch/table mode
sebserver.examconfig.props.label.browserUserAgentWinTouchMode.0=Touch default sebserver.examconfig.props.label.browserUserAgentWinTouchMode.0=Touch default
@ -764,28 +866,28 @@ sebserver.examconfig.props.label.browserUserAgentMac.1=Custom
sebserver.examconfig.props.label.browserUserAgentMac.1.tooltip=Zoom only text on web pages using Ctrl-Mousewheel (Win) sebserver.examconfig.props.label.browserUserAgentMac.1.tooltip=Zoom only text on web pages using Ctrl-Mousewheel (Win)
sebserver.examconfig.props.label.enableSebBrowser=Enable SEB with browser window sebserver.examconfig.props.label.enableSebBrowser=Enable SEB with browser window
sebserver.examconfig.props.label.enableSebBrowser.tooltip=Disable this to start another application in kiosk mode</br>(for example a virtual desktop infrastructure client) sebserver.examconfig.props.label.enableSebBrowser.tooltip=Disable this to start another application in kiosk mode<br/>(for example a virtual desktop infrastructure client)
sebserver.examconfig.props.label.browserWindowTitleSuffix=Suffix to be added to every browser window sebserver.examconfig.props.label.browserWindowTitleSuffix=Suffix to be added to every browser window
sebserver.examconfig.props.label.allowDownUploads=Allow downloading and uploading files (Mac) sebserver.examconfig.props.label.allowDownUploads=Allow downloading and uploading files (Mac)
sebserver.examconfig.props.label.allowDownUpload.tooltip=Usually to be used with permitted third party applications</br> for which you want to provide files to be down-loaded. sebserver.examconfig.props.label.allowDownUpload.tooltip=Usually to be used with permitted third party applications<br/> for which you want to provide files to be down-loaded.
sebserver.examconfig.props.label.downloadDirectoryWin=Download directory (Win) sebserver.examconfig.props.label.downloadDirectoryWin=Download directory (Win)
sebserver.examconfig.props.label.downloadDirectoryOSX=Download directory (Mac) sebserver.examconfig.props.label.downloadDirectoryOSX=Download directory (Mac)
sebserver.examconfig.props.label.openDownloads=Open files after downloading (Mac) sebserver.examconfig.props.label.openDownloads=Open files after downloading (Mac)
sebserver.examconfig.props.label.chooseFileToUploadPolicy=Choose file to upload (Mac) sebserver.examconfig.props.label.chooseFileToUploadPolicy=Choose file to upload (Mac)
sebserver.examconfig.props.label.chooseFileToUploadPolicy.tooltip=SEB can let users choose the file to upload or automatically use the same file which was down-loaded before.</br>If not found, a file requester or an error is presented depending on this setting. sebserver.examconfig.props.label.chooseFileToUploadPolicy.tooltip=SEB can let users choose the file to upload or automatically use the same file which was down-loaded before.<br/>If not found, a file requester or an error is presented depending on this setting.
sebserver.examconfig.props.label.chooseFileToUploadPolicy.0=manually with file requester sebserver.examconfig.props.label.chooseFileToUploadPolicy.0=manually with file requester
sebserver.examconfig.props.label.chooseFileToUploadPolicy.1=by attempting to upload the same file downloaded before sebserver.examconfig.props.label.chooseFileToUploadPolicy.1=by attempting to upload the same file downloaded before
sebserver.examconfig.props.label.chooseFileToUploadPolicy.2=by only allowing to upload the same file downloaded before sebserver.examconfig.props.label.chooseFileToUploadPolicy.2=by only allowing to upload the same file downloaded before
sebserver.examconfig.props.label.downloadPDFFiles=Download and open PDF files instead of displaying them inline (Mac) sebserver.examconfig.props.label.downloadPDFFiles=Download and open PDF files instead of displaying them inline (Mac)
sebserver.examconfig.props.label.downloadPDFFiles.tooltip=PDF files will not be displayed by SEB but downloaded and openend (if "Open files after downloading" is active!)</br> by the application set in Finder (usually Preview or Adobe Acrobat). sebserver.examconfig.props.label.downloadPDFFiles.tooltip=PDF files will not be displayed by SEB but downloaded and openend (if "Open files after downloading" is active!)<br/> by the application set in Finder (usually Preview or Adobe Acrobat).
sebserver.examconfig.props.label.allowPDFPlugIn=Allow using Acrobat Reader PDF plugin (insecure! Mac only) sebserver.examconfig.props.label.allowPDFPlugIn=Allow using Acrobat Reader PDF plugin (insecure! Mac only)
sebserver.examconfig.props.label.allowPDFPlugIn.tooltip=The Adobe Acrobat Reader browser plugin should only be used on secured managed Mac computers,</br> at it allows limited access the file system and unlimited to cloud services sebserver.examconfig.props.label.allowPDFPlugIn.tooltip=The Adobe Acrobat Reader browser plugin should only be used on secured managed Mac computers,<br/> at it allows limited access the file system and unlimited to cloud services
sebserver.examconfig.props.label.downloadAndOpenSebConfig=Download and open SEB Config Files sebserver.examconfig.props.label.downloadAndOpenSebConfig=Download and open SEB Config Files
sebserver.examconfig.props.label.downloadAndOpenSebConfig.tooltip=Download and open .seb config files regardless if downloading and opening other file types is allowed. sebserver.examconfig.props.label.downloadAndOpenSebConfig.tooltip=Download and open .seb config files regardless if downloading and opening other file types is allowed.
sebserver.examconfig.props.group.quitLink=Link to quit SEB after exam sebserver.examconfig.props.group.quitLink=Link to quit SEB after exam
sebserver.examconfig.props.label.quitURL=Place this quit link to the 'feedback' page displayed after an exam was successfully finished.</br> Clicking that link will quit SEB without having to enter the quit password. sebserver.examconfig.props.label.quitURL=Place this quit link to the 'feedback' page displayed after an exam was successfully finished.<br/> Clicking that link will quit SEB without having to enter the quit password.
sebserver.examconfig.props.label.quitURLConfirm=Ask user to confirm quitting sebserver.examconfig.props.label.quitURLConfirm=Ask user to confirm quitting
sebserver.examconfig.props.group.backToStart=Back to Start Button sebserver.examconfig.props.group.backToStart=Back to Start Button
@ -798,7 +900,7 @@ sebserver.examconfig.props.label.restartExamPasswordProtected=Protect back to st
sebserver.examconfig.props.label.restartExamPasswordProtected.tooltip=The quit/restart password (if set) must be entered when the back to start button was pressed. sebserver.examconfig.props.label.restartExamPasswordProtected.tooltip=The quit/restart password (if set) must be entered when the back to start button was pressed.
sebserver.examconfig.props.label.allowSwitchToApplications=Allow switching to third party application (Mac) sebserver.examconfig.props.label.allowSwitchToApplications=Allow switching to third party application (Mac)
sebserver.examconfig.props.label.allowSwitchToApplications.tooltip=Decreases security of the kiosk mode by allowing process switcher (Cmd+Tab).</br> The blacked out background of SEB also doesn't cover some alerts and modal windows in this mode. sebserver.examconfig.props.label.allowSwitchToApplications.tooltip=Decreases security of the kiosk mode by allowing process switcher (Cmd+Tab).<br/> The blacked out background of SEB also doesn't cover some alerts and modal windows in this mode.
sebserver.examconfig.props.label.allowFlashFullscreen=Allow Flash to switch to fullscreen mode (Mac) sebserver.examconfig.props.label.allowFlashFullscreen=Allow Flash to switch to fullscreen mode (Mac)
sebserver.examconfig.props.label.permittedProcesses.add.tooltip=Add permitted process sebserver.examconfig.props.label.permittedProcesses.add.tooltip=Add permitted process
sebserver.examconfig.props.label.permittedProcesses.remove.tooltip=Remove selected permitted process sebserver.examconfig.props.label.permittedProcesses.remove.tooltip=Remove selected permitted process
@ -811,11 +913,11 @@ sebserver.examconfig.props.label.permittedProcesses.os.tooltip=Indicates on whic
sebserver.examconfig.props.label.permittedProcesses.os.0=OS X sebserver.examconfig.props.label.permittedProcesses.os.0=OS X
sebserver.examconfig.props.label.permittedProcesses.os.1=Win sebserver.examconfig.props.label.permittedProcesses.os.1=Win
sebserver.examconfig.props.label.permittedProcesses.title=Title sebserver.examconfig.props.label.permittedProcesses.title=Title
sebserver.examconfig.props.label.permittedProcesses.title.tooltip=Application title which is displayed in the application chooser.</br> Background processes don't have a title, because they can't be selected by users. sebserver.examconfig.props.label.permittedProcesses.title.tooltip=Application title which is displayed in the application chooser.<br/> Background processes don't have a title, because they can't be selected by users.
sebserver.examconfig.props.label.permittedProcesses.description=Description sebserver.examconfig.props.label.permittedProcesses.description=Description
sebserver.examconfig.props.label.permittedProcesses.description.tooltip=Optional, should explain what kind of process this is,</br> because this might not be obvious only from the executable's name. sebserver.examconfig.props.label.permittedProcesses.description.tooltip=Optional, should explain what kind of process this is,<br/> because this might not be obvious only from the executable's name.
sebserver.examconfig.props.label.permittedProcesses.executable=Executable sebserver.examconfig.props.label.permittedProcesses.executable=Executable
sebserver.examconfig.props.label.permittedProcesses.executable.tooltip=File name of the executable, which should not contain any parts of a file system path,</br> only the filename of the exe file (like calc.exe). sebserver.examconfig.props.label.permittedProcesses.executable.tooltip=File name of the executable, which should not contain any parts of a file system path,<br/> only the filename of the exe file (like calc.exe).
sebserver.examconfig.props.label.permittedProcesses.originalName=Original Name sebserver.examconfig.props.label.permittedProcesses.originalName=Original Name
sebserver.examconfig.props.label.permittedProcesses.allowedExecutables=Window handling process sebserver.examconfig.props.label.permittedProcesses.allowedExecutables=Window handling process
sebserver.examconfig.props.label.permittedProcesses.path=Path sebserver.examconfig.props.label.permittedProcesses.path=Path
@ -825,13 +927,13 @@ sebserver.examconfig.props.label.permittedProcesses.arguments.argument=Argument
sebserver.examconfig.props.label.permittedProcesses.arguments.addAction=Add new argument sebserver.examconfig.props.label.permittedProcesses.arguments.addAction=Add new argument
sebserver.examconfig.props.label.permittedProcesses.arguments.removeAction=Remove this argument sebserver.examconfig.props.label.permittedProcesses.arguments.removeAction=Remove this argument
sebserver.examconfig.props.label.permittedProcesses.identifier=Identifier sebserver.examconfig.props.label.permittedProcesses.identifier=Identifier
sebserver.examconfig.props.label.permittedProcesses.identifier.tooltip=(Sub) string in the title of the main window of a tricky third party application (Java, Acrobat etc.).</br> Mac OS X: Bundle identifier of the process in reverse domain notation. sebserver.examconfig.props.label.permittedProcesses.identifier.tooltip=(Sub) string in the title of the main window of a tricky third party application (Java, Acrobat etc.).<br/> Mac OS X: Bundle identifier of the process in reverse domain notation.
sebserver.examconfig.props.label.permittedProcesses.iconInTaskbar=Icon in taskbar sebserver.examconfig.props.label.permittedProcesses.iconInTaskbar=Icon in taskbar
sebserver.examconfig.props.label.permittedProcesses.iconInTaskbar.tooltip=Show icon of permitted application in task bar</br> (not possible when 'run in background' is enabled). sebserver.examconfig.props.label.permittedProcesses.iconInTaskbar.tooltip=Show icon of permitted application in task bar<br/> (not possible when 'run in background' is enabled).
sebserver.examconfig.props.label.permittedProcesses.autostart=Autostart sebserver.examconfig.props.label.permittedProcesses.autostart=Autostart
sebserver.examconfig.props.label.permittedProcesses.autostart.tooltip=Start the process automatically together with SEB. sebserver.examconfig.props.label.permittedProcesses.autostart.tooltip=Start the process automatically together with SEB.
sebserver.examconfig.props.label.permittedProcesses.runInBackground=Allow running in background sebserver.examconfig.props.label.permittedProcesses.runInBackground=Allow running in background
sebserver.examconfig.props.label.permittedProcesses.runInBackground.tooltip=Allow the permitted process to already be running when SEB starts.</br> Such a process can't have an icon in the task bar. sebserver.examconfig.props.label.permittedProcesses.runInBackground.tooltip=Allow the permitted process to already be running when SEB starts.<br/> Such a process can't have an icon in the task bar.
sebserver.examconfig.props.label.permittedProcesses.allowUserToChooseApp=Allow user to select location of application sebserver.examconfig.props.label.permittedProcesses.allowUserToChooseApp=Allow user to select location of application
sebserver.examconfig.props.label.permittedProcesses.strongKill=Force quit (risk of data loss) sebserver.examconfig.props.label.permittedProcesses.strongKill=Force quit (risk of data loss)
sebserver.examconfig.props.label.permittedProcesses.strongKill.tooltip=Terminate process in a not-nice way, which may cause data loss if the application had unsaved data sebserver.examconfig.props.label.permittedProcesses.strongKill.tooltip=Terminate process in a not-nice way, which may cause data loss if the application had unsaved data
@ -846,15 +948,15 @@ sebserver.examconfig.props.label.prohibitedProcesses.os=OS
sebserver.examconfig.props.label.prohibitedProcesses.os.0=OS X sebserver.examconfig.props.label.prohibitedProcesses.os.0=OS X
sebserver.examconfig.props.label.prohibitedProcesses.os.1=Win sebserver.examconfig.props.label.prohibitedProcesses.os.1=Win
sebserver.examconfig.props.label.prohibitedProcesses.description=Description sebserver.examconfig.props.label.prohibitedProcesses.description=Description
sebserver.examconfig.props.label.prohibitedProcesses.description.tooltip=Optional, to explain what kind of process this is,</br> because this might not be obvious only from the executable's name. sebserver.examconfig.props.label.prohibitedProcesses.description.tooltip=Optional, to explain what kind of process this is,<br/> because this might not be obvious only from the executable's name.
sebserver.examconfig.props.label.prohibitedProcesses.executable=Executable sebserver.examconfig.props.label.prohibitedProcesses.executable=Executable
sebserver.examconfig.props.label.prohibitedProcesses.executable.tooltip=File name of the executable, which should not contain any parts of a file system path,</br> only the filename of the exe file (like calc.exe). sebserver.examconfig.props.label.prohibitedProcesses.executable.tooltip=File name of the executable, which should not contain any parts of a file system path,<br/> only the filename of the exe file (like calc.exe).
sebserver.examconfig.props.label.prohibitedProcesses.originalName=Original Name sebserver.examconfig.props.label.prohibitedProcesses.originalName=Original Name
sebserver.examconfig.props.label.prohibitedProcesses.originalName.tooltip=Original file name (optional) sebserver.examconfig.props.label.prohibitedProcesses.originalName.tooltip=Original file name (optional)
sebserver.examconfig.props.label.prohibitedProcesses.identifier=Identifier sebserver.examconfig.props.label.prohibitedProcesses.identifier=Identifier
sebserver.examconfig.props.label.prohibitedProcesses.identifier.tooltip=Title of the main window of a Java third party application.</br> Mac OS X: Bundle identifier of the process in reverse domain notation. sebserver.examconfig.props.label.prohibitedProcesses.identifier.tooltip=Title of the main window of a Java third party application.<br/> Mac OS X: Bundle identifier of the process in reverse domain notation.
sebserver.examconfig.props.label.prohibitedProcesses.strongKill=Force quit (risk of data loss) sebserver.examconfig.props.label.prohibitedProcesses.strongKill=Force quit (risk of data loss)
sebserver.examconfig.props.label.prohibitedProcesses.strongKill.tooltip=Terminate process in a not-nice way,</br> which may cause data loss if the application had unsaved data sebserver.examconfig.props.label.prohibitedProcesses.strongKill.tooltip=Terminate process in a not-nice way,<br/> which may cause data loss if the application had unsaved data
sebserver.examconfig.props.group.urlFilter=Filter sebserver.examconfig.props.group.urlFilter=Filter
sebserver.examconfig.props.label.URLFilterEnable=Activate URL Filtering sebserver.examconfig.props.label.URLFilterEnable=Activate URL Filtering
@ -1014,7 +1116,7 @@ sebserver.examconfig.props.label.insideSebEnableLogOff.tooltip=Activates the but
sebserver.examconfig.props.label.insideSebEnableShutDown=Enable Shut down sebserver.examconfig.props.label.insideSebEnableShutDown=Enable Shut down
sebserver.examconfig.props.label.insideSebEnableShutDown.tooltip=Activates the button "Shutdown" sebserver.examconfig.props.label.insideSebEnableShutDown.tooltip=Activates the button "Shutdown"
sebserver.examconfig.props.label.insideSebEnableEaseOfAccess=Enable Ease of Access sebserver.examconfig.props.label.insideSebEnableEaseOfAccess=Enable Ease of Access
sebserver.examconfig.props.label.insideSebEnableEaseOfAccess.tooltip=Shows options when the button "Ease of Access" in the lower left corner is clicked,</br>which offers help e.g. to visually or aurally handicapped persons, like the Magnifier Glass. sebserver.examconfig.props.label.insideSebEnableEaseOfAccess.tooltip=Shows options when the button "Ease of Access" in the lower left corner is clicked,<br/>which offers help e.g. to visually or aurally handicapped persons, like the Magnifier Glass.
sebserver.examconfig.props.label.insideSebEnableVmWareClientShade=Enable VMware Client Shade sebserver.examconfig.props.label.insideSebEnableVmWareClientShade=Enable VMware Client Shade
sebserver.examconfig.props.label.insideSebEnableVmWareClientShade.tooltip=Activates the "Shade" bar at the upper edge of a virtual desktop, if existent. If you're not using VMware, this setting doesn't have any effect. sebserver.examconfig.props.label.insideSebEnableVmWareClientShade.tooltip=Activates the "Shade" bar at the upper edge of a virtual desktop, if existent. If you're not using VMware, this setting doesn't have any effect.
sebserver.examconfig.props.label.insideSebEnableNetworkConnectionSelector=Enable network connection selector sebserver.examconfig.props.label.insideSebEnableNetworkConnectionSelector=Enable network connection selector
@ -1103,13 +1205,13 @@ sebserver.configtemplate.attr.type.COMPOSITE_TABLE=Table
sebserver.configtemplate.attrs.list.title=Configuration Attributes sebserver.configtemplate.attrs.list.title=Configuration Attributes
sebserver.configtemplate.attrs.list.title.tooltip=Table of all SEB exam configuration attributes of this template sebserver.configtemplate.attrs.list.title.tooltip=Table of all SEB exam configuration attributes of this template
sebserver.configtemplate.attrs.list.name=Name sebserver.configtemplate.attrs.list.name=Name
sebserver.configtemplate.attrs.list.name.tooltip=The technical name of the SEB exam configuration attribute with the display name in brackets if available.</br></br>{0} sebserver.configtemplate.attrs.list.name.tooltip=The technical name of the SEB exam configuration attribute with the display name in brackets if available.<br/><br/>{0}
sebserver.configtemplate.attrs.list.view=View sebserver.configtemplate.attrs.list.view=View
sebserver.configtemplate.attrs.list.view.tooltip=The view/tab where the SEB exam configuration attribute belongs to.</br></br>{0} sebserver.configtemplate.attrs.list.view.tooltip=The view/tab where the SEB exam configuration attribute belongs to.<br/><br/>{0}
sebserver.configtemplate.attrs.list.group=Group sebserver.configtemplate.attrs.list.group=Group
sebserver.configtemplate.attrs.list.group.tooltip=The group on the view/tab where the SEB exam configuration attribute belongs to.</br></br>{0} sebserver.configtemplate.attrs.list.group.tooltip=The group on the view/tab where the SEB exam configuration attribute belongs to.<br/><br/>{0}
sebserver.configtemplate.attrs.list.type=Type sebserver.configtemplate.attrs.list.type=Type
sebserver.configtemplate.attrs.list.type.tooltip=The type of the SEB exam configuration attribute.</br></br>{0} sebserver.configtemplate.attrs.list.type.tooltip=The type of the SEB exam configuration attribute.<br/><br/>{0}
sebserver.configtemplate.attr.list.actions= sebserver.configtemplate.attr.list.actions=
sebserver.configtemplate.attr.list.actions.modify=Edit Attribute sebserver.configtemplate.attr.list.actions.modify=Edit Attribute
@ -1149,17 +1251,31 @@ sebserver.monitoring.exam.info.pleaseSelect=Please select first an Exam from the
sebserver.monitoring.exam.list.empty=There are currently no running exams sebserver.monitoring.exam.list.empty=There are currently no running exams
sebserver.monitoring.exam.list.column.name=Name sebserver.monitoring.exam.list.column.name=Name
sebserver.monitoring.exam.list.column.name.tooltip=The name of the exam.<br/><br/>Use the filter above to narrow down a specific exam name.<br/>{0}
sebserver.monitoring.exam.list.column.type=Type sebserver.monitoring.exam.list.column.type=Type
sebserver.monitoring.exam.list.column.type.tooltip=The type of the exam.<br/><br/>Use the filter above to set a specific exam type.<br/>{0}
sebserver.monitoring.exam.list.column.startTime=Start Time {0} sebserver.monitoring.exam.list.column.startTime=Start Time {0}
sebserver.monitoring.exam.list.column.startTime.tooltip=The start date and time of the exam.<br/><br/>{0}
sebserver.monitoring.exam.list.column.endTime=End Time {0} sebserver.monitoring.exam.list.column.endTime=End Time {0}
sebserver.monitoring.exam.list.column.endTime.tooltip=The end date and time of the exam.<br/><br/>{0}
sebserver.monitoring.exam=Monitoring Exam: {0} sebserver.monitoring.exam=Monitoring Exam: {0}
sebserver.monitoring.connection.list.column.id=Client Identifier sebserver.monitoring.connection.list.column.id=User Name or Session
sebserver.monitoring.connection.list.column.id.tooltip=The user session identifier or username sent by the SEB client after LMS login.
sebserver.monitoring.connection.list.column.address=IP Address sebserver.monitoring.connection.list.column.address=IP Address
sebserver.monitoring.connection.list.column.address.tooltip=The IP address from the host the SEB client is connecting to the SEB Server.
sebserver.monitoring.connection.list.column.status=Status sebserver.monitoring.connection.list.column.status=Status
sebserver.monitoring.connection.list.column.examname=Exam sebserver.monitoring.connection.list.column.status.tooltip=The current connection status
sebserver.monitoring.connection.list.column.vdiAddress=IP Address (VDI)
sebserver.monitoring.connection.form.id=User Name or Session
sebserver.monitoring.connection.form.id.tooltip=The user session identifier or username sent by the SEB client after LMS login.
sebserver.monitoring.connection.form.address=IP Address
sebserver.monitoring.connection.form.address.tooltip=The IP address from the host the SEB client is connecting to the SEB Server.
sebserver.monitoring.connection.form.status=Status
sebserver.monitoring.connection.form.status.tooltip=The current connection status
sebserver.monitoring.connection.form.exam=Exam
sebserver.monitoring.connection.form.exam.tooltip=The exam name
sebserver.monitoring.exam.connection.emptySelection=Please select first a Connection from the list sebserver.monitoring.exam.connection.emptySelection=Please select first a Connection from the list
sebserver.monitoring.exam.connection.emptySelection.active=Please select first an active Connection from the list sebserver.monitoring.exam.connection.emptySelection.active=Please select first an active Connection from the list
@ -1186,10 +1302,15 @@ sebserver.monitoring.exam.connection.action.show.undefined=Show Undefined
sebserver.monitoring.exam.connection.eventlist.title=Events sebserver.monitoring.exam.connection.eventlist.title=Events
sebserver.monitoring.exam.connection.eventlist.empty=No event found sebserver.monitoring.exam.connection.eventlist.empty=No event found
sebserver.monitoring.exam.connection.eventlist.type=Event Type sebserver.monitoring.exam.connection.eventlist.type=Event Type
sebserver.monitoring.exam.connection.eventlist.type.tooltip=The type of the log event.<br/><br/>Use the filter above to set a specific event type.<br/>{0}
sebserver.monitoring.exam.connection.eventlist.clienttime=Client Time {0} sebserver.monitoring.exam.connection.eventlist.clienttime=Client Time {0}
sebserver.monitoring.exam.connection.eventlist.clienttime.tooltip=The time the SEB client has sent within the log event.<br/><br/>{0}
sebserver.monitoring.exam.connection.eventlist.servertime=Server Time {0} sebserver.monitoring.exam.connection.eventlist.servertime=Server Time {0}
sebserver.monitoring.exam.connection.eventlist.servertime.tooltip=The exact time (UTC) the SEB Server has received the log event.<br/><br/>{0}
sebserver.monitoring.exam.connection.eventlist.value=Value sebserver.monitoring.exam.connection.eventlist.value=Value
sebserver.monitoring.exam.connection.eventlist.value.tooltip=The value of the log event.<br/><br/>{0}
sebserver.monitoring.exam.connection.eventlist.text=Text sebserver.monitoring.exam.connection.eventlist.text=Text
sebserver.monitoring.exam.connection.eventlist.text.tooltip=The text of the log event.<br/><br/>{0}
sebserver.monitoring.exam.connection.event.type.UNKNOWN=Unknown sebserver.monitoring.exam.connection.event.type.UNKNOWN=Unknown
sebserver.monitoring.exam.connection.event.type.DEBUG_LOG=Debug sebserver.monitoring.exam.connection.event.type.DEBUG_LOG=Debug
@ -1218,15 +1339,15 @@ sebserver.logs.activity.seblogs.details=Show Details
sebserver.userlogs.list.title=User Activity Logs sebserver.userlogs.list.title=User Activity Logs
sebserver.userlogs.list.column.institution=Institution sebserver.userlogs.list.column.institution=Institution
sebserver.userlogs.list.column.institution.tooltip=The institution of the user activity log.</br></br>Use the filter above to specify the institution.</br>{0} sebserver.userlogs.list.column.institution.tooltip=The institution of the user activity log.<br/><br/>Use the filter above to specify the institution.<br/>{0}
sebserver.userlogs.list.column.user=User sebserver.userlogs.list.column.user=User
sebserver.userlogs.list.column.user.tooltip=The user account of the user activity log.</br></br>Use the filter above to specify a user account.</br>{0} sebserver.userlogs.list.column.user.tooltip=The user account of the user activity log.<br/><br/>Use the filter above to specify a user account.<br/>{0}
sebserver.userlogs.list.column.dateTime=Date {0} sebserver.userlogs.list.column.dateTime=Date {0}
sebserver.userlogs.list.column.dateTime.tooltip=The date when the user activity log happened.</br></br>Use the filter above to specify a from- and to-date range.</br>{0} sebserver.userlogs.list.column.dateTime.tooltip=The date when the user activity log happened.<br/><br/>Use the filter above to specify a from- and to-date range.<br/>{0}
sebserver.userlogs.list.column.activityType=User Activity sebserver.userlogs.list.column.activityType=User Activity
sebserver.userlogs.list.column.activityType.tooltip=The type of the user activity.</br></br>Use the filter above to specify a activity type.</br>{0} sebserver.userlogs.list.column.activityType.tooltip=The type of the user activity.<br/><br/>Use the filter above to specify a activity type.<br/>{0}
sebserver.userlogs.list.column.entityType=Domain Type sebserver.userlogs.list.column.entityType=Domain Type
sebserver.userlogs.list.column.entityType.tooltip=The domain type of the user activity.</br></br>Use the filter above to specify a domain type.</br>{0} sebserver.userlogs.list.column.entityType.tooltip=The domain type of the user activity.<br/><br/>Use the filter above to specify a domain type.<br/>{0}
sebserver.userlogs.list.column.entityId=Entity-ID sebserver.userlogs.list.column.entityId=Entity-ID
sebserver.userlogs.list.column.message=Message sebserver.userlogs.list.column.message=Message
@ -1240,13 +1361,19 @@ sebserver.seblogs.list.title=SEB Client Logs
sebserver.seblogs.list.actions= sebserver.seblogs.list.actions=
sebserver.seblogs.list.empty=No SEB client logs has been found. Please adapt or clear the filter sebserver.seblogs.list.empty=No SEB client logs has been found. Please adapt or clear the filter
sebserver.seblogs.info.pleaseSelect=Please select first a SEB Client Log from the list sebserver.seblogs.info.pleaseSelect=Please select first a SEB client Log from the list
sebserver.seblogs.list.column.institution=Institution sebserver.seblogs.list.column.institution=Institution
sebserver.seblogs.list.column.institution.tooltip=The institution where the exam belongs to.<br/><br/>Use the filter above to specify the institution.<br/>{0}
sebserver.seblogs.list.column.exam=Exam sebserver.seblogs.list.column.exam=Exam
sebserver.seblogs.list.column.exam.tooltip=The exam of the SEB client logs.<br/><br/>Use the filter above to specify an exam.<br/>{0}
sebserver.seblogs.list.column.client-session=User Session-ID sebserver.seblogs.list.column.client-session=User Session-ID
sebserver.seblogs.list.column.client-session.tooltip=The user or user-session identifier.<br/><br/>Use the filter above narrow down a user identifier name.<br/>{0}
sebserver.seblogs.list.column.type=Event Type sebserver.seblogs.list.column.type=Event Type
sebserver.seblogs.list.column.type.tooltip=The SEB client log event type.<br/><br/>Use the filter above to specify log type.<br/>{0}
sebserver.seblogs.list.column.time=Event Time {0} sebserver.seblogs.list.column.time=Event Time {0}
sebserver.seblogs.list.column.time.tooltip=The SEB client log time.<br/><br/>Use the filter above to specify from- and to-date and time.<br/>{0}
sebserver.seblogs.list.column.value=Value sebserver.seblogs.list.column.value=Value
sebserver.seblogs.list.column.value.tooltip=The SEB client log value.<br/><br/>{0}
sebserver.seblogs.details.title=SEB Client Log Details sebserver.seblogs.details.title=SEB Client Log Details
sebserver.seblogs.details.event.title=Event sebserver.seblogs.details.event.title=Event
@ -1255,19 +1382,34 @@ sebserver.seblogs.details.exam.title=Exam Details
sebserver.seblogs.details.dateTime=Date sebserver.seblogs.details.dateTime=Date
sebserver.seblogs.form.column.client-session=Session-ID sebserver.seblogs.form.column.client-session=Session-ID
sebserver.seblogs.form.column.client-session.tooltip=The user or user-session identifier.
sebserver.seblogs.form.column.type=Event Type sebserver.seblogs.form.column.type=Event Type
sebserver.seblogs.form.column.type.tooltip=The SEB client log event type.
sebserver.seblogs.form.column.server-time=Server Time sebserver.seblogs.form.column.server-time=Server Time
sebserver.seblogs.form.column.server-time.tooltip=The exact time when the SEB Server got the event log sent by an SEB client.
sebserver.seblogs.form.column.client-time=SEB Client Time sebserver.seblogs.form.column.client-time=SEB Client Time
sebserver.seblogs.form.column.client-time.tooltip=The time that was send within the log from SEB client.
sebserver.seblogs.form.column.value=Value sebserver.seblogs.form.column.value=Value
sebserver.seblogs.form.column.value.tooltip=The SEB client log event value
sebserver.seblogs.form.column.message=Message sebserver.seblogs.form.column.message=Message
sebserver.seblogs.form.column.message.tooltip=The SEB client log message
sebserver.seblogs.form.column.connection.session-id=User Session-ID sebserver.seblogs.form.column.connection.session-id=User Session-ID
sebserver.seblogs.form.column.connection.address=SEB Client Address sebserver.seblogs.form.column.connection.session-id.tooltip=The user or user-session identifier.
sebserver.seblogs.form.column.connection.address=SEB client Address
sebserver.seblogs.form.column.connection.address.tooltip=The IP address of the SEB client
sebserver.seblogs.form.column.connection.token=SEB Connection Token sebserver.seblogs.form.column.connection.token=SEB Connection Token
sebserver.seblogs.form.column.connection.token.tooltip=The connection token that was generated by the SEB Server to identify the SEB client connection.
sebserver.seblogs.form.column.connection.status=Connection Status sebserver.seblogs.form.column.connection.status=Connection Status
sebserver.seblogs.form.column.connection.status.tooltip=The current SEB client connection status.
sebserver.seblogs.form.column.exam.name=Name sebserver.seblogs.form.column.exam.name=Name
sebserver.seblogs.form.column.exam.name.tooltip=The name of the exam.
sebserver.seblogs.form.column.exam.description=Description sebserver.seblogs.form.column.exam.description=Description
sebserver.seblogs.form.column.exam.description.tooltip=The description of the exam.
sebserver.seblogs.form.column.exam.type=Type sebserver.seblogs.form.column.exam.type=Type
sebserver.seblogs.form.column.exam.type.tooltip=The type of the exam
sebserver.seblogs.form.column.exam.startTime=Start Time sebserver.seblogs.form.column.exam.startTime=Start Time
sebserver.seblogs.form.column.exam.startTime.tooltip=The start date and time of the exam
sebserver.seblogs.form.column.exam.endTime=End Time sebserver.seblogs.form.column.exam.endTime=End Time
sebserver.seblogs.form.column.exam.endTime.tooltip=The end date and time of the exam