fixed for demo, code cleanup

This commit is contained in:
anhefti 2019-04-05 15:58:59 +02:00
parent eb8e37a888
commit 71a75bb406
13 changed files with 148 additions and 115 deletions

View file

@ -195,12 +195,13 @@ public class ExamForm implements TemplateComposer {
"sebserver.exam.form.status", "sebserver.exam.form.status",
i18nSupport.getText(new LocTextKey("sebserver.exam.status." + examStatus.name()))) i18nSupport.getText(new LocTextKey("sebserver.exam.status." + examStatus.name())))
.readonly(true)) .readonly(true))
.addField(FormBuilder.multiComboSelection( .addFieldIf(
isNotNew,
() -> FormBuilder.multiComboSelection(
Domain.EXAM.ATTR_SUPPORTER, Domain.EXAM.ATTR_SUPPORTER,
"sebserver.exam.form.supporter", "sebserver.exam.form.supporter",
StringUtils.join(exam.supporter, Constants.LIST_SEPARATOR_CHAR), StringUtils.join(exam.supporter, Constants.LIST_SEPARATOR_CHAR),
this.resourceService::examSupporterResources) this.resourceService::examSupporterResources))
.withCondition(isNotNew))
.buildFor(importFromQuizData .buildFor(importFromQuizData
? restService.getRestCall(ImportAsExam.class) ? restService.getRestCall(ImportAsExam.class)
@ -267,7 +268,9 @@ public class ExamForm implements TemplateComposer {
thresholdColumnKey, thresholdColumnKey,
ExamForm::thresholdsValue, ExamForm::thresholdsValue,
false)) false))
.withDefaultAction(actionBuilder .withDefaultActionIf(
() -> editable,
() -> actionBuilder
.newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST) .newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST)
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
.create()) .create())

View file

@ -11,6 +11,8 @@ package ch.ethz.seb.sebserver.gui.content;
import java.util.function.Function; import java.util.function.Function;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -18,17 +20,16 @@ import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam; 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.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.ResourceService; import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageMessageException;
import ch.ethz.seb.sebserver.gui.service.page.PageService; import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
@ -60,8 +61,9 @@ public class ExamList implements TemplateComposer {
new LocTextKey("sebserver.exam.list.column.name"); new LocTextKey("sebserver.exam.list.column.name");
private final static LocTextKey columnTitleTypeKey = private final static LocTextKey columnTitleTypeKey =
new LocTextKey("sebserver.exam.list.column.type"); new LocTextKey("sebserver.exam.list.column.type");
private final static LocTextKey noModifyOfOutDatedExams =
new LocTextKey("sebserver.exam.list.modify.out.dated");
private final TableFilterAttribute institutionFilter;
private final TableFilterAttribute lmsFilter; private final TableFilterAttribute lmsFilter;
private final TableFilterAttribute nameFilter = private final TableFilterAttribute nameFilter =
new TableFilterAttribute(CriteriaType.TEXT, QuizData.FILTER_ATTR_NAME); new TableFilterAttribute(CriteriaType.TEXT, QuizData.FILTER_ATTR_NAME);
@ -77,11 +79,6 @@ public class ExamList implements TemplateComposer {
this.resourceService = resourceService; this.resourceService = resourceService;
this.pageSize = (pageSize != null) ? pageSize : 20; this.pageSize = (pageSize != null) ? pageSize : 20;
this.institutionFilter = new TableFilterAttribute(
CriteriaType.SINGLE_SELECTION,
Entity.FILTER_ATTR_INSTITUTION,
this.resourceService::institutionResource);
this.lmsFilter = new TableFilterAttribute( this.lmsFilter = new TableFilterAttribute(
CriteriaType.SINGLE_SELECTION, CriteriaType.SINGLE_SELECTION,
LmsSetup.FILTER_ATTR_LMS_SETUP, LmsSetup.FILTER_ATTR_LMS_SETUP,
@ -101,7 +98,6 @@ public class ExamList implements TemplateComposer {
pageContext.getParent(), pageContext.getParent(),
new LocTextKey("sebserver.exam.list.title")); new LocTextKey("sebserver.exam.list.title"));
final boolean isSEBAdmin = currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN);
final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(pageContext.clearEntityKeys()); final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(pageContext.clearEntityKeys());
// table // table
@ -109,13 +105,6 @@ public class ExamList implements TemplateComposer {
this.pageService.entityTableBuilder(restService.getRestCall(GetExams.class)) this.pageService.entityTableBuilder(restService.getRestCall(GetExams.class))
.withEmptyMessage(new LocTextKey("sebserver.exam.list.empty")) .withEmptyMessage(new LocTextKey("sebserver.exam.list.empty"))
.withPaging(this.pageSize) .withPaging(this.pageSize)
.withColumnIf(() -> isSEBAdmin,
new ColumnDefinition<>(
Domain.EXAM.ATTR_INSTITUTION_ID,
new LocTextKey("sebserver.exam.list.column.institution"),
examInstitutionNameFunction(this.resourceService),
this.institutionFilter,
false))
.withColumn(new ColumnDefinition<>( .withColumn(new ColumnDefinition<>(
Domain.EXAM.ATTR_LMS_SETUP_ID, Domain.EXAM.ATTR_LMS_SETUP_ID,
columnTitleLmsSetupKey, columnTitleLmsSetupKey,
@ -158,14 +147,25 @@ public class ExamList implements TemplateComposer {
.publishIf(table::hasAnyContent) .publishIf(table::hasAnyContent)
.newAction(ActionDefinition.EXAM_MODIFY_FROM_LIST) .newAction(ActionDefinition.EXAM_MODIFY_FROM_LIST)
.withSelect(table::getSelection, PageAction::applySingleSelection, emptySelectionTextKey) .withSelect(
table::getSelection,
action -> this.modifyExam(action, table),
emptySelectionTextKey)
.publishIf(() -> userGrant.im() && table.hasAnyContent()); .publishIf(() -> userGrant.im() && table.hasAnyContent());
} }
private static Function<Exam, String> examInstitutionNameFunction(final ResourceService resourceService) { private PageAction modifyExam(final PageAction action, final EntityTable<Exam> table) {
return exam -> resourceService.getInstitutionNameFunction() final Exam exam = table.getSelectedROWData();
.apply(String.valueOf(exam.institutionId));
if (exam.startTime != null) {
final DateTime now = DateTime.now(DateTimeZone.UTC);
if (exam.startTime.isBefore(now)) {
throw new PageMessageException(noModifyOfOutDatedExams);
}
}
return action.withEntityKey(action.getSingleSelection());
} }
private static Function<Exam, String> examLmsSetupNameFunction(final ResourceService resourceService) { private static Function<Exam, String> examLmsSetupNameFunction(final ResourceService resourceService) {

View file

@ -129,11 +129,12 @@ public class InstitutionForm implements TemplateComposer {
Domain.INSTITUTION.ATTR_URL_SUFFIX, Domain.INSTITUTION.ATTR_URL_SUFFIX,
"sebserver.institution.form.urlSuffix", "sebserver.institution.form.urlSuffix",
institution.urlSuffix)) institution.urlSuffix))
.addField(FormBuilder.imageUpload( .addFieldIf(
() -> !isNew && modifyGrant,
() -> FormBuilder.imageUpload(
Domain.INSTITUTION.ATTR_LOGO_IMAGE, Domain.INSTITUTION.ATTR_LOGO_IMAGE,
"sebserver.institution.form.logoImage", "sebserver.institution.form.logoImage",
institution.logoImage) institution.logoImage))
.withCondition(() -> !isNew && modifyGrant))
.buildFor((isNew) .buildFor((isNew)
? this.restService.getRestCall(NewInstitution.class) ? this.restService.getRestCall(NewInstitution.class)
: this.restService.getRestCall(SaveInstitution.class)); : this.restService.getRestCall(SaveInstitution.class));

View file

@ -137,12 +137,13 @@ public class LmsSetupForm implements TemplateComposer {
.putStaticValueIf(isNotNew, .putStaticValueIf(isNotNew,
Domain.LMS_SETUP.ATTR_LMS_TYPE, Domain.LMS_SETUP.ATTR_LMS_TYPE,
String.valueOf(lmsSetup.getLmsType())) String.valueOf(lmsSetup.getLmsType()))
.addField(FormBuilder.singleSelection( .addFieldIf(
isSEBAdmin,
() -> FormBuilder.singleSelection(
Domain.LMS_SETUP.ATTR_INSTITUTION_ID, Domain.LMS_SETUP.ATTR_INSTITUTION_ID,
"sebserver.lmssetup.form.institution", "sebserver.lmssetup.form.institution",
String.valueOf(lmsSetup.getInstitutionId()), String.valueOf(lmsSetup.getInstitutionId()),
() -> this.resourceService.institutionResource()) () -> this.resourceService.institutionResource())
.withCondition(isSEBAdmin)
.readonlyIf(isNotNew)) .readonlyIf(isNotNew))
.addField(FormBuilder.text( .addField(FormBuilder.text(
Domain.LMS_SETUP.ATTR_NAME, Domain.LMS_SETUP.ATTR_NAME,
@ -154,21 +155,24 @@ public class LmsSetupForm implements TemplateComposer {
(lmsType != null) ? lmsType.name() : null, (lmsType != null) ? lmsType.name() : null,
this.resourceService::lmsTypeResources) this.resourceService::lmsTypeResources)
.readonlyIf(isNotNew)) .readonlyIf(isNotNew))
.addField(FormBuilder.text( .addFieldIf(
() -> isNotNew.getAsBoolean(),
() -> FormBuilder.text(
Domain.LMS_SETUP.ATTR_LMS_URL, Domain.LMS_SETUP.ATTR_LMS_URL,
"sebserver.lmssetup.form.url", "sebserver.lmssetup.form.url",
lmsSetup.getLmsApiUrl()) lmsSetup.getLmsApiUrl()))
.withCondition(() -> isNotNew.getAsBoolean())) .addFieldIf(
.addField(FormBuilder.text( () -> isNotNew.getAsBoolean(),
() -> FormBuilder.text(
Domain.LMS_SETUP.ATTR_LMS_CLIENTNAME, Domain.LMS_SETUP.ATTR_LMS_CLIENTNAME,
"sebserver.lmssetup.form.clientname.lms", "sebserver.lmssetup.form.clientname.lms",
lmsSetup.getLmsAuthName()) lmsSetup.getLmsAuthName()))
.withCondition(() -> isNotNew.getAsBoolean())) .addFieldIf(
.addField(FormBuilder.text( () -> isNotNew.getAsBoolean(),
() -> FormBuilder.text(
Domain.LMS_SETUP.ATTR_LMS_CLIENTSECRET, Domain.LMS_SETUP.ATTR_LMS_CLIENTSECRET,
"sebserver.lmssetup.form.secret.lms") "sebserver.lmssetup.form.secret.lms")
.asPasswordField() .asPasswordField())
.withCondition(() -> isNotNew.getAsBoolean()))
.buildFor((entityKey == null) .buildFor((entityKey == null)
? restService.getRestCall(NewLmsSetup.class) ? restService.getRestCall(NewLmsSetup.class)

View file

@ -108,8 +108,9 @@ public class LmsSetupList implements TemplateComposer {
this.pageService.entityTableBuilder(restService.getRestCall(GetLmsSetups.class)) this.pageService.entityTableBuilder(restService.getRestCall(GetLmsSetups.class))
.withEmptyMessage(EMPTY_LIST_TEXT_KEY) .withEmptyMessage(EMPTY_LIST_TEXT_KEY)
.withPaging(this.pageSize) .withPaging(this.pageSize)
.withColumnIf(() -> isSEBAdmin, .withColumnIf(
new ColumnDefinition<>( () -> isSEBAdmin,
() -> new ColumnDefinition<>(
Domain.LMS_SETUP.ATTR_INSTITUTION_ID, Domain.LMS_SETUP.ATTR_INSTITUTION_ID,
INSTITUTION_TEXT_KEY, INSTITUTION_TEXT_KEY,
lmsSetupInstitutionNameFunction(this.resourceService), lmsSetupInstitutionNameFunction(this.resourceService),

View file

@ -95,11 +95,12 @@ public class UserAccountChangePasswordForm implements TemplateComposer {
PasswordChange.ATTR_NAME_NEW_PASSWORD, PasswordChange.ATTR_NAME_NEW_PASSWORD,
"sebserver.useraccount.form.password.new") "sebserver.useraccount.form.password.new")
.asPasswordField()) .asPasswordField())
.addField(FormBuilder.text( .addFieldIf(
() -> entityKey != null,
() -> FormBuilder.text(
PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD,
"sebserver.useraccount.form.password.new.confirm") "sebserver.useraccount.form.password.new.confirm")
.asPasswordField() .asPasswordField())
.withCondition(() -> entityKey != null))
.buildFor(this.restService.getRestCall(ChangePassword.class)); .buildFor(this.restService.getRestCall(ChangePassword.class));
this.pageService.pageActionBuilder(pageContext) this.pageService.pageActionBuilder(pageContext)

View file

@ -138,12 +138,13 @@ public class UserAccountForm implements TemplateComposer {
.putStaticValueIf(isNotNew, .putStaticValueIf(isNotNew,
Domain.USER.ATTR_INSTITUTION_ID, Domain.USER.ATTR_INSTITUTION_ID,
String.valueOf(userAccount.getInstitutionId())) String.valueOf(userAccount.getInstitutionId()))
.addField(FormBuilder.singleSelection( .addFieldIf(
isSEBAdmin,
() -> FormBuilder.singleSelection(
Domain.USER.ATTR_INSTITUTION_ID, Domain.USER.ATTR_INSTITUTION_ID,
"sebserver.useraccount.form.institution", "sebserver.useraccount.form.institution",
String.valueOf(userAccount.getInstitutionId()), String.valueOf(userAccount.getInstitutionId()),
() -> this.resourceService.institutionResource()) () -> this.resourceService.institutionResource())
.withCondition(isSEBAdmin)
.readonlyIf(isNotNew)) .readonlyIf(isNotNew))
.addField(FormBuilder.text( .addField(FormBuilder.text(
Domain.USER.ATTR_NAME, Domain.USER.ATTR_NAME,
@ -161,29 +162,33 @@ public class UserAccountForm implements TemplateComposer {
Domain.USER.ATTR_LANGUAGE, Domain.USER.ATTR_LANGUAGE,
"sebserver.useraccount.form.language", "sebserver.useraccount.form.language",
userAccount.getLanguage().getLanguage(), userAccount.getLanguage().getLanguage(),
this.resourceService::languageResources)) this.resourceService::languageResources)
.readonly(true))
.addField(FormBuilder.singleSelection( .addField(FormBuilder.singleSelection(
Domain.USER.ATTR_TIMEZONE, Domain.USER.ATTR_TIMEZONE,
"sebserver.useraccount.form.timezone", "sebserver.useraccount.form.timezone",
userAccount.getTimeZone().getID(), userAccount.getTimeZone().getID(),
this.resourceService::timeZoneResources)) this.resourceService::timeZoneResources))
.addField(FormBuilder.multiSelection( .addFieldIf(
() -> modifyGrant,
() -> FormBuilder.multiSelection(
USER_ROLE.REFERENCE_NAME, USER_ROLE.REFERENCE_NAME,
"sebserver.useraccount.form.roles", "sebserver.useraccount.form.roles",
StringUtils.join(userAccount.getRoles(), Constants.LIST_SEPARATOR_CHAR), StringUtils.join(userAccount.getRoles(), Constants.LIST_SEPARATOR_CHAR),
this.resourceService::userRoleResources) this.resourceService::userRoleResources)
.withCondition(() -> modifyGrant)
.visibleIf(writeGrant)) .visibleIf(writeGrant))
.addField(FormBuilder.text( .addFieldIf(
isNew,
() -> FormBuilder.text(
PasswordChange.ATTR_NAME_NEW_PASSWORD, PasswordChange.ATTR_NAME_NEW_PASSWORD,
"sebserver.useraccount.form.password") "sebserver.useraccount.form.password")
.asPasswordField() .asPasswordField())
.withCondition(isNew)) .addFieldIf(
.addField(FormBuilder.text( isNew,
() -> FormBuilder.text(
PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD,
"sebserver.useraccount.form.password.confirm") "sebserver.useraccount.form.password.confirm")
.asPasswordField() .asPasswordField())
.withCondition(isNew))
.buildFor((entityKey == null) .buildFor((entityKey == null)
? restService.getRestCall(NewUserAccount.class) ? restService.getRestCall(NewUserAccount.class)
: restService.getRestCall(SaveUserAccount.class)); : restService.getRestCall(SaveUserAccount.class));

View file

@ -118,8 +118,9 @@ public class UserAccountList implements TemplateComposer {
.withEmptyMessage(new LocTextKey("sebserver.useraccount.list.empty")) .withEmptyMessage(new LocTextKey("sebserver.useraccount.list.empty"))
.withPaging(this.pageSize) .withPaging(this.pageSize)
.withColumnIf(isSEBAdmin, .withColumnIf(
new ColumnDefinition<>( isSEBAdmin,
() -> new ColumnDefinition<>(
Domain.USER.ATTR_INSTITUTION_ID, Domain.USER.ATTR_INSTITUTION_ID,
INSTITUTION_TEXT_KEY, INSTITUTION_TEXT_KEY,
userInstitutionNameFunction(this.resourceService), userInstitutionNameFunction(this.resourceService),

View file

@ -16,7 +16,6 @@ public abstract class FieldBuilder<T> {
int spanEmptyCell = -1; int spanEmptyCell = -1;
boolean autoEmptyCellSeparation = false; boolean autoEmptyCellSeparation = false;
String group = null; String group = null;
BooleanSupplier condition = null;
boolean readonly = false; boolean readonly = false;
boolean visible = true; boolean visible = true;
@ -55,11 +54,6 @@ public abstract class FieldBuilder<T> {
return this; return this;
} }
public FieldBuilder<T> withCondition(final BooleanSupplier condition) {
this.condition = condition;
return this;
}
public FieldBuilder<T> readonly(final boolean readonly) { public FieldBuilder<T> readonly(final boolean readonly) {
this.readonly = readonly; this.readonly = readonly;
return this; return this;

View file

@ -145,8 +145,18 @@ public class FormBuilder {
return this; return this;
} }
public FormBuilder addFieldIf(
final BooleanSupplier condition,
final Supplier<FieldBuilder<?>> templateSupplier) {
if (condition.getAsBoolean()) {
return addField(templateSupplier.get());
}
return this;
}
public FormBuilder addField(final FieldBuilder<?> template) { public FormBuilder addField(final FieldBuilder<?> template) {
if (template.condition == null || template.condition.getAsBoolean()) {
template.spanLabel = (template.spanLabel < 0) ? this.defaultSpanLabel : template.spanLabel; template.spanLabel = (template.spanLabel < 0) ? this.defaultSpanLabel : template.spanLabel;
template.spanInput = (template.spanInput < 0) ? this.defaultSpanInput : template.spanInput; template.spanInput = (template.spanInput < 0) ? this.defaultSpanInput : template.spanInput;
template.spanEmptyCell = (template.spanEmptyCell < 0) ? this.defaultSpanEmptyCell : template.spanEmptyCell; template.spanEmptyCell = (template.spanEmptyCell < 0) ? this.defaultSpanEmptyCell : template.spanEmptyCell;
@ -161,7 +171,7 @@ public class FormBuilder {
if (StringUtils.isNoneBlank(template.group)) { if (StringUtils.isNoneBlank(template.group)) {
this.form.addToGroup(template.group, template.name); this.form.addToGroup(template.group, template.name);
} }
}
return this; return this;
} }

View file

@ -12,6 +12,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
@ -80,10 +81,10 @@ public class TableBuilder<ROW extends Entity> {
public TableBuilder<ROW> withColumnIf( public TableBuilder<ROW> withColumnIf(
final BooleanSupplier condition, final BooleanSupplier condition,
final ColumnDefinition<ROW> columnDefinition) { final Supplier<ColumnDefinition<ROW>> columnDefSupplier) {
if (condition != null && condition.getAsBoolean()) { if (condition != null && condition.getAsBoolean()) {
this.columns.add(columnDefinition); this.columns.add(columnDefSupplier.get());
} }
return this; return this;
} }
@ -98,6 +99,17 @@ public class TableBuilder<ROW extends Entity> {
return this; return this;
} }
public TableBuilder<ROW> withDefaultActionIf(
final BooleanSupplier condition,
final Supplier<PageAction> actionSupplier) {
if (condition.getAsBoolean()) {
return withDefaultAction(actionSupplier.get());
}
return this;
}
public TableBuilder<ROW> withDefaultAction(final PageAction action) { public TableBuilder<ROW> withDefaultAction(final PageAction action) {
this.defaultActionFunction = table -> action; this.defaultActionFunction = table -> action;
return this; return this;

View file

@ -110,7 +110,7 @@ public class ExamDAOImpl implements ExamDAO {
} }
if (from != null) { if (from != null) {
if (exam.startTime.isAfter(from)) { if (exam.startTime.isBefore(from)) {
return false; return false;
} }
} }

View file

@ -234,6 +234,7 @@ sebserver.exam.list.column.starttime=Start Time {0}
sebserver.exam.list.column.type=Type sebserver.exam.list.column.type=Type
sebserver.exam.list.empty=No Exams has been found. Please adapt the filter or import one from Quiz sebserver.exam.list.empty=No Exams has been found. Please adapt the filter or import one from Quiz
sebserver.exam.list.modify.out.dated=Running or finished exams cannot be modified.
sebserver.exam.action.list=Exam sebserver.exam.action.list=Exam
sebserver.exam.action.list.view=View Exam sebserver.exam.action.list.view=View Exam