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 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_TYPE = "lms_setup_type";
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_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)
public final String id;
@JsonProperty(QUIZ_ATTR_INSTITUION_ID)
@JsonProperty(QUIZ_ATTR_INSTITUTION_ID)
public final Long institutionId;
@JsonProperty(QUIZ_ATTR_LMS_SETUP_ID)
@ -76,7 +83,7 @@ public final class QuizData implements GrantEntity {
@JsonCreator
public QuizData(
@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_TYPE) final LmsType lmsType,
@JsonProperty(QUIZ_ATTR_NAME) final String name,

View file

@ -332,7 +332,9 @@ public final class Utils {
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) {

View file

@ -141,7 +141,7 @@ public final class InstitutionalAuthenticationEntryPoint implements Authenticati
final String requestURI = request.getRequestURI();
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 {
@ -149,7 +149,7 @@ public final class InstitutionalAuthenticationEntryPoint implements Authenticati
requestURI.lastIndexOf(Constants.SLASH) + 1,
requestURI.length());
} 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;
}
}

View file

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

View file

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

View file

@ -49,6 +49,10 @@ public class ExamSebRestrictionSettings {
private final static LocTextKey SEB_RESTRICTION_FORM_TITLE =
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 =
new LocTextKey("sebserver.exam.form.sebrestriction.configKeys");
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 PageContext pageContext,
final FormHandle<?> formHandle) {
@ -187,6 +191,14 @@ public class ExamSebRestrictionSettings {
.withEmptyCellSeparation(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(
SebRestriction.ATTR_CONFIG_KEYS,
SEB_RESTRICTION_FORM_CONFIG_KEYS,
@ -207,7 +219,7 @@ public class ExamSebRestrictionSettings {
SEB_RESTRICTION_FORM_EDX_WHITE_LIST_PATHS,
sebRestriction.getAdditionalProperties()
.get(OpenEdxSebRestriction.ATTR_WHITELIST_PATHS),
() -> resourceService.sebRestrictionWhiteListResources()))
resourceService::sebRestrictionWhiteListResources))
.addFieldIf(
() -> lmsType == LmsType.OPEN_EDX,
@ -216,7 +228,7 @@ public class ExamSebRestrictionSettings {
SEB_RESTRICTION_FORM_EDX_PERMISSIONS,
sebRestriction.getAdditionalProperties()
.get(OpenEdxSebRestriction.ATTR_PERMISSION_COMPONENTS),
() -> resourceService.sebRestrictionPermissionResources()))
resourceService::sebRestrictionPermissionResources))
.addFieldIf(
() -> 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 PageContext pageContext,
final FormHandle<ExamConfigurationMap> formHandle) {
@ -212,7 +212,8 @@ final class ExamToConfigBindingForm {
CONFIG_MAPPING_NAME_TEXT_KEY,
String.valueOf(examConfigurationMap.configurationNodeId),
resourceService::examConfigurationSelectionResources)
.withSelectionListener(form -> updateFormValuesFromConfigSelection(form, resourceService)))
.withSelectionListener(form -> updateFormValuesFromConfigSelection(form, resourceService))
.mandatory())
.addField(FormBuilder.text(
Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION,

View file

@ -142,20 +142,23 @@ public class IndicatorForm implements TemplateComposer {
.addField(FormBuilder.text(
Domain.INDICATOR.ATTR_NAME,
FORM_NAME_TEXT_KEY,
indicator.name))
indicator.name)
.mandatory(!isReadonly))
.addField(FormBuilder.singleSelection(
Domain.INDICATOR.ATTR_TYPE,
FORM_TYPE_TEXT_KEY,
(indicator.type != null) ? indicator.type.name() : null,
this.resourceService::indicatorTypeResources)
.withSelectionListener(this::updateForm))
.withSelectionListener(this::updateForm)
.mandatory(!isReadonly))
.addField(FormBuilder.text(
TYPE_DESCRIPTION_FIELD_NAME,
FORM_DESC_TEXT_KEY,
typeDescription)
.asArea()
.asHTML(true)
.readonly(true)
.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);
if (StringUtils.isNotBlank(typeValue)) {
form.setFieldValue(

View file

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

View file

@ -144,9 +144,20 @@ public class LmsSetupList implements TemplateComposer {
this.pageService.getResourceService().<LmsSetup> localizedActivityFunction())
.withFilter(this.activityFilter)
.sortable())
.withDefaultAction(actionBuilder
.newAction(ActionDefinition.LMS_SETUP_VIEW_FROM_LIST)
.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));
// propagate content actions to action-pane
@ -158,13 +169,21 @@ public class LmsSetupList implements TemplateComposer {
.newAction(ActionDefinition.LMS_SETUP_VIEW_FROM_LIST)
.withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> table.hasAnyContent())
.publishIf(table::hasAnyContent, false)
.newAction(ActionDefinition.LMS_SETUP_MODIFY_FROM_LIST)
.withSelect(
table.getGrantedSelection(currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION),
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 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.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
@ -82,6 +84,7 @@ public class MonitoringClientConnection implements TemplateComposer {
private final ResourceService resourceService;
private final I18nSupport i18nSupport;
private final InstructionProcessor instructionProcessor;
private final SebClientLogDetailsPopup sebClientLogDetailsPopup;
private final long pollInterval;
private final int pageSize;
@ -94,6 +97,7 @@ public class MonitoringClientConnection implements TemplateComposer {
final PageService pageService,
final ResourceService resourceService,
final InstructionProcessor instructionProcessor,
final SebClientLogDetailsPopup sebClientLogDetailsPopup,
@Value("${sebserver.gui.webservice.poll-interval:500}") final long pollInterval,
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
@ -103,6 +107,7 @@ public class MonitoringClientConnection implements TemplateComposer {
this.i18nSupport = resourceService.getI18nSupport();
this.instructionProcessor = instructionProcessor;
this.pollInterval = pollInterval;
this.sebClientLogDetailsPopup = sebClientLogDetailsPopup;
this.pageSize = pageSize;
this.typeFilter = new TableFilterAttribute(
@ -160,29 +165,38 @@ public class MonitoringClientConnection implements TemplateComposer {
this.serverPushService.runServerPush(
new ServerPushContext(content, Utils.truePredicate()),
this.pollInterval,
clientConnectionDetails::updateData,
clientConnectionDetails::updateGUI);
context1 -> clientConnectionDetails.updateData(),
context -> clientConnectionDetails.updateGUI());
widgetFactory.labelLocalized(
content,
CustomVariant.TEXT_H3,
EVENT_LIST_TITLE_KEY);
PageService.PageActionBuilder actionBuilder = this.pageService
.pageActionBuilder(
pageContext
.clearAttributes()
.clearEntityKeys());
// 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)
.withPaging(this.pageSize)
.withRestCallAdapter(restCallBuilder -> restCallBuilder.withQueryParam(
ClientEvent.FILTER_ATTR_CONECTION_ID,
entityKey.modelId))
.withColumn(new ColumnDefinition<ClientEvent>(
.withColumn(new ColumnDefinition<ExtendedClientEvent>(
Domain.CLIENT_EVENT.ATTR_TYPE,
LIST_COLUMN_TYPE_KEY,
this.resourceService::getEventTypeName)
.withFilter(this.typeFilter)
.sortable()
.widthProportion(2))
.withColumn(new ColumnDefinition<>(
.withColumn(new ColumnDefinition<ExtendedClientEvent>(
Domain.CLIENT_EVENT.ATTR_TEXT,
LIST_COLUMN_TEXT_KEY,
ClientEvent::getText)
@ -190,19 +204,22 @@ public class MonitoringClientConnection implements TemplateComposer {
.sortable()
.withCellTooltip()
.widthProportion(4))
.withColumn(new ColumnDefinition<>(
.withColumn(new ColumnDefinition<ExtendedClientEvent>(
Domain.CLIENT_EVENT.ATTR_NUMERIC_VALUE,
LIST_COLUMN_VALUE_KEY,
ClientEvent::getValue)
.widthProportion(1))
.withColumn(new ColumnDefinition<>(
.withColumn(new ColumnDefinition<ExtendedClientEvent>(
Domain.CLIENT_EVENT.ATTR_CLIENT_TIME,
new LocTextKey(LIST_COLUMN_CLIENT_TIME_KEY.name,
this.i18nSupport.getUsersTimeZoneTitleSuffix()),
this::getClientTime)
.sortable()
.widthProportion(1))
.withColumn(new ColumnDefinition<>(
.withColumn(new ColumnDefinition<ExtendedClientEvent>(
Domain.CLIENT_EVENT.ATTR_SERVER_TIME,
new LocTextKey(LIST_COLUMN_SERVER_TIME_KEY.name,
this.i18nSupport.getUsersTimeZoneTitleSuffix()),
@ -210,15 +227,17 @@ public class MonitoringClientConnection implements TemplateComposer {
.sortable()
.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));
this.pageService
.pageActionBuilder(
pageContext
.clearAttributes()
.clearEntityKeys())
.newAction(ActionDefinition.MONITOR_EXAM_FROM_DETAILS)
actionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_BACK_TO_OVERVIEW)
.withEntityKey(parentEntityKey)
.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) {
return Constants.EMPTY_NOTE;
}
@ -246,7 +265,7 @@ public class MonitoringClientConnection implements TemplateComposer {
.formatDisplayTime(Utils.toDateTimeUTC(event.getClientTime()));
}
private final String getServerTime(final ClientEvent event) {
private String getServerTime(final ClientEvent event) {
if (event == null || event.getServerTime() == null) {
return Constants.EMPTY_NOTE;
}

View file

@ -135,12 +135,18 @@ public class MonitoringRunningExam implements TemplateComposer {
indicators,
restCall);
clientTable.withDefaultAction(
actionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION)
.withParentEntityKey(entityKey)
.create(),
this.pageService);
clientTable
.withDefaultAction(
actionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION)
.withParentEntityKey(entityKey)
.create(),
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(
new ServerPushContext(content, Utils.truePredicate()),
@ -170,7 +176,7 @@ public class MonitoringRunningExam implements TemplateComposer {
return copyOfPageAction;
})
.publishIf(privilege)
.publishIf(privilege, false)
.newAction(ActionDefinition.MONITOR_EXAM_QUIT_ALL)
.withEntityKey(entityKey)
@ -187,7 +193,7 @@ public class MonitoringRunningExam implements TemplateComposer {
action -> this.quitSebClients(action, clientTable, false),
EMPTY_ACTIVE_SELECTION_TEXT_KEY)
.noEventPropagation()
.publishIf(privilege)
.publishIf(privilege, false)
.newAction(ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION)
.withEntityKey(entityKey)
@ -197,7 +203,7 @@ public class MonitoringRunningExam implements TemplateComposer {
action -> this.disableSebClients(action, clientTable, false),
EMPTY_SELECTION_TEXT_KEY)
.noEventPropagation()
.publishIf(privilege);
.publishIf(privilege, false);
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 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 ConnectionStatus status) {
@ -337,7 +343,7 @@ public class MonitoringRunningExam implements TemplateComposer {
return action;
}
private final Consumer<ServerPushContext> updateTableGUI(final ClientConnectionTable clientTable) {
private Consumer<ServerPushContext> updateTableGUI(final ClientConnectionTable clientTable) {
return context -> {
if (!context.isDisposed()) {
try {

View file

@ -131,13 +131,17 @@ public class MonitoringRunningExamList implements TemplateComposer {
.newAction(ActionDefinition.MONITOR_EXAM_FROM_LIST)
.create())
.withSelectionListener(this.pageService.getSelectionPublisher(
pageContext,
ActionDefinition.MONITOR_EXAM_FROM_LIST))
.compose(pageContext.copyOf(content));
actionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_FROM_LIST)
.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.Function;
import ch.ethz.seb.sebserver.gbl.Constants;
import org.eclipse.swt.widgets.Composite;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@ -57,21 +58,14 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
public class QuizDiscoveryList implements TemplateComposer {
// 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 =
new LocTextKey("sebserver.quizdiscovery.list.title");
private static final LocTextKey EMPTY_LIST_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.list.empty");
private final static LocTextKey EMPTY_SELECTION_TEXT =
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");
private final static LocTextKey LMS_TEXT_KEY =
new LocTextKey("sebserver.quizdiscovery.list.column.lmssetup");
@ -83,6 +77,20 @@ public class QuizDiscoveryList implements TemplateComposer {
new LocTextKey("sebserver.quizdiscovery.list.column.endtime");
private final static LocTextKey DETAILS_TITLE_TEXT_KEY =
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 =
new LocTextKey("sebserver.quizdiscovery.quiz.import.out.dated");
@ -151,8 +159,8 @@ public class QuizDiscoveryList implements TemplateComposer {
.withColumnIf(
isSebAdmin,
() -> new ColumnDefinition<QuizData>(
QuizData.QUIZ_ATTR_INSTITUION_ID,
INSTITUION_TEXT_KEY,
QuizData.QUIZ_ATTR_INSTITUTION_ID,
INSTITUTION_TEXT_KEY,
quiz -> institutionNameFunction
.apply(String.valueOf(quiz.institutionId)))
.withFilter(this.institutionFilter))
@ -202,16 +210,16 @@ public class QuizDiscoveryList implements TemplateComposer {
.noEventPropagation()
.create())
.withSelectionListener(this.pageService.getSelectionPublisher(
pageContext,
ActionDefinition.QUIZ_DISCOVERY_SHOW_DETAILS,
ActionDefinition.QUIZ_DISCOVERY_EXAM_IMPORT))
.compose(pageContext.copyOf(content));
// propagate content actions to action-pane
final GrantCheck examGrant = currentUser.grantCheck(EntityType.EXAM);
actionBuilder
// Removed as discussed in SEBSERV-52
// .newAction(ActionDefinition.LMS_SETUP_NEW)
// .publishIf(lmsSetupGrant::iw)
.newAction(ActionDefinition.QUIZ_DISCOVERY_SHOW_DETAILS)
.withSelect(
table::getSelection,
@ -221,14 +229,14 @@ public class QuizDiscoveryList implements TemplateComposer {
institutionNameFunction),
EMPTY_SELECTION_TEXT)
.noEventPropagation()
.publishIf(table::hasAnyContent)
.publishIf(table::hasAnyContent, false)
.newAction(ActionDefinition.QUIZ_DISCOVERY_EXAM_IMPORT)
.withSelect(
table::getSelection,
action -> this.importQuizData(action, table),
EMPTY_SELECTION_TEXT)
.publishIf(() -> examGrant.im() && table.hasAnyContent());
.publishIf(() -> examGrant.im() && table.hasAnyContent(), false);
}
private static Function<QuizData, String> quizDataLmsSetupNameFunction(final ResourceService resourceService) {
@ -250,7 +258,7 @@ public class QuizDiscoveryList implements TemplateComposer {
return action
.withEntityKey(action.getSingleSelection())
.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(
@ -291,17 +299,17 @@ public class QuizDiscoveryList implements TemplateComposer {
.addFieldIf(
() -> this.resourceService.getCurrentUser().get().hasRole(UserRole.SEB_SERVER_ADMIN),
() -> FormBuilder.text(
QuizData.QUIZ_ATTR_INSTITUION_ID,
INSTITUION_TEXT_KEY,
QuizData.QUIZ_ATTR_INSTITUTION_ID,
QUIZ_DETAILS_INSTITUTION_TEXT_KEY,
institutionNameFunction.apply(quizData.getModelId())))
.addField(FormBuilder.singleSelection(
QuizData.QUIZ_ATTR_LMS_SETUP_ID,
LMS_TEXT_KEY,
QUIZ_DETAILS_LMS_TEXT_KEY,
String.valueOf(quizData.lmsSetupId),
() -> this.resourceService.lmsSetupResource()))
this.resourceService::lmsSetupResource))
.addField(FormBuilder.text(
QuizData.QUIZ_ATTR_NAME,
NAME_TEXT_KEY,
QUIZ_DETAILS_NAME_TEXT_KEY,
quizData.name))
.addField(FormBuilder.text(
QuizData.QUIZ_ATTR_DESCRIPTION,
@ -323,19 +331,17 @@ public class QuizDiscoveryList implements TemplateComposer {
if (!quizData.additionalAttributes.isEmpty()) {
quizData.additionalAttributes
.entrySet()
.stream()
.forEach(entry -> {
LocTextKey titleKey = new LocTextKey(TEXT_KEY_ADDITIONAL_ATTR_PREFIX + entry.getKey());
.forEach((key, value) -> {
LocTextKey titleKey = new LocTextKey(TEXT_KEY_ADDITIONAL_ATTR_PREFIX + key);
if (!this.pageService.getI18nSupport().hasText(titleKey)) {
titleKey = new LocTextKey(entry.getKey());
titleKey = new LocTextKey(key);
}
formbuilder
.addField(FormBuilder.text(
entry.getKey(),
key,
titleKey,
toAdditionalValue(entry.getKey(), entry.getValue()))
.asHTML(ADDITIONAL_HTML_ATTRIBUTES.contains(entry.getKey())));
toAdditionalValue(key, value))
.asHTML(ADDITIONAL_HTML_ATTRIBUTES.contains(key)));
});
}
@ -343,7 +349,7 @@ public class QuizDiscoveryList implements TemplateComposer {
}
private String toAdditionalValue(final String name, final String value) {
if ("timecreated".equals(name)) {
if (QuizData.ATTR_ADDITIONAL_CREATION_TIME.equals(name)) {
try {
return this.pageService
.getI18nSupport()
@ -351,7 +357,7 @@ public class QuizDiscoveryList implements TemplateComposer {
} catch (final Exception e) {
return value;
}
} else if ("timelimit".equals(name)) {
} else if (QuizData.ATTR_ADDITIONAL_TIME_LIMIT.equals(name)) {
try {
return this.pageService
.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;
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.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.ClientEvent;
import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
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.i18n.I18nSupport;
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.PageActionBuilder;
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.exam.GetExam;
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.TableFilterAttribute;
import ch.ethz.seb.sebserver.gui.table.EntityTable;
import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
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
@Component
@GuiProfile
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 =
new LocTextKey("sebserver.seblogs.list.title");
private static final LocTextKey EMPTY_TEXT_KEY =
@ -74,45 +58,6 @@ public class SebClientLogs implements TemplateComposer {
new LocTextKey("sebserver.seblogs.list.column.time");
private static final LocTextKey VALUE_TEXT_KEY =
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 =
new LocTextKey("sebserver.seblogs.info.pleaseSelect");
@ -124,18 +69,19 @@ public class SebClientLogs implements TemplateComposer {
private final ResourceService resourceService;
private final RestService restService;
private final I18nSupport i18nSupport;
private final WidgetFactory widgetFactory;
private final SebClientLogDetailsPopup sebClientLogDetailsPopup;
private final int pageSize;
public SebClientLogs(
final PageService pageService,
final SebClientLogDetailsPopup sebClientLogDetailsPopup,
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
this.pageService = pageService;
this.resourceService = pageService.getResourceService();
this.restService = this.resourceService.getRestService();
this.i18nSupport = this.resourceService.getI18nSupport();
this.widgetFactory = pageService.getWidgetFactory();
this.sebClientLogDetailsPopup = sebClientLogDetailsPopup;
this.pageSize = pageSize;
this.examFilter = new TableFilterAttribute(
@ -220,162 +166,34 @@ public class SebClientLogs implements TemplateComposer {
.withDefaultAction(t -> actionBuilder
.newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS)
.withExec(action -> this.showDetails(action, t.getSingleSelectedROWData()))
.withExec(action -> sebClientLogDetailsPopup.showDetails(action, t.getSingleSelectedROWData()))
.noEventPropagation()
.create())
.withSelectionListener(this.pageService.getSelectionPublisher(
pageContext,
ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS))
.compose(pageContext.copyOf(content));
actionBuilder
.newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS)
.withSelect(
table::getSelection,
action -> this.showDetails(action, table.getSingleSelectedROWData()),
action -> sebClientLogDetailsPopup.showDetails(action, table.getSingleSelectedROWData()),
EMPTY_SELECTION_TEXT)
.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() {
final Map<Long, String> examNameMapping = this.resourceService.getExamNameMapping();
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) {
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 PageContext pageContext,
final boolean copyAsTemplate,

View file

@ -183,6 +183,11 @@ public enum ActionDefinition {
ImageIcon.SAVE,
PageStateDefinitionImpl.LMS_SETUP_VIEW,
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(
new LocTextKey("sebserver.lmssetup.action.activate"),
ImageIcon.TOGGLE_OFF,
@ -193,6 +198,11 @@ public enum ActionDefinition {
ImageIcon.TOGGLE_ON,
PageStateDefinitionImpl.LMS_SETUP_VIEW,
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(
new LocTextKey("sebserver.quizdiscovery.action.list"),
@ -593,7 +603,7 @@ public enum ActionDefinition {
ImageIcon.SEND_QUIT,
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
ActionCategory.FORM),
MONITOR_EXAM_FROM_DETAILS(
MONITOR_EXAM_BACK_TO_OVERVIEW(
new LocTextKey("sebserver.monitoring.exam.action.detail.view"),
ImageIcon.SHOW,
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,

View file

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

View file

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

View file

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

View file

@ -118,10 +118,25 @@ public interface PageService {
*
* @param table the entity table
* @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 */
<T extends Entity & Activatable> Function<PageAction, PageAction> activationToggleActionFunction(
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
*
@ -142,14 +157,12 @@ public interface PageService {
* @param table the entity table
* @return a message supplier to notify deactivation dependencies to the user */
default <T extends Entity & Activatable> Supplier<LocTextKey> confirmDeactivation(final EntityTable<T> table) {
return () -> {
return confirmDeactivation(table
.getSelectedROWData()
.stream()
.filter(e -> e.isActive()) // NOTE: Activatable::isActive leads to an error here!?
.collect(Collectors.toSet()))
.get();
};
return () -> confirmDeactivation(table
.getSelectedROWData()
.stream()
.filter(Activatable::isActive) // NOTE: Activatable::isActive leads to an error here!?
.collect(Collectors.toSet()))
.get();
}
/** 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 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 */
default <T extends Entity> Consumer<Set<T>> getSelectionPublisher(
default <T> Consumer<Set<T>> getSelectionPublisher(
final PageContext pageContext,
final ActionDefinition... actionDefinitions) {
return rows -> {
firePageEvent(
new ActionActivationEvent(!rows.isEmpty(), actionDefinitions),
pageContext);
};
return rows -> firePageEvent(
new ActionActivationEvent(!rows.isEmpty(), actionDefinitions),
pageContext);
}
/** 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.
*
* @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 <T> the type of the Entity of the table
* @return TableBuilder of specified type */
@ -298,7 +309,7 @@ public interface PageService {
void clearState();
/** 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.
* 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 PageContext originalPageContext;

View file

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

View file

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

View file

@ -21,10 +21,12 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.bcel.Const;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
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 INDICATOR_NAME_TEXT_KEY_PREFIX =
"sebserver.monitoring.connection.list.column.indicator.";
"sebserver.exam.indicator.type.description.";
private final static LocTextKey CONNECTION_ID_TEXT_KEY =
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 =
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 =
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 ResourceService resourceService;
@ -88,6 +96,7 @@ public final class ClientConnectionTable {
private final EnumSet<ConnectionStatus> statusFilter;
private String statusFilterParam = "";
private boolean statusFilterChanged = false;
private Consumer<Set<EntityKey>> selectionListener;
private int tableWidth;
private boolean needsSort = false;
@ -136,21 +145,27 @@ public final class ClientConnectionTable {
this.table.setHeaderVisible(true);
this.table.setLinesVisible(true);
this.table.addListener(SWT.Selection, event -> this.notifySelectionChange());
this.widgetFactory.tableColumnLocalized(
this.table,
CONNECTION_ID_TEXT_KEY);
CONNECTION_ID_TEXT_KEY,
CONNECTION_ID_TOOLTIP_TEXT_KEY);
this.widgetFactory.tableColumnLocalized(
this.table,
CONNECTION_ADDRESS_TEXT_KEY);
CONNECTION_ADDRESS_TEXT_KEY,
CONNECTION_ADDRESS_TOOLTIP_TEXT_KEY);
this.widgetFactory.tableColumnLocalized(
this.table,
CONNECTION_STATUS_TEXT_KEY);
CONNECTION_STATUS_TEXT_KEY,
CONNECTION_STATUS_TOOLTIP_TEXT_KEY);
for (final Indicator indDef : indicators) {
final TableColumn tc = new TableColumn(this.table, SWT.NONE);
final String indicatorName = this.widgetFactory.getI18nSupport().getText(
INDICATOR_NAME_TEXT_KEY_PREFIX + indDef.name,
indDef.name);
tc.setText(indicatorName);
TableColumn tableColumn = widgetFactory.tableColumnLocalized(
this.table,
new LocTextKey(INDICATOR_NAME_TEXT_KEY_PREFIX + indDef.name),
new LocTextKey(INDICATOR_NAME_TEXT_KEY_PREFIX + indDef.type.name)
);
tableColumn.setText(indDef.name);
}
this.tableMapping = new LinkedHashMap<>();
@ -184,7 +199,7 @@ public final class ClientConnectionTable {
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 -> {
final Tuple<String> selection = getSingleSelection();
if (selection == null) {
@ -200,7 +215,7 @@ public final class ClientConnectionTable {
selection._2);
pageService.executePageAction(copyOfPageAction);
});
return this;
}
public Set<String> getConnectionTokens(
@ -237,9 +252,15 @@ public final class ClientConnectionTable {
public void removeSelection() {
if (this.table != null) {
this.table.deselectAll();
this.notifySelectionChange();
}
}
public ClientConnectionTable withSelectionListener(Consumer<Set<EntityKey>> selectionListener) {
this.selectionListener = selectionListener;
return this;
}
public Set<EntityKey> getSelection() {
final int[] selectionIndices = this.table.getSelectionIndices();
if (selectionIndices == null || selectionIndices.length < 1) {
@ -282,11 +303,10 @@ public final class ClientConnectionTable {
log.error("Error poll connection data: ", error);
return Collections.emptyList();
})
.stream()
.forEach(data -> {
final UpdatableTableItem tableItem = this.tableMapping.computeIfAbsent(
data.getConnectionId(),
connectionId -> new UpdatableTableItem(connectionId));
UpdatableTableItem::new);
tableItem.push(data);
if (this.statusFilterChanged) {
this.toDelete.remove(data.getConnectionId());
@ -294,7 +314,7 @@ public final class ClientConnectionTable {
});
if (this.statusFilterChanged && !this.toDelete.isEmpty()) {
this.toDelete.stream().forEach(id -> {
this.toDelete.forEach(id -> {
final UpdatableTableItem item = this.tableMapping.remove(id);
final List<Long> list = this.sessionIds.get(item.connectionData.clientConnection.userSessionId);
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> {
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))
.stream()
.forEach(key -> {
final Button button = this.checkboxes.get(key);
if (button != null) {
@ -120,7 +119,7 @@ public final class MultiSelectionCheckbox extends Composite implements Selection
this.checkboxes
.values()
.stream()
.filter(button -> button.getSelection())
.filter(Button::getSelection)
.map(button -> (String) button.getData(OPTION_VALUE))
.collect(Collectors.toList()).toArray());
}
@ -129,7 +128,6 @@ public final class MultiSelectionCheckbox extends Composite implements Selection
public void clear() {
this.checkboxes
.values()
.stream()
.forEach(button -> button.setSelection(false));
}

View file

@ -14,6 +14,7 @@ import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import ch.ethz.seb.sebserver.gbl.Constants;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.SWT;
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 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 COLOR_TEXT_KEY = new LocTextKey("sebserver.exam.indicator.thresholds.list.color");
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 static final LocTextKey VALUE_TEXT_KEY =
new LocTextKey("sebserver.exam.indicator.thresholds.list.value");
private static final LocTextKey VALUE_TOOLTIP_TEXT_KEY =
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 Supplier<IndicatorType> indicatorTypeSupplier;
@ -80,14 +89,16 @@ public final class ThresholdList extends Composite {
final Label valueTitle = widgetFactory.labelLocalized(
this,
CustomVariant.TITLE_LABEL,
VALUE_TEXT_KEY);
VALUE_TEXT_KEY,
VALUE_TOOLTIP_TEXT_KEY);
this.valueCell = new GridData(SWT.FILL, SWT.CENTER, true, false);
valueTitle.setLayoutData(this.valueCell);
final Label colorTitle = widgetFactory.labelLocalized(
this,
CustomVariant.TITLE_LABEL,
COLOR_TEXT_KEY);
COLOR_TEXT_KEY,
COLOR_TOOLTIP_TEXT_KEY);
this.colorCell = new GridData(SWT.FILL, SWT.CENTER, true, false);
colorTitle.setLayoutData(this.colorCell);
@ -103,9 +114,7 @@ public final class ThresholdList extends Composite {
public void setThresholds(final Collection<Threshold> thresholds) {
clearList();
if (thresholds != null) {
thresholds
.stream()
.forEach(this::addThreshold);
thresholds.forEach(this::addThreshold);
}
}
@ -122,13 +131,11 @@ public final class ThresholdList extends Composite {
.stream()
.filter(entry -> entry.getValue() == null || StringUtils.isBlank(entry.getColor()))
.collect(Collectors.toList())
.stream()
.forEach(entry -> removeThreshold(entry));
.forEach(this::removeThreshold);
}
private void clearList() {
this.thresholds.stream()
.forEach(e -> e.dispose());
this.thresholds.forEach(Entry::dispose);
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 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_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 LmsSetup lmsSetup;
@ -96,20 +97,18 @@ public class MoodleCourseAccess extends CourseAccess {
@Override
protected Supplier<List<QuizData>> allQuizzesSupplier() {
return () -> {
return getRestTemplate()
.map(this::collectAllQuizzes)
.getOrThrow();
};
return () -> getRestTemplate()
.map(this::collectAllQuizzes)
.getOrThrow();
}
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(
restTemplate)
.stream()
.reduce(
new ArrayList<QuizData>(),
new ArrayList<>(),
(list, courseData) -> {
list.addAll(quizDataOf(
this.lmsSetup,
@ -138,7 +137,7 @@ public class MoodleCourseAccess extends CourseAccess {
// then get all quizzes of courses and filter
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(
MOODLE_QUIZ_API_FUNCTION_NAME,
@ -149,7 +148,6 @@ public class MoodleCourseAccess extends CourseAccess {
CourseQuizData.class);
courseQuizData.quizzes
.stream()
.forEach(quiz -> {
final CourseData course = courseData.get(quiz.course);
if (course != null) {
@ -166,24 +164,24 @@ public class MoodleCourseAccess extends CourseAccess {
}
}
static Map<String, String> additionalAttrs = new HashMap<>();
private static List<QuizData> quizDataOf(
final LmsSetup lmsSetup,
final CourseData courseData,
final String uriPrefix) {
final Map<String, String> additionalAttrs = new HashMap<>();
additionalAttrs.clear();
additionalAttrs.put("timecreated", String.valueOf(courseData.timecreated));
additionalAttrs.put("course_shortname", courseData.shortname);
additionalAttrs.put("course_fullname", courseData.fullname);
additionalAttrs.put("course_displayname", courseData.displayname);
additionalAttrs.put("course_summary", courseData.summary);
additionalAttrs.put(QuizData.ATTR_ADDITIONAL_CREATION_TIME, String.valueOf(courseData.time_created));
additionalAttrs.put(QuizData.ATTR_ADDITIONAL_SHORT_NAME, courseData.short_name);
additionalAttrs.put(QuizData.ATTR_ADDITIONAL_FULL_NAME, courseData.full_name);
additionalAttrs.put(QuizData.ATTR_ADDITIONAL_DISPLAY_NAME, courseData.display_name);
additionalAttrs.put(QuizData.ATTR_ADDITIONAL_SUMMARY, courseData.summary);
return courseData.quizzes
.stream()
.map(courseQuizData -> {
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(
courseQuizData.id,
lmsSetup.getInstitutionId(),
@ -191,13 +189,12 @@ public class MoodleCourseAccess extends CourseAccess {
lmsSetup.getLmsType(),
courseQuizData.name,
courseQuizData.intro,
Utils.toDateTimeUTCUnix(courseData.startdate),
Utils.toDateTimeUTCUnix(courseData.enddate),
Utils.toDateTimeUTCUnix(courseData.start_date),
Utils.toDateTimeUTCUnix(courseData.end_date),
startURI,
additionalAttrs);
})
.collect(Collectors.toList());
}
private Result<MoodleAPIRestTemplate> getRestTemplate() {
@ -218,34 +215,34 @@ public class MoodleCourseAccess extends CourseAccess {
@JsonIgnoreProperties(ignoreUnknown = true)
static final class CourseData {
final String id;
final String shortname;
final String fullname;
final String displayname;
final String short_name;
final String full_name;
final String display_name;
final String summary;
final Long startdate; // unixtime milliseconds UTC
final Long enddate; // unixtime milliseconds UTC
final Long timecreated; // unixtime milliseconds UTC
final Long start_date; // unix-time milliseconds UTC
final Long end_date; // unix-time milliseconds UTC
final Long time_created; // unix-time milliseconds UTC
final Collection<CourseQuiz> quizzes = new ArrayList<>();
@JsonCreator
protected CourseData(
@JsonProperty(value = "id") final String id,
@JsonProperty(value = "shortname") final String shortname,
@JsonProperty(value = "fullname") final String fullname,
@JsonProperty(value = "displayname") final String displayname,
@JsonProperty(value = "shortname") final String short_name,
@JsonProperty(value = "fullname") final String full_name,
@JsonProperty(value = "displayname") final String display_name,
@JsonProperty(value = "summary") final String summary,
@JsonProperty(value = "startdate") final Long startdate,
@JsonProperty(value = "enddate") final Long enddate,
@JsonProperty(value = "timecreated") final Long timecreated) {
@JsonProperty(value = "startdate") final Long start_date,
@JsonProperty(value = "enddate") final Long end_date,
@JsonProperty(value = "timecreated") final Long time_created) {
this.id = id;
this.shortname = shortname;
this.fullname = fullname;
this.displayname = displayname;
this.short_name = short_name;
this.full_name = full_name;
this.display_name = display_name;
this.summary = summary;
this.startdate = startdate;
this.enddate = enddate;
this.timecreated = timecreated;
this.start_date = start_date;
this.end_date = end_date;
this.time_created = time_created;
}
}
@ -266,26 +263,26 @@ public class MoodleCourseAccess extends CourseAccess {
static final class CourseQuiz {
final String id;
final String course;
final String coursemodule;
final String course_module;
final String name;
final String intro; // HTML
final Long timelimit; // unixtime milliseconds UTC
final Long time_limit; // unix-time milliseconds UTC
@JsonCreator
protected CourseQuiz(
@JsonProperty(value = "id") final String id,
@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 = "intro") final String intro,
@JsonProperty(value = "timelimit") final Long timelimit) {
@JsonProperty(value = "timelimit") final Long time_limit) {
this.id = id;
this.course = course;
this.coursemodule = coursemodule;
this.course_module = course_module;
this.name = name;
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
final T result = changeAction
.apply(mapping)
.onError(t -> log.error("Fauled to save exam configuration: {}",
.onError(t -> log.error("Failed to save exam configuration: {}",
mapping.configurationNodeId))
.getOrThrow();

View file

@ -144,6 +144,7 @@ public class ExamConfigurationMappingController extends EntityController<ExamCon
final ExamConfigurationMap requestModel = this.createNew(postMap);
return this.checkCreateAccess(requestModel)
.flatMap(this::validForCreate)
.map(this::checkPasswordMatch)
.flatMap(entity -> this.examConfigUpdateService.processExamConfigurationMappingChange(
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.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.unsupported.file=This file type is not supported. Supported files are: {0}
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.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.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.password.wrong=The old password is wrong
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.logout=Sign 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.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.title=Institutions
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.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.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.form=Institution
@ -164,9 +165,9 @@ sebserver.institution.form.title=Institution
sebserver.institution.form.name=Name
sebserver.institution.form.name.tooltip=The name of the institution
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.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
@ -176,27 +177,27 @@ sebserver.institution.form.logoImage.unsupportedFileType=The selected file is no
sebserver.useraccount.list.actions=
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.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.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.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.title=User Accounts
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.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.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.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.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.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.tooltip=The users language.
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.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.tooltip=The password of the user account
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.title=Learning Management System Setups
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.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.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.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.form=LMS Setup
@ -270,7 +275,7 @@ sebserver.lmssetup.action.modify=Edit
sebserver.lmssetup.action.savetest=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.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.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
@ -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.new=Add Learning Management System Setup
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.tooltip=The name of the LMS setup
sebserver.lmssetup.form.type=Type
sebserver.lmssetup.form.clientname.seb=SEB Auth. Name
sebserver.lmssetup.form.secret.seb=SEB Auth. Password
sebserver.lmssetup.form.type.tooltip=The type of the LMS Setup.
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.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.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.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.tooltip=The server name of the proxy host to connect to
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.tooltip=The proxy authentication credentials (name and password)<br/>to authenticate the connection within the proxy server
################################
# Quiz Discovery
@ -306,10 +319,15 @@ sebserver.quizdiscovery.list.actions=
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.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.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.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.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.tooltip=The end time of the quiz.<br/><br/>{0}
sebserver.quizdiscovery.info.pleaseSelect=Please select first a Quiz from the list
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.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.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.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.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.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.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.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.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.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.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.toolitp=The time limit of the quiz<br/><br/>This is defined on the corresponding LMS
################################
# Exam
@ -336,10 +370,15 @@ sebserver.quizdiscovery.quiz.details.additional.timelimit=Time Limit
sebserver.exam.list.actions=
sebserver.exam.list.title=Exams
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.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.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.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.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.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=Exam
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.tooltip=The identifier that identifies the quiz of the exam on the corresponding LMS
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.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.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.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.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.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.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.action.add=Add as supporter for this exam
sebserver.exam.form.supporter.action.remove=Remove supporter
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.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.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.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.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.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.tooltip=Define the additional SEB restriction permissions
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.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.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.tooltip=The actual course and course content
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.tooltip=The progress overview of the Open edX course
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.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.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.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.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.tooltip=No exam type specified
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.tooltip=Exam type specified for bring your own devices
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.RUNNING=Running
@ -413,9 +482,13 @@ sebserver.exam.status.FINISHED=Finished
sebserver.exam.configuration.list.actions=
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.tooltip=The name of the attached SEB exam configuration.
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.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.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
@ -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=SEB Configuration Mapping
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.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.tooltip=The description of the selected SEB exam configuration
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.tooltip=Please confirm the encryption password if there is one
sebserver.exam.indicator.list.actions=
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.tooltip=The type of indicator
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.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.pleaseSelect=Please select first an indicator from the list
sebserver.exam.indicator.type.LAST_PING=Last Ping Time
sebserver.exam.indicator.type.ERROR_COUNT=Errors
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.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.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.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/>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/>The value is natural numbers.
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.new=Add Indicator
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.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.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.tooltip=A detailed description of the selected indicator.
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.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.list.title=Thresholds
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.add=Add Threshold
sebserver.exam.indicator.thresholds.list.remove=Delete 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.add=Add a new threshold
sebserver.exam.indicator.thresholds.list.remove=Delete this threshold
################################
# 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.actions=
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.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.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.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.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=SEB Client Configuration
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.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.tooltip=The date when the SEB client configuration was first created.
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.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.view=View Configuration
@ -527,13 +629,13 @@ sebserver.clientconfig.action.deactivate=Deactivate Configuration
sebserver.examconfig.action.list=Exam Configuration
sebserver.examconfig.list.title=Exam Configurations
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.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.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.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=
@ -577,10 +679,10 @@ sebserver.examconfig.form.with-history=With History
sebserver.examconfig.form.template=Template
sebserver.examconfig.form.template.tooltip=The template this SEB exam configuration depends on.
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.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.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.confirm=Confirm password
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.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.confirm=Confirm password
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.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.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.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.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.tooltip=Height of SEB dock/task bar in points/pixels
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.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.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.label.zoomMode.0=Use page zoom
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.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.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.en-AU=English (Australia) (en-AU)
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.1=open in same 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.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=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.group.newwinsize=New browser window size and position
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.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.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.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.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.tooltip=Allow web applications to access camera
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.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.browserWindowAllowReload=Allow reload exam
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.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.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.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.1=Custom
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.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.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.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.downloadDirectoryOSX=Download directory (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.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.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.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.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.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.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.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.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.permittedProcesses.add.tooltip=Add 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.1=Win
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.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.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.allowedExecutables=Window handling process
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.removeAction=Remove this argument
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.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.tooltip=Start the process automatically together with SEB.
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.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
@ -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.1=Win
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.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.tooltip=Original file name (optional)
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.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.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.tooltip=Activates the button "Shutdown"
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.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
@ -1103,13 +1205,13 @@ sebserver.configtemplate.attr.type.COMPOSITE_TABLE=Table
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.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.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.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.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.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.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.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.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.tooltip=The end date and time of the exam.<br/><br/>{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.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.examname=Exam
sebserver.monitoring.connection.list.column.vdiAddress=IP Address (VDI)
sebserver.monitoring.connection.list.column.status.tooltip=The current connection status
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.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.empty=No event found
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.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.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.tooltip=The value of the log event.<br/><br/>{0}
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.DEBUG_LOG=Debug
@ -1218,15 +1339,15 @@ sebserver.logs.activity.seblogs.details=Show Details
sebserver.userlogs.list.title=User Activity Logs
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.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.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.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.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.message=Message
@ -1240,13 +1361,19 @@ sebserver.seblogs.list.title=SEB Client Logs
sebserver.seblogs.list.actions=
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.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.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.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.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.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.tooltip=The SEB client log value.<br/><br/>{0}
sebserver.seblogs.details.title=SEB Client Log Details
sebserver.seblogs.details.event.title=Event
@ -1255,19 +1382,34 @@ sebserver.seblogs.details.exam.title=Exam Details
sebserver.seblogs.details.dateTime=Date
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.tooltip=The SEB client log event type.
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.tooltip=The time that was send within the log from SEB client.
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.tooltip=The SEB client log message
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.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.tooltip=The current SEB client connection status.
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.tooltip=The description of the exam.
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.tooltip=The start date and time of the exam
sebserver.seblogs.form.column.exam.endTime=End Time
sebserver.seblogs.form.column.exam.endTime.tooltip=The end date and time of the exam