SEBSERV-63 monitoring and running exam list page
This commit is contained in:
parent
43283fe14f
commit
5f9a2c6fe0
27 changed files with 546 additions and 96 deletions
|
@ -17,7 +17,6 @@ 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;
|
||||||
|
|
||||||
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.exam.Exam;
|
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||||
|
@ -49,6 +48,8 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
||||||
@GuiProfile
|
@GuiProfile
|
||||||
public class ExamList implements TemplateComposer {
|
public class ExamList implements TemplateComposer {
|
||||||
|
|
||||||
|
private static final LocTextKey PAGE_TITLE_KEY =
|
||||||
|
new LocTextKey("sebserver.exam.list.title");
|
||||||
private static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION =
|
private static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION =
|
||||||
new LocTextKey("sebserver.exam.list.action.no.modify.privilege");
|
new LocTextKey("sebserver.exam.list.action.no.modify.privilege");
|
||||||
private final static LocTextKey EMPTY_SELECTION_TEXT_KEY =
|
private final static LocTextKey EMPTY_SELECTION_TEXT_KEY =
|
||||||
|
@ -69,6 +70,7 @@ public class ExamList implements TemplateComposer {
|
||||||
new TableFilterAttribute(CriteriaType.TEXT, QuizData.FILTER_ATTR_NAME);
|
new TableFilterAttribute(CriteriaType.TEXT, QuizData.FILTER_ATTR_NAME);
|
||||||
private final TableFilterAttribute startTimeFilter =
|
private final TableFilterAttribute startTimeFilter =
|
||||||
new TableFilterAttribute(CriteriaType.DATE, QuizData.FILTER_ATTR_START_TIME);
|
new TableFilterAttribute(CriteriaType.DATE, QuizData.FILTER_ATTR_START_TIME);
|
||||||
|
private final TableFilterAttribute typeFilter;
|
||||||
|
|
||||||
private final PageService pageService;
|
private final PageService pageService;
|
||||||
private final ResourceService resourceService;
|
private final ResourceService resourceService;
|
||||||
|
@ -77,16 +79,21 @@ public class ExamList implements TemplateComposer {
|
||||||
protected ExamList(
|
protected ExamList(
|
||||||
final PageService pageService,
|
final PageService pageService,
|
||||||
final ResourceService resourceService,
|
final ResourceService resourceService,
|
||||||
@Value("${sebserver.gui.list.page.size}") final Integer pageSize) {
|
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
||||||
|
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.resourceService = resourceService;
|
this.resourceService = resourceService;
|
||||||
this.pageSize = (pageSize != null) ? pageSize : 20;
|
this.pageSize = pageSize;
|
||||||
|
|
||||||
this.lmsFilter = new TableFilterAttribute(
|
this.lmsFilter = new TableFilterAttribute(
|
||||||
CriteriaType.SINGLE_SELECTION,
|
CriteriaType.SINGLE_SELECTION,
|
||||||
LmsSetup.FILTER_ATTR_LMS_SETUP,
|
LmsSetup.FILTER_ATTR_LMS_SETUP,
|
||||||
this.resourceService::lmsSetupResource);
|
this.resourceService::lmsSetupResource);
|
||||||
|
|
||||||
|
this.typeFilter = new TableFilterAttribute(
|
||||||
|
CriteriaType.SINGLE_SELECTION,
|
||||||
|
Exam.FILTER_ATTR_TYPE,
|
||||||
|
this.resourceService::examTypeResources);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -100,9 +107,10 @@ public class ExamList implements TemplateComposer {
|
||||||
// content page layout with title
|
// content page layout with title
|
||||||
final Composite content = widgetFactory.defaultPageLayout(
|
final Composite content = widgetFactory.defaultPageLayout(
|
||||||
pageContext.getParent(),
|
pageContext.getParent(),
|
||||||
new LocTextKey("sebserver.exam.list.title"));
|
PAGE_TITLE_KEY);
|
||||||
|
|
||||||
final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(pageContext.clearEntityKeys());
|
final PageActionBuilder actionBuilder = this.pageService
|
||||||
|
.pageActionBuilder(pageContext.clearEntityKeys());
|
||||||
|
|
||||||
// table
|
// table
|
||||||
final EntityTable<Exam> table =
|
final EntityTable<Exam> table =
|
||||||
|
@ -132,7 +140,8 @@ public class ExamList implements TemplateComposer {
|
||||||
.withColumn(new ColumnDefinition<>(
|
.withColumn(new ColumnDefinition<>(
|
||||||
Domain.EXAM.ATTR_TYPE,
|
Domain.EXAM.ATTR_TYPE,
|
||||||
COLUMN_TITLE_TYPE_KEY,
|
COLUMN_TITLE_TYPE_KEY,
|
||||||
this::examTypeName)
|
this.resourceService::examTypeName)
|
||||||
|
.withFilter(this.typeFilter)
|
||||||
.sortable())
|
.sortable())
|
||||||
.withDefaultAction(actionBuilder
|
.withDefaultAction(actionBuilder
|
||||||
.newAction(ActionDefinition.EXAM_VIEW_FROM_LIST)
|
.newAction(ActionDefinition.EXAM_VIEW_FROM_LIST)
|
||||||
|
@ -177,13 +186,4 @@ public class ExamList implements TemplateComposer {
|
||||||
.apply(String.valueOf(exam.lmsSetupId));
|
.apply(String.valueOf(exam.lmsSetupId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String examTypeName(final Exam exam) {
|
|
||||||
if (exam.type == null) {
|
|
||||||
return Constants.EMPTY_NOTE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.resourceService.getI18nSupport()
|
|
||||||
.getText(ResourceService.EXAM_TYPE_PREFIX + exam.type.name());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
package ch.ethz.seb.sebserver.gui.content;
|
package ch.ethz.seb.sebserver.gui.content;
|
||||||
|
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@ -51,15 +52,18 @@ public class InstitutionList implements TemplateComposer {
|
||||||
private final PageService pageService;
|
private final PageService pageService;
|
||||||
private final RestService restService;
|
private final RestService restService;
|
||||||
private final CurrentUser currentUser;
|
private final CurrentUser currentUser;
|
||||||
|
private final int pageSize;
|
||||||
|
|
||||||
protected InstitutionList(
|
protected InstitutionList(
|
||||||
final PageService pageService,
|
final PageService pageService,
|
||||||
final RestService restService,
|
final RestService restService,
|
||||||
final CurrentUser currentUser) {
|
final CurrentUser currentUser,
|
||||||
|
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
||||||
|
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.restService = restService;
|
this.restService = restService;
|
||||||
this.currentUser = currentUser;
|
this.currentUser = currentUser;
|
||||||
|
this.pageSize = pageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -75,7 +79,7 @@ public class InstitutionList implements TemplateComposer {
|
||||||
final EntityTable<Institution> table =
|
final EntityTable<Institution> table =
|
||||||
this.pageService.entityTableBuilder(this.restService.getRestCall(GetInstitutionPage.class))
|
this.pageService.entityTableBuilder(this.restService.getRestCall(GetInstitutionPage.class))
|
||||||
.withEmptyMessage(EMPTY_LIST_TEXT_KEY)
|
.withEmptyMessage(EMPTY_LIST_TEXT_KEY)
|
||||||
.withPaging(3)
|
.withPaging(this.pageSize)
|
||||||
.withColumn(new ColumnDefinition<>(
|
.withColumn(new ColumnDefinition<>(
|
||||||
Domain.INSTITUTION.ATTR_NAME,
|
Domain.INSTITUTION.ATTR_NAME,
|
||||||
NAME_TEXT_KEY,
|
NAME_TEXT_KEY,
|
||||||
|
|
|
@ -20,6 +20,7 @@ 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.Entity;
|
||||||
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.UserInfo;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
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;
|
||||||
|
@ -66,6 +67,7 @@ public class LmsSetupList implements TemplateComposer {
|
||||||
private final TableFilterAttribute nameFilter =
|
private final TableFilterAttribute nameFilter =
|
||||||
new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME);
|
new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME);
|
||||||
private final TableFilterAttribute typeFilter;
|
private final TableFilterAttribute typeFilter;
|
||||||
|
private final TableFilterAttribute activityFilter;
|
||||||
|
|
||||||
private final PageService pageService;
|
private final PageService pageService;
|
||||||
private final ResourceService resourceService;
|
private final ResourceService resourceService;
|
||||||
|
@ -74,11 +76,11 @@ public class LmsSetupList implements TemplateComposer {
|
||||||
protected LmsSetupList(
|
protected LmsSetupList(
|
||||||
final PageService pageService,
|
final PageService pageService,
|
||||||
final ResourceService resourceService,
|
final ResourceService resourceService,
|
||||||
@Value("${sebserver.gui.list.page.size}") final Integer pageSize) {
|
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
||||||
|
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.resourceService = resourceService;
|
this.resourceService = resourceService;
|
||||||
this.pageSize = (pageSize != null) ? pageSize : 20;
|
this.pageSize = pageSize;
|
||||||
|
|
||||||
this.institutionFilter = new TableFilterAttribute(
|
this.institutionFilter = new TableFilterAttribute(
|
||||||
CriteriaType.SINGLE_SELECTION,
|
CriteriaType.SINGLE_SELECTION,
|
||||||
|
@ -89,6 +91,11 @@ public class LmsSetupList implements TemplateComposer {
|
||||||
CriteriaType.SINGLE_SELECTION,
|
CriteriaType.SINGLE_SELECTION,
|
||||||
Domain.LMS_SETUP.ATTR_LMS_TYPE,
|
Domain.LMS_SETUP.ATTR_LMS_TYPE,
|
||||||
this.resourceService::lmsTypeResources);
|
this.resourceService::lmsTypeResources);
|
||||||
|
|
||||||
|
this.activityFilter = new TableFilterAttribute(
|
||||||
|
CriteriaType.SINGLE_SELECTION,
|
||||||
|
UserInfo.FILTER_ATTR_ACTIVE,
|
||||||
|
this.resourceService::activityResources);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -135,6 +142,7 @@ public class LmsSetupList implements TemplateComposer {
|
||||||
Domain.LMS_SETUP.ATTR_ACTIVE,
|
Domain.LMS_SETUP.ATTR_ACTIVE,
|
||||||
ACTIVITY_TEXT_KEY,
|
ACTIVITY_TEXT_KEY,
|
||||||
LmsSetup::getActive)
|
LmsSetup::getActive)
|
||||||
|
.withFilter(this.activityFilter)
|
||||||
.sortable())
|
.sortable())
|
||||||
.withDefaultAction(actionBuilder
|
.withDefaultAction(actionBuilder
|
||||||
.newAction(ActionDefinition.LMS_SETUP_VIEW_FROM_LIST)
|
.newAction(ActionDefinition.LMS_SETUP_VIEW_FROM_LIST)
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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 org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class MonitoringRunningExam implements TemplateComposer {
|
||||||
|
|
||||||
|
public MonitoringRunningExam() {
|
||||||
|
// TODO Auto-generated constructor stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compose(final PageContext pageContext) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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 org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
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.user.UserRole;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
|
||||||
|
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.PageService.PageActionBuilder;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
||||||
|
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.session.GetRunningExamPage;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class MonitoringRunningExamList implements TemplateComposer {
|
||||||
|
|
||||||
|
private static final LocTextKey PAGE_TITLE_KEY =
|
||||||
|
new LocTextKey("sebserver.monitoring.exam.list.title");
|
||||||
|
private final static LocTextKey EMPTY_SELECTION_TEXT_KEY =
|
||||||
|
new LocTextKey("sebserver.monitoring.exam.info.pleaseSelect");
|
||||||
|
private final static LocTextKey COLUMN_TITLE_NAME_KEY =
|
||||||
|
new LocTextKey("sebserver.monitoring.exam.list.column.name");
|
||||||
|
private final static LocTextKey COLUMN_TITLE_TYPE_KEY =
|
||||||
|
new LocTextKey("sebserver.monitoring.exam.list.column.type");
|
||||||
|
private final static LocTextKey EMPTY_LIST_TEXT_KEY =
|
||||||
|
new LocTextKey("sebserver.monitoring.exam.list.empty");
|
||||||
|
|
||||||
|
private final TableFilterAttribute nameFilter =
|
||||||
|
new TableFilterAttribute(CriteriaType.TEXT, QuizData.FILTER_ATTR_NAME);
|
||||||
|
private final TableFilterAttribute typeFilter;
|
||||||
|
|
||||||
|
private final PageService pageService;
|
||||||
|
private final ResourceService resourceService;
|
||||||
|
private final int pageSize;
|
||||||
|
|
||||||
|
protected MonitoringRunningExamList(
|
||||||
|
final PageService pageService,
|
||||||
|
final ResourceService resourceService,
|
||||||
|
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
||||||
|
|
||||||
|
this.pageService = pageService;
|
||||||
|
this.resourceService = resourceService;
|
||||||
|
this.pageSize = pageSize;
|
||||||
|
|
||||||
|
this.typeFilter = new TableFilterAttribute(
|
||||||
|
CriteriaType.SINGLE_SELECTION,
|
||||||
|
Exam.FILTER_ATTR_TYPE,
|
||||||
|
this.resourceService::examTypeResources);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compose(final PageContext pageContext) {
|
||||||
|
final WidgetFactory widgetFactory = this.pageService.getWidgetFactory();
|
||||||
|
final CurrentUser currentUser = this.resourceService.getCurrentUser();
|
||||||
|
final RestService restService = this.resourceService.getRestService();
|
||||||
|
final I18nSupport i18nSupport = this.resourceService.getI18nSupport();
|
||||||
|
|
||||||
|
// content page layout with title
|
||||||
|
final Composite content = widgetFactory.defaultPageLayout(
|
||||||
|
pageContext.getParent(),
|
||||||
|
PAGE_TITLE_KEY);
|
||||||
|
|
||||||
|
final PageActionBuilder actionBuilder = this.pageService
|
||||||
|
.pageActionBuilder(pageContext.clearEntityKeys());
|
||||||
|
|
||||||
|
// table
|
||||||
|
final EntityTable<Exam> table =
|
||||||
|
this.pageService.entityTableBuilder(restService.getRestCall(GetRunningExamPage.class))
|
||||||
|
.withEmptyMessage(EMPTY_LIST_TEXT_KEY)
|
||||||
|
.withPaging(this.pageSize)
|
||||||
|
.withColumn(new ColumnDefinition<>(
|
||||||
|
QuizData.QUIZ_ATTR_NAME,
|
||||||
|
COLUMN_TITLE_NAME_KEY,
|
||||||
|
Exam::getName)
|
||||||
|
.withFilter(this.nameFilter)
|
||||||
|
.sortable())
|
||||||
|
.withColumn(new ColumnDefinition<>(
|
||||||
|
Domain.EXAM.ATTR_TYPE,
|
||||||
|
COLUMN_TITLE_TYPE_KEY,
|
||||||
|
this.resourceService::examTypeName)
|
||||||
|
.withFilter(this.typeFilter)
|
||||||
|
.sortable())
|
||||||
|
.withColumn(new ColumnDefinition<>(
|
||||||
|
QuizData.QUIZ_ATTR_START_TIME,
|
||||||
|
new LocTextKey(
|
||||||
|
"sebserver.monitoring.exam.list.column.startTime",
|
||||||
|
i18nSupport.getUsersTimeZoneTitleSuffix()),
|
||||||
|
Exam::getStartTime)
|
||||||
|
.sortable())
|
||||||
|
.withColumn(new ColumnDefinition<>(
|
||||||
|
QuizData.QUIZ_ATTR_END_TIME,
|
||||||
|
new LocTextKey(
|
||||||
|
"sebserver.monitoring.exam.list.column.endTime",
|
||||||
|
i18nSupport.getUsersTimeZoneTitleSuffix()),
|
||||||
|
Exam::getEndTime)
|
||||||
|
.sortable())
|
||||||
|
.withDefaultAction(actionBuilder
|
||||||
|
.newAction(ActionDefinition.MONITOR_EXAM)
|
||||||
|
.create())
|
||||||
|
.compose(content);
|
||||||
|
|
||||||
|
actionBuilder
|
||||||
|
|
||||||
|
.newAction(ActionDefinition.MONITOR_EXAM)
|
||||||
|
.withSelect(table::getSelection, PageAction::applySingleSelection, EMPTY_SELECTION_TEXT_KEY)
|
||||||
|
.publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -94,12 +94,12 @@ public class QuizDiscoveryList implements TemplateComposer {
|
||||||
protected QuizDiscoveryList(
|
protected QuizDiscoveryList(
|
||||||
final PageService pageService,
|
final PageService pageService,
|
||||||
final ResourceService resourceService,
|
final ResourceService resourceService,
|
||||||
@Value("${sebserver.gui.list.page.size}") final Integer pageSize) {
|
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
||||||
|
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.widgetFactory = pageService.getWidgetFactory();
|
this.widgetFactory = pageService.getWidgetFactory();
|
||||||
this.resourceService = resourceService;
|
this.resourceService = resourceService;
|
||||||
this.pageSize = (pageSize != null) ? pageSize : 20;
|
this.pageSize = pageSize;
|
||||||
|
|
||||||
this.lmsFilter = new TableFilterAttribute(
|
this.lmsFilter = new TableFilterAttribute(
|
||||||
CriteriaType.SINGLE_SELECTION,
|
CriteriaType.SINGLE_SELECTION,
|
||||||
|
|
|
@ -22,6 +22,7 @@ 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.Entity;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig;
|
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
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;
|
||||||
|
@ -72,6 +73,7 @@ public class SebClientConfigList implements TemplateComposer {
|
||||||
DateTime.now(DateTimeZone.UTC)
|
DateTime.now(DateTimeZone.UTC)
|
||||||
.minusYears(1)
|
.minusYears(1)
|
||||||
.toString(Constants.DEFAULT_DATE_TIME_FORMAT));
|
.toString(Constants.DEFAULT_DATE_TIME_FORMAT));
|
||||||
|
private final TableFilterAttribute activityFilter;
|
||||||
|
|
||||||
private final PageService pageService;
|
private final PageService pageService;
|
||||||
private final RestService restService;
|
private final RestService restService;
|
||||||
|
@ -83,7 +85,7 @@ public class SebClientConfigList implements TemplateComposer {
|
||||||
final PageService pageService,
|
final PageService pageService,
|
||||||
final RestService restService,
|
final RestService restService,
|
||||||
final CurrentUser currentUser,
|
final CurrentUser currentUser,
|
||||||
@Value("${sebserver.gui.list.page.size}") final Integer pageSize) {
|
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
||||||
|
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.restService = restService;
|
this.restService = restService;
|
||||||
|
@ -95,6 +97,11 @@ public class SebClientConfigList implements TemplateComposer {
|
||||||
CriteriaType.SINGLE_SELECTION,
|
CriteriaType.SINGLE_SELECTION,
|
||||||
Entity.FILTER_ATTR_INSTITUTION,
|
Entity.FILTER_ATTR_INSTITUTION,
|
||||||
this.resourceService::institutionResource);
|
this.resourceService::institutionResource);
|
||||||
|
|
||||||
|
this.activityFilter = new TableFilterAttribute(
|
||||||
|
CriteriaType.SINGLE_SELECTION,
|
||||||
|
UserInfo.FILTER_ATTR_ACTIVE,
|
||||||
|
this.resourceService::activityResources);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -139,6 +146,7 @@ public class SebClientConfigList implements TemplateComposer {
|
||||||
Domain.SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE,
|
Domain.SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE,
|
||||||
ACTIVE_TEXT_KEY,
|
ACTIVE_TEXT_KEY,
|
||||||
SebClientConfig::getActive)
|
SebClientConfig::getActive)
|
||||||
|
.withFilter(this.activityFilter)
|
||||||
.sortable())
|
.sortable())
|
||||||
.withDefaultAction(pageActionBuilder
|
.withDefaultAction(pageActionBuilder
|
||||||
.newAction(ActionDefinition.SEB_CLIENT_CONFIG_VIEW_FROM_LIST)
|
.newAction(ActionDefinition.SEB_CLIENT_CONFIG_VIEW_FROM_LIST)
|
||||||
|
|
|
@ -73,7 +73,7 @@ public class SebExamConfigList implements TemplateComposer {
|
||||||
final PageService pageService,
|
final PageService pageService,
|
||||||
final RestService restService,
|
final RestService restService,
|
||||||
final CurrentUser currentUser,
|
final CurrentUser currentUser,
|
||||||
@Value("${sebserver.gui.list.page.size}") final Integer pageSize) {
|
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
||||||
|
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.restService = restService;
|
this.restService = restService;
|
||||||
|
|
|
@ -73,6 +73,7 @@ public class UserAccountList implements TemplateComposer {
|
||||||
private final TableFilterAttribute mailFilter =
|
private final TableFilterAttribute mailFilter =
|
||||||
new TableFilterAttribute(CriteriaType.TEXT, UserInfo.FILTER_ATTR_EMAIL);
|
new TableFilterAttribute(CriteriaType.TEXT, UserInfo.FILTER_ATTR_EMAIL);
|
||||||
private final TableFilterAttribute languageFilter;
|
private final TableFilterAttribute languageFilter;
|
||||||
|
private final TableFilterAttribute activityFilter;
|
||||||
|
|
||||||
// dependencies
|
// dependencies
|
||||||
private final PageService pageService;
|
private final PageService pageService;
|
||||||
|
@ -82,11 +83,11 @@ public class UserAccountList implements TemplateComposer {
|
||||||
protected UserAccountList(
|
protected UserAccountList(
|
||||||
final PageService pageService,
|
final PageService pageService,
|
||||||
final ResourceService resourceService,
|
final ResourceService resourceService,
|
||||||
@Value("${sebserver.gui.list.page.size}") final Integer pageSize) {
|
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
||||||
|
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.resourceService = resourceService;
|
this.resourceService = resourceService;
|
||||||
this.pageSize = (pageSize != null) ? pageSize : 20;
|
this.pageSize = pageSize;
|
||||||
|
|
||||||
this.institutionFilter = new TableFilterAttribute(
|
this.institutionFilter = new TableFilterAttribute(
|
||||||
CriteriaType.SINGLE_SELECTION,
|
CriteriaType.SINGLE_SELECTION,
|
||||||
|
@ -97,6 +98,11 @@ public class UserAccountList implements TemplateComposer {
|
||||||
CriteriaType.SINGLE_SELECTION,
|
CriteriaType.SINGLE_SELECTION,
|
||||||
UserInfo.FILTER_ATTR_LANGUAGE,
|
UserInfo.FILTER_ATTR_LANGUAGE,
|
||||||
this.resourceService::languageResources);
|
this.resourceService::languageResources);
|
||||||
|
|
||||||
|
this.activityFilter = new TableFilterAttribute(
|
||||||
|
CriteriaType.SINGLE_SELECTION,
|
||||||
|
UserInfo.FILTER_ATTR_ACTIVE,
|
||||||
|
this.resourceService::activityResources);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -154,12 +160,13 @@ public class UserAccountList implements TemplateComposer {
|
||||||
.withFilter(this.languageFilter)
|
.withFilter(this.languageFilter)
|
||||||
.localized()
|
.localized()
|
||||||
.sortable()
|
.sortable()
|
||||||
.widthProportion(2))
|
.widthProportion(1))
|
||||||
.withColumn(new ColumnDefinition<>(
|
.withColumn(new ColumnDefinition<>(
|
||||||
Domain.USER.ATTR_ACTIVE,
|
Domain.USER.ATTR_ACTIVE,
|
||||||
ACTIVE_TEXT_KEY,
|
ACTIVE_TEXT_KEY,
|
||||||
UserInfo::getActive)
|
UserInfo::getActive)
|
||||||
.sortable()
|
.sortable()
|
||||||
|
.withFilter(this.activityFilter)
|
||||||
.widthProportion(1))
|
.widthProportion(1))
|
||||||
.withDefaultAction(actionBuilder
|
.withDefaultAction(actionBuilder
|
||||||
.newAction(ActionDefinition.USER_ACCOUNT_VIEW_FROM_LIST)
|
.newAction(ActionDefinition.USER_ACCOUNT_VIEW_FROM_LIST)
|
||||||
|
|
|
@ -21,6 +21,7 @@ public enum ActionCategory {
|
||||||
INDICATOR_LIST(new LocTextKey("sebserver.exam.indicator.list.actions"), 2),
|
INDICATOR_LIST(new LocTextKey("sebserver.exam.indicator.list.actions"), 2),
|
||||||
SEB_CLIENT_CONFIG_LIST(new LocTextKey("sebserver.clientconfig.list.actions"), 1),
|
SEB_CLIENT_CONFIG_LIST(new LocTextKey("sebserver.clientconfig.list.actions"), 1),
|
||||||
SEB_EXAM_CONFIG_LIST(new LocTextKey("sebserver.examconfig.list.actions"), 1),
|
SEB_EXAM_CONFIG_LIST(new LocTextKey("sebserver.examconfig.list.actions"), 1),
|
||||||
|
RUNNING_EXAM_LIST(new LocTextKey("sebserver.monitoring.exam.list.actions"), 1),
|
||||||
VARIA(new LocTextKey("sebserver.overall.action.category.varia"), 100),
|
VARIA(new LocTextKey("sebserver.overall.action.category.varia"), 100),
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -388,7 +388,14 @@ public enum ActionDefinition {
|
||||||
PageStateDefinition.SEB_EXAM_CONFIG_EDIT,
|
PageStateDefinition.SEB_EXAM_CONFIG_EDIT,
|
||||||
ActionCategory.SEB_EXAM_CONFIG_LIST),
|
ActionCategory.SEB_EXAM_CONFIG_LIST),
|
||||||
|
|
||||||
;
|
RUNNING_EXAM_VIEW_LIST(
|
||||||
|
new LocTextKey("sebserver.monitoring.action.list"),
|
||||||
|
PageStateDefinition.MONITORING_RUNNING_EXAM_LIST),
|
||||||
|
MONITOR_EXAM(
|
||||||
|
new LocTextKey("sebserver.monitoring.exam.action.list.view"),
|
||||||
|
ImageIcon.SHOW,
|
||||||
|
PageStateDefinition.MONITORING_RUNNING_EXAM,
|
||||||
|
ActionCategory.RUNNING_EXAM_LIST);
|
||||||
|
|
||||||
public final LocTextKey title;
|
public final LocTextKey title;
|
||||||
public final ImageIcon icon;
|
public final ImageIcon icon;
|
||||||
|
|
|
@ -202,7 +202,18 @@ public class ActivitiesPane implements TemplateComposer {
|
||||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_LIST)
|
.newAction(ActionDefinition.SEB_EXAM_CONFIG_LIST)
|
||||||
.create());
|
.create());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monitoring exams
|
||||||
|
if (this.currentUser.get().hasRole(UserRole.EXAM_SUPPORTER)) {
|
||||||
|
final TreeItem clientConfig = this.widgetFactory.treeItemLocalized(
|
||||||
|
navigation,
|
||||||
|
ActivityDefinition.MONITORING_EXAMS.displayName);
|
||||||
|
injectActivitySelection(
|
||||||
|
clientConfig,
|
||||||
|
actionBuilder
|
||||||
|
.newAction(ActionDefinition.RUNNING_EXAM_VIEW_LIST)
|
||||||
|
.create());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO other activities
|
// TODO other activities
|
||||||
|
|
|
@ -19,7 +19,8 @@ public enum ActivityDefinition implements Activity {
|
||||||
EXAM(new LocTextKey("sebserver.exam.action.list")),
|
EXAM(new LocTextKey("sebserver.exam.action.list")),
|
||||||
SEB_CONFIGURATION(new LocTextKey("sebserver.sebconfig.activity.name")),
|
SEB_CONFIGURATION(new LocTextKey("sebserver.sebconfig.activity.name")),
|
||||||
SEB_CLIENT_CONFIG(new LocTextKey("sebserver.clientconfig.action.list")),
|
SEB_CLIENT_CONFIG(new LocTextKey("sebserver.clientconfig.action.list")),
|
||||||
SEB_EXAM_CONFIG(new LocTextKey("sebserver.examconfig.action.list"));
|
SEB_EXAM_CONFIG(new LocTextKey("sebserver.examconfig.action.list")),
|
||||||
|
MONITORING_EXAMS(new LocTextKey("sebserver.monitoring.action.list"));
|
||||||
|
|
||||||
public final LocTextKey displayName;
|
public final LocTextKey displayName;
|
||||||
|
|
||||||
|
|
|
@ -16,12 +16,14 @@ import ch.ethz.seb.sebserver.gui.content.InstitutionForm;
|
||||||
import ch.ethz.seb.sebserver.gui.content.InstitutionList;
|
import ch.ethz.seb.sebserver.gui.content.InstitutionList;
|
||||||
import ch.ethz.seb.sebserver.gui.content.LmsSetupForm;
|
import ch.ethz.seb.sebserver.gui.content.LmsSetupForm;
|
||||||
import ch.ethz.seb.sebserver.gui.content.LmsSetupList;
|
import ch.ethz.seb.sebserver.gui.content.LmsSetupList;
|
||||||
|
import ch.ethz.seb.sebserver.gui.content.MonitoringRunningExam;
|
||||||
|
import ch.ethz.seb.sebserver.gui.content.MonitoringRunningExamList;
|
||||||
import ch.ethz.seb.sebserver.gui.content.QuizDiscoveryList;
|
import ch.ethz.seb.sebserver.gui.content.QuizDiscoveryList;
|
||||||
import ch.ethz.seb.sebserver.gui.content.SebClientConfigForm;
|
import ch.ethz.seb.sebserver.gui.content.SebClientConfigForm;
|
||||||
import ch.ethz.seb.sebserver.gui.content.SebClientConfigList;
|
import ch.ethz.seb.sebserver.gui.content.SebClientConfigList;
|
||||||
import ch.ethz.seb.sebserver.gui.content.SebExamConfigSettingsForm;
|
|
||||||
import ch.ethz.seb.sebserver.gui.content.SebExamConfigList;
|
import ch.ethz.seb.sebserver.gui.content.SebExamConfigList;
|
||||||
import ch.ethz.seb.sebserver.gui.content.SebExamConfigPropForm;
|
import ch.ethz.seb.sebserver.gui.content.SebExamConfigPropForm;
|
||||||
|
import ch.ethz.seb.sebserver.gui.content.SebExamConfigSettingsForm;
|
||||||
import ch.ethz.seb.sebserver.gui.content.UserAccountChangePasswordForm;
|
import ch.ethz.seb.sebserver.gui.content.UserAccountChangePasswordForm;
|
||||||
import ch.ethz.seb.sebserver.gui.content.UserAccountForm;
|
import ch.ethz.seb.sebserver.gui.content.UserAccountForm;
|
||||||
import ch.ethz.seb.sebserver.gui.content.UserAccountList;
|
import ch.ethz.seb.sebserver.gui.content.UserAccountList;
|
||||||
|
@ -62,6 +64,9 @@ public enum PageStateDefinition implements PageState {
|
||||||
SEB_EXAM_CONFIG_PROP_EDIT(Type.FORM_EDIT, SebExamConfigPropForm.class, ActivityDefinition.SEB_EXAM_CONFIG),
|
SEB_EXAM_CONFIG_PROP_EDIT(Type.FORM_EDIT, SebExamConfigPropForm.class, ActivityDefinition.SEB_EXAM_CONFIG),
|
||||||
SEB_EXAM_CONFIG_EDIT(Type.FORM_VIEW, SebExamConfigSettingsForm.class, ActivityDefinition.SEB_EXAM_CONFIG),
|
SEB_EXAM_CONFIG_EDIT(Type.FORM_VIEW, SebExamConfigSettingsForm.class, ActivityDefinition.SEB_EXAM_CONFIG),
|
||||||
|
|
||||||
|
MONITORING_RUNNING_EXAM_LIST(Type.LIST_VIEW, MonitoringRunningExamList.class, ActivityDefinition.MONITORING_EXAMS),
|
||||||
|
MONITORING_RUNNING_EXAM(Type.FORM_VIEW, MonitoringRunningExam.class, ActivityDefinition.MONITORING_EXAMS)
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
public final Type type;
|
public final Type type;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service;
|
package ch.ethz.seb.sebserver.gui.service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -25,6 +26,7 @@ import org.springframework.stereotype.Service;
|
||||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityName;
|
import ch.ethz.seb.sebserver.gbl.model.EntityName;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType;
|
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap;
|
import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
|
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
|
||||||
|
@ -93,6 +95,13 @@ public class ResourceService {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Tuple<String>> activityResources() {
|
||||||
|
final List<Tuple<String>> result = new ArrayList<>();
|
||||||
|
result.add(new Tuple<>("true", this.i18nSupport.getText("sebserver.overall.status.active")));
|
||||||
|
result.add(new Tuple<>("false", this.i18nSupport.getText("sebserver.overall.status.inactive")));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public List<Tuple<String>> lmsTypeResources() {
|
public List<Tuple<String>> lmsTypeResources() {
|
||||||
return Arrays.asList(LmsType.values())
|
return Arrays.asList(LmsType.values())
|
||||||
.stream()
|
.stream()
|
||||||
|
@ -119,20 +128,6 @@ public class ResourceService {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
// public Function<String, String> getExamConfigurationNameFunction() {
|
|
||||||
// final Map<String, String> idNameMap = getExamConfigurationSelection()
|
|
||||||
// .getOr(Collections.emptyList())
|
|
||||||
// .stream()
|
|
||||||
// .collect(Collectors.toMap(e -> e.modelId, e -> e.name));
|
|
||||||
//
|
|
||||||
// return id -> {
|
|
||||||
// if (!idNameMap.containsKey(id)) {
|
|
||||||
// return Constants.EMPTY_NOTE;
|
|
||||||
// }
|
|
||||||
// return idNameMap.get(id);
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
public List<Tuple<String>> userRoleResources() {
|
public List<Tuple<String>> userRoleResources() {
|
||||||
return UserRole.publicRolesForUser(this.currentUser.get())
|
return UserRole.publicRolesForUser(this.currentUser.get())
|
||||||
.stream()
|
.stream()
|
||||||
|
@ -229,6 +224,7 @@ public class ResourceService {
|
||||||
public List<Tuple<String>> examTypeResources() {
|
public List<Tuple<String>> examTypeResources() {
|
||||||
return Arrays.asList(ExamType.values())
|
return Arrays.asList(ExamType.values())
|
||||||
.stream()
|
.stream()
|
||||||
|
.filter(type -> type != ExamType.UNDEFINED)
|
||||||
.map(type -> new Tuple<>(
|
.map(type -> new Tuple<>(
|
||||||
type.name(),
|
type.name(),
|
||||||
this.i18nSupport.getText(EXAM_TYPE_PREFIX + type.name())))
|
this.i18nSupport.getText(EXAM_TYPE_PREFIX + type.name())))
|
||||||
|
@ -313,4 +309,13 @@ public class ResourceService {
|
||||||
.call();
|
.call();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String examTypeName(final Exam exam) {
|
||||||
|
if (exam.type == null) {
|
||||||
|
return Constants.EMPTY_NOTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.i18nSupport
|
||||||
|
.getText(ResourceService.EXAM_TYPE_PREFIX + exam.type.name());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class GetRunningExamPage extends RestCall<Page<Exam>> {
|
||||||
|
|
||||||
|
public GetRunningExamPage() {
|
||||||
|
super(new TypeKey<>(
|
||||||
|
CallType.GET_PAGE,
|
||||||
|
EntityType.EXAM,
|
||||||
|
new TypeReference<Page<Exam>>() {
|
||||||
|
}),
|
||||||
|
HttpMethod.GET,
|
||||||
|
MediaType.APPLICATION_FORM_URLENCODED,
|
||||||
|
API.EXAM_MONITORING_ENDPOINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -92,9 +92,10 @@ public class TableFilter<ROW extends Entity> {
|
||||||
.map(FilterComponent::reset)
|
.map(FilterComponent::reset)
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
|
|
||||||
final FilterComponent lastComp = this.components.get(this.components.size() - 1);
|
FilterComponent lastComp = this.components.get(this.components.size() - 1);
|
||||||
if (lastComp instanceof TableFilter.NullFilter) {
|
while (lastComp instanceof TableFilter.NullFilter) {
|
||||||
this.components.remove(lastComp);
|
this.components.remove(lastComp);
|
||||||
|
lastComp = this.components.get(this.components.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
addActions();
|
addActions();
|
||||||
|
|
|
@ -269,6 +269,9 @@ public class PaginationServiceImpl implements PaginationService {
|
||||||
configurationNodeTableMap.put(
|
configurationNodeTableMap.put(
|
||||||
Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION,
|
Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION,
|
||||||
ConfigurationNodeRecordDynamicSqlSupport.description.name());
|
ConfigurationNodeRecordDynamicSqlSupport.description.name());
|
||||||
|
configurationNodeTableMap.put(
|
||||||
|
Domain.CONFIGURATION_NODE.ATTR_STATUS,
|
||||||
|
ConfigurationNodeRecordDynamicSqlSupport.status.name());
|
||||||
this.sortColumnMapping.put(
|
this.sortColumnMapping.put(
|
||||||
ConfigurationNodeRecordDynamicSqlSupport.configurationNodeRecord.name(),
|
ConfigurationNodeRecordDynamicSqlSupport.configurationNodeRecord.name(),
|
||||||
configurationNodeTableMap);
|
configurationNodeTableMap);
|
||||||
|
|
|
@ -10,10 +10,12 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session;
|
||||||
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
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.session.ClientConnectionData;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||||
|
|
||||||
/** A Service to handle running exam sessions */
|
/** A Service to handle running exam sessions */
|
||||||
public interface ExamSessionService {
|
public interface ExamSessionService {
|
||||||
|
@ -33,13 +35,22 @@ public interface ExamSessionService {
|
||||||
* @return Result referencing the running Exam or an error if the Exam not exists or is not currently running */
|
* @return Result referencing the running Exam or an error if the Exam not exists or is not currently running */
|
||||||
Result<Exam> getRunningExam(Long examId);
|
Result<Exam> getRunningExam(Long examId);
|
||||||
|
|
||||||
/** Gets all all currently running Exams for a particular Institution.
|
/** Gets all currently running Exams for a particular Institution.
|
||||||
*
|
*
|
||||||
* @param institutionId the Institution identifier
|
* @param institutionId the Institution identifier
|
||||||
* @return Result referencing the list of all currently running Exams of the institution or to an error if
|
* @return Result referencing the list of all currently running Exams of the institution or to an error if
|
||||||
* happened. */
|
* happened. */
|
||||||
Result<Collection<Exam>> getRunningExamsForInstitution(Long institutionId);
|
Result<Collection<Exam>> getRunningExamsForInstitution(Long institutionId);
|
||||||
|
|
||||||
|
/** Gets all currently running Exams for a particular FilterMap.
|
||||||
|
*
|
||||||
|
* @param filterMap the FilterMap containing the filter attributes
|
||||||
|
* @param predicate additional filter predicate
|
||||||
|
* @return Result referencing the list of all currently running Exams or to an error if happened. */
|
||||||
|
Result<Collection<Exam>> getFilteredRunningExams(
|
||||||
|
FilterMap filterMap,
|
||||||
|
Predicate<Exam> predicate);
|
||||||
|
|
||||||
/** Streams the default SEB Exam Configuration to a ClientConnection with given connectionToken.
|
/** Streams the default SEB Exam Configuration to a ClientConnection with given connectionToken.
|
||||||
*
|
*
|
||||||
* @param connectionToken The connection token that identifiers the ClientConnection
|
* @param connectionToken The connection token that identifiers the ClientConnection
|
||||||
|
|
|
@ -13,6 +13,7 @@ import java.io.OutputStream;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -30,6 +31,7 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
|
@ -96,6 +98,18 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<Collection<Exam>> getFilteredRunningExams(
|
||||||
|
final FilterMap filterMap,
|
||||||
|
final Predicate<Exam> predicate) {
|
||||||
|
|
||||||
|
return this.examDAO.allMatching(filterMap, predicate)
|
||||||
|
.map(col -> col.stream()
|
||||||
|
.map(exam -> this.examSessionCacheService.getRunningExam(exam.id))
|
||||||
|
.filter(exam -> exam != null)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void streamDefaultExamConfig(
|
public void streamDefaultExamConfig(
|
||||||
final String connectionToken,
|
final String connectionToken,
|
||||||
|
|
|
@ -126,6 +126,7 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler {
|
||||||
final Exception ex,
|
final Exception ex,
|
||||||
final WebRequest request) {
|
final WebRequest request) {
|
||||||
|
|
||||||
|
log.error("Unexpected internal error catched at the API endpoint: ", ex);
|
||||||
return APIMessage.ErrorMessage.UNEXPECTED
|
return APIMessage.ErrorMessage.UNEXPECTED
|
||||||
.createErrorResponse(ex.getMessage());
|
.createErrorResponse(ex.getMessage());
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,7 +144,8 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
pageSize,
|
pageSize,
|
||||||
sort,
|
sort,
|
||||||
getSQLTableOfEntity().name(),
|
getSQLTableOfEntity().name(),
|
||||||
() -> getAll(filterMap)).getOrThrow();
|
() -> getAll(filterMap))
|
||||||
|
.getOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ******************
|
// ******************
|
||||||
|
|
|
@ -120,39 +120,54 @@ public class ExamAdministrationController extends ActivatableEntityController<Ex
|
||||||
EntityType.EXAM,
|
EntityType.EXAM,
|
||||||
institutionId);
|
institutionId);
|
||||||
|
|
||||||
final int pageNum = this.paginationService.getPageNumber(pageNumber);
|
|
||||||
final int pSize = this.paginationService.getPageSize(pageSize);
|
|
||||||
|
|
||||||
final List<Exam> exams = new ArrayList<>(
|
final List<Exam> exams = new ArrayList<>(
|
||||||
this.examDAO.allMatching(new FilterMap(allRequestParams)).getOrThrow());
|
this.examDAO
|
||||||
|
.allMatching(new FilterMap(allRequestParams))
|
||||||
|
.getOrThrow());
|
||||||
|
|
||||||
if (!StringUtils.isBlank(sort)) {
|
return buildSortedExamPage(
|
||||||
final String sortBy = PageSortOrder.decode(sort);
|
this.paginationService.getPageNumber(pageNumber),
|
||||||
if (sortBy.equals(QuizData.QUIZ_ATTR_NAME)) {
|
this.paginationService.getPageSize(pageSize),
|
||||||
Collections.sort(exams, (exam1, exam2) -> exam1.name.compareTo(exam2.name));
|
|
||||||
}
|
|
||||||
if (sortBy.equals(QuizData.FILTER_ATTR_START_TIME)) {
|
|
||||||
Collections.sort(exams, (exam1, exam2) -> exam1.startTime.compareTo(exam2.startTime));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PageSortOrder.DESCENDING == PageSortOrder.getSortOrder(sort)) {
|
|
||||||
Collections.reverse(exams);
|
|
||||||
}
|
|
||||||
|
|
||||||
final int start = (pageNum - 1) * pSize;
|
|
||||||
int end = start + pageSize;
|
|
||||||
if (exams.size() < end) {
|
|
||||||
end = exams.size();
|
|
||||||
}
|
|
||||||
return new Page<>(
|
|
||||||
exams.size() / pSize,
|
|
||||||
pageNum,
|
|
||||||
sort,
|
sort,
|
||||||
exams.subList(start, end));
|
exams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Page<Exam> buildSortedExamPage(
|
||||||
|
final Integer pageNumber,
|
||||||
|
final Integer pageSize,
|
||||||
|
final String sort,
|
||||||
|
final List<Exam> exams) {
|
||||||
|
|
||||||
|
if (!StringUtils.isBlank(sort)) {
|
||||||
|
final String sortBy = PageSortOrder.decode(sort);
|
||||||
|
if (sortBy.equals(Exam.FILTER_ATTR_NAME)) {
|
||||||
|
Collections.sort(exams, (exam1, exam2) -> exam1.name.compareTo(exam2.name));
|
||||||
|
}
|
||||||
|
if (sortBy.equals(Exam.FILTER_ATTR_TYPE)) {
|
||||||
|
Collections.sort(exams, (exam1, exam2) -> exam1.type.compareTo(exam2.type));
|
||||||
|
}
|
||||||
|
if (sortBy.equals(QuizData.FILTER_ATTR_START_TIME)) {
|
||||||
|
Collections.sort(exams, (exam1, exam2) -> exam1.startTime.compareTo(exam2.startTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PageSortOrder.DESCENDING == PageSortOrder.getSortOrder(sort)) {
|
||||||
|
Collections.reverse(exams);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int start = (pageNumber - 1) * pageSize;
|
||||||
|
int end = start + pageSize;
|
||||||
|
if (exams.size() < end) {
|
||||||
|
end = exams.size();
|
||||||
|
}
|
||||||
|
return new Page<>(
|
||||||
|
exams.size() / pageSize,
|
||||||
|
pageNumber,
|
||||||
|
sort,
|
||||||
|
exams.subList(start, end));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Exam createNew(final POSTMapper postParams) {
|
protected Exam createNew(final POSTMapper postParams) {
|
||||||
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
|
||||||
|
|
||||||
public class ExamMonitiroingEndpoint {
|
|
||||||
|
|
||||||
public ExamMonitiroingEndpoint() {
|
|
||||||
// TODO Auto-generated constructor stub
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
|
import org.springframework.web.bind.annotation.InitBinder;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PermissionDeniedException;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
|
||||||
|
|
||||||
|
@WebServiceProfile
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.EXAM_MONITORING_ENDPOINT)
|
||||||
|
public class ExamMonitoringController {
|
||||||
|
|
||||||
|
private final ExamSessionService examSessionService;
|
||||||
|
private final AuthorizationService authorization;
|
||||||
|
private final PaginationService paginationService;
|
||||||
|
|
||||||
|
public ExamMonitoringController(
|
||||||
|
final ExamSessionService examSessionService,
|
||||||
|
final AuthorizationService authorization,
|
||||||
|
final PaginationService paginationService) {
|
||||||
|
|
||||||
|
this.examSessionService = examSessionService;
|
||||||
|
this.authorization = authorization;
|
||||||
|
this.paginationService = paginationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This is called by Spring to initialize the WebDataBinder and is used here to
|
||||||
|
* initialize the default value binding for the institutionId request-parameter
|
||||||
|
* that has the current users insitutionId as default.
|
||||||
|
*
|
||||||
|
* See also UserService.addUsersInstitutionDefaultPropertySupport */
|
||||||
|
@InitBinder
|
||||||
|
public void initBinder(final WebDataBinder binder) throws Exception {
|
||||||
|
this.authorization
|
||||||
|
.getUserService()
|
||||||
|
.addUsersInstitutionDefaultPropertySupport(binder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ******************
|
||||||
|
// * GET (getAll)
|
||||||
|
// ******************
|
||||||
|
|
||||||
|
/** Get a page of all currently running exams
|
||||||
|
*
|
||||||
|
* GET /{api}/{entity-type-endpoint-name}
|
||||||
|
*
|
||||||
|
* GET /admin-api/v1/monitoring
|
||||||
|
* GET /admin-api/v1/monitoring?page_number=2&page_size=10&sort=-name
|
||||||
|
* GET /admin-api/v1/monitoring?name=seb&active=true
|
||||||
|
*
|
||||||
|
* @param institutionId The institution identifier of the request.
|
||||||
|
* Default is the institution identifier of the institution of the current user
|
||||||
|
* @param pageNumber the number of the page that is requested
|
||||||
|
* @param pageSize the size of the page that is requested
|
||||||
|
* @param sort the sort parameter to sort the list of entities before paging
|
||||||
|
* the sort parameter is the name of the entity-model attribute to sort with a leading '-' sign for
|
||||||
|
* descending sort order
|
||||||
|
* @param allRequestParams a MultiValueMap of all request parameter that is used for filtering
|
||||||
|
* @return Page of domain-model-entities of specified type */
|
||||||
|
@RequestMapping(
|
||||||
|
method = RequestMethod.GET,
|
||||||
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||||
|
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||||
|
public Page<Exam> getPage(
|
||||||
|
@RequestParam(
|
||||||
|
name = API.PARAM_INSTITUTION_ID,
|
||||||
|
required = true,
|
||||||
|
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||||
|
@RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber,
|
||||||
|
@RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize,
|
||||||
|
@RequestParam(name = Page.ATTR_SORT, required = false) final String sort,
|
||||||
|
@RequestParam final MultiValueMap<String, String> allRequestParams) {
|
||||||
|
|
||||||
|
// check if user has EXAM_SUPPORTER privilege.
|
||||||
|
final SEBServerUser currentUser = this.authorization
|
||||||
|
.getUserService()
|
||||||
|
.getCurrentUser();
|
||||||
|
|
||||||
|
if (!currentUser.getUserRoles().contains(UserRole.EXAM_SUPPORTER)) {
|
||||||
|
throw new PermissionDeniedException(
|
||||||
|
EntityType.EXAM,
|
||||||
|
PrivilegeType.READ,
|
||||||
|
currentUser.getUserInfo().uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
final FilterMap filterMap = new FilterMap(allRequestParams);
|
||||||
|
|
||||||
|
// if current user has no read access for specified entity type within other institution
|
||||||
|
// then the current users institutionId is put as a SQL filter criteria attribute to extends query performance
|
||||||
|
if (!this.authorization.hasGrant(PrivilegeType.READ, EntityType.EXAM)) {
|
||||||
|
filterMap.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Exam> exams = new ArrayList<>(this.examSessionService
|
||||||
|
.getFilteredRunningExams(filterMap, exam -> true)
|
||||||
|
.getOrThrow());
|
||||||
|
|
||||||
|
return ExamAdministrationController.buildSortedExamPage(
|
||||||
|
this.paginationService.getPageNumber(pageNumber),
|
||||||
|
this.paginationService.getPageSize(pageSize),
|
||||||
|
sort,
|
||||||
|
exams);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -852,11 +852,29 @@ sebserver.examconfig.props.label.enableF10=Enable F10
|
||||||
sebserver.examconfig.props.label.enableF11=Enable F11
|
sebserver.examconfig.props.label.enableF11=Enable F11
|
||||||
sebserver.examconfig.props.label.enableF12=Enable F12
|
sebserver.examconfig.props.label.enableF12=Enable F12
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sebserver.examconfig.props.validation.password.confirm=Please enter correct confirm password
|
sebserver.examconfig.props.validation.password.confirm=Please enter correct confirm password
|
||||||
sebserver.examconfig.props.validation.unexpected=Unexpected error happened. Value was not set correctly
|
sebserver.examconfig.props.validation.unexpected=Unexpected error happened. Value was not set correctly
|
||||||
sebserver.examconfig.props.validation.IntegerTypeValidator=Invalid number
|
sebserver.examconfig.props.validation.IntegerTypeValidator=Invalid number
|
||||||
sebserver.examconfig.props.validation.DecimalTypeValidator=Invalid decimal number
|
sebserver.examconfig.props.validation.DecimalTypeValidator=Invalid decimal number
|
||||||
sebserver.examconfig.props.validation.ExitKeySequenceValidator=Key is already in sequence
|
sebserver.examconfig.props.validation.ExitKeySequenceValidator=Key is already in sequence
|
||||||
sebserver.examconfig.props.validation.WindowsSizeValidator=Invalid number
|
sebserver.examconfig.props.validation.WindowsSizeValidator=Invalid number
|
||||||
|
|
||||||
|
|
||||||
|
################################
|
||||||
|
# Monitoring
|
||||||
|
################################
|
||||||
|
|
||||||
|
sebserver.monitoring.action.list=Monitoring
|
||||||
|
sebserver.monitoring.exam.list.title=Running Exams
|
||||||
|
|
||||||
|
sebserver.monitoring.exam.list.actions=Selected Exam
|
||||||
|
sebserver.monitoring.exam.action.list.view=Monitoring
|
||||||
|
|
||||||
|
|
||||||
|
sebserver.monitoring.exam.info.pleaseSelect=Please select an exam first
|
||||||
|
sebserver.monitoring.exam.list.empty=There are currently no running exams
|
||||||
|
sebserver.monitoring.exam.list.column.name=Name
|
||||||
|
sebserver.monitoring.exam.list.column.type=Type
|
||||||
|
sebserver.monitoring.exam.list.column.startTime=Start Time
|
||||||
|
sebserver.monitoring.exam.list.column.endTime=End Time
|
||||||
|
|
||||||
|
|
|
@ -281,11 +281,11 @@ Combo-Button {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
background-image: gradient(linear, left top, left bottom, from(#ffffff), to(#ffffff));
|
background-image: gradient(linear, left top, left bottom, from(#ffffff), to(#ffffff));
|
||||||
border: none;
|
border: none;
|
||||||
width: 30px;
|
width: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
Combo-Field {
|
Combo-Field {
|
||||||
padding: 3px 10px 1px 10px;
|
padding: 3px 0px 1px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
Combo.error {
|
Combo.error {
|
||||||
|
|
Loading…
Add table
Reference in a new issue