From b6466d8f1dce55127f2efb86637b494c1b74fe10 Mon Sep 17 00:00:00 2001 From: anhefti Date: Mon, 30 Mar 2020 11:03:26 +0200 Subject: [PATCH] new seb restrcition strategy and minor fixes --- .../ch/ethz/seb/sebserver/gbl/api/API.java | 1 + .../seb/sebserver/gbl/model/exam/Exam.java | 24 +++--- .../gbl/model/institution/LmsSetup.java | 4 +- .../ch/ethz/seb/sebserver/gbl/util/Utils.java | 12 +++ .../seb/sebserver/gui/content/ExamForm.java | 25 ++++-- .../sebserver/gui/content/RegisterPage.java | 12 ++- .../gui/service/page/impl/PageAction.java | 3 + .../api/exam/CheckSebRestriction.java | 41 +++++++++ .../sebserver/gui/widget/SingleSelection.java | 3 +- .../sebserver/gui/widget/WidgetFactory.java | 86 +++++++++++-------- .../servicelayer/dao/impl/ExamDAOImpl.java | 17 ++-- .../servicelayer/exam/ExamAdminService.java | 8 +- .../exam/ExamAdminServiceImpl.java | 11 +++ .../lms/impl/SebRestrictionServiceImpl.java | 3 +- .../impl/ExamConfigUpdateServiceImpl.java | 12 ++- .../session/impl/ExamSessionServiceImpl.java | 2 +- .../api/ExamAdministrationController.java | 25 +++++- .../integration/UseCasesIntegrationTest.java | 1 - .../integration/api/admin/ExamAPITest.java | 2 - 19 files changed, 207 insertions(+), 85 deletions(-) create mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/CheckSebRestriction.java diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java index 0075af09..1e636b78 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java @@ -117,6 +117,7 @@ public final class API { public static final String EXAM_ADMINISTRATION_DOWNLOAD_CONFIG_PATH_SEGMENT = "/download-config"; public static final String EXAM_ADMINISTRATION_CONSISTENCY_CHECK_PATH_SEGMENT = "/check-consistency"; public static final String EXAM_ADMINISTRATION_SEB_RESTRICTION_PATH_SEGMENT = "/seb-restriction"; + public static final String EXAM_ADMINISTRATION_CHECK_RESTRICTION_PATH_SEGMENT = "/check-seb-restriction"; public static final String EXAM_INDICATOR_ENDPOINT = "/indicator"; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java index f803b8f1..fa8a0739 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java @@ -48,7 +48,7 @@ public final class Exam implements GrantEntity { null, null, ExamStatus.FINISHED, - Boolean.FALSE, +// Boolean.FALSE, null, Boolean.FALSE, null); @@ -117,8 +117,8 @@ public final class Exam implements GrantEntity { @JsonProperty(EXAM.ATTR_STATUS) public final ExamStatus status; - @JsonProperty(EXAM.ATTR_LMS_SEB_RESTRICTION) - public final Boolean lmsSebRestriction; +// @JsonProperty(EXAM.ATTR_LMS_SEB_RESTRICTION) +// public final Boolean lmsSebRestriction; @JsonProperty(EXAM.ATTR_BROWSER_KEYS) public final String browserExamKeys; @@ -145,7 +145,7 @@ public final class Exam implements GrantEntity { @JsonProperty(EXAM.ATTR_OWNER) final String owner, @JsonProperty(EXAM.ATTR_SUPPORTER) final Collection supporter, @JsonProperty(EXAM.ATTR_STATUS) final ExamStatus status, - @JsonProperty(EXAM.ATTR_LMS_SEB_RESTRICTION) final Boolean lmsSebRestriction, +// @JsonProperty(EXAM.ATTR_LMS_SEB_RESTRICTION) final Boolean lmsSebRestriction, @JsonProperty(EXAM.ATTR_BROWSER_KEYS) final String browserExamKeys, @JsonProperty(EXAM.ATTR_ACTIVE) final Boolean active, @JsonProperty(EXAM.ATTR_LASTUPDATE) final String lastUpdate) { @@ -163,7 +163,7 @@ public final class Exam implements GrantEntity { this.quitPassword = quitPassword; this.owner = owner; this.status = (status != null) ? status : getStatusFromDate(startTime, endTime); - this.lmsSebRestriction = (lmsSebRestriction != null) ? lmsSebRestriction : Boolean.FALSE; +// this.lmsSebRestriction = (lmsSebRestriction != null) ? lmsSebRestriction : Boolean.FALSE; this.browserExamKeys = browserExamKeys; this.active = (active != null) ? active : Boolean.TRUE; this.lastUpdate = lastUpdate; @@ -191,7 +191,7 @@ public final class Exam implements GrantEntity { EXAM.ATTR_STATUS, ExamStatus.class, getStatusFromDate(this.startTime, this.endTime)); - this.lmsSebRestriction = mapper.getBoolean(EXAM.ATTR_LMS_SEB_RESTRICTION); +// this.lmsSebRestriction = mapper.getBoolean(EXAM.ATTR_LMS_SEB_RESTRICTION); this.browserExamKeys = mapper.getString(EXAM.ATTR_BROWSER_KEYS); this.active = mapper.getBoolean(EXAM.ATTR_ACTIVE); this.supporter = mapper.getStringSet(EXAM.ATTR_SUPPORTER); @@ -216,7 +216,7 @@ public final class Exam implements GrantEntity { this.quitPassword = null; this.owner = null; this.status = (status != null) ? status : getStatusFromDate(this.startTime, this.endTime); - this.lmsSebRestriction = null; +// this.lmsSebRestriction = null; this.browserExamKeys = null; this.active = null; this.supporter = null; @@ -314,9 +314,9 @@ public final class Exam implements GrantEntity { return this.status; } - public Boolean getLmsSebRestriction() { - return this.lmsSebRestriction; - } +// public Boolean getLmsSebRestriction() { +// return this.lmsSebRestriction; +// } public String getBrowserExamKeys() { return this.browserExamKeys; @@ -357,8 +357,8 @@ public final class Exam implements GrantEntity { builder.append(this.supporter); builder.append(", status="); builder.append(this.status); - builder.append(", lmsSebRestriction="); - builder.append(this.lmsSebRestriction); +// builder.append(", lmsSebRestriction="); +// builder.append(this.lmsSebRestriction); builder.append(", browserExamKeys="); builder.append(this.browserExamKeys); builder.append(", active="); diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java index 3d27ee9a..78c7d1ea 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java @@ -39,13 +39,13 @@ public final class LmsSetup implements GrantEntity, Activatable { public enum Features { COURSE_API, - SEA_RESTRICTION, + SEB_RESTRICTION, COURSE_STRUCTURE_API, } public enum LmsType { MOCKUP(Features.COURSE_API), - OPEN_EDX(Features.COURSE_API, Features.SEA_RESTRICTION), + OPEN_EDX(Features.COURSE_API, Features.SEB_RESTRICTION), MOODLE(Features.COURSE_API); public final EnumSet features; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java index 2bbef38f..a33aac64 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java @@ -636,4 +636,16 @@ public final class Utils { .toString(); } + public static String truncateText(final String text, final int toChars) { + if (text == null || toChars < 3) { + return text; + } + + if (text.length() <= toChars) { + return text; + } + + return StringUtils.truncate(text, toChars - 3) + "..."; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java index 5add8a71..58214a90 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java @@ -65,6 +65,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.download.DownloadService; import ch.ethz.seb.sebserver.gui.service.remote.download.SebExamConfigDownload; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckExamConsistency; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckSebRestriction; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteExamConfigMapping; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteIndicator; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; @@ -222,7 +223,7 @@ public class ExamForm implements TemplateComposer { .call() .getOr(Collections.emptyList()); if (warnings != null && !warnings.isEmpty()) { - showConsistencyChecks(warnings, formContext.getParent()); + showConsistencyChecks(warnings, formContext.getParent()); } } @@ -234,7 +235,7 @@ public class ExamForm implements TemplateComposer { formContext.getParent(), titleKey); if (warnings != null && !warnings.isEmpty()) { - GridData gridData = (GridData) content.getLayoutData(); + final GridData gridData = (GridData) content.getLayoutData(); gridData.verticalIndent = 10; } @@ -248,6 +249,12 @@ public class ExamForm implements TemplateComposer { || examStatus == ExamStatus.RUNNING && currentUser.get().hasRole(UserRole.EXAM_ADMIN); final boolean sebRestrictionAvailable = testSebRestrictionAPI(exam); + final boolean isRestricted = readonly && sebRestrictionAvailable && this.restService + .getBuilder(CheckSebRestriction.class) + .withURIVariable(API.PARAM_MODEL_ID, exam.getModelId()) + .call() + .onError(e -> log.error("Unexpected error while trying to verify seb restriction settings: ", e)) + .getOr(false); // The Exam form final FormHandle formHandle = this.pageService.formBuilder( @@ -399,12 +406,14 @@ public class ExamForm implements TemplateComposer { .newAction(ActionDefinition.EXAM_ENABLE_SEB_RESTRICTION) .withEntityKey(entityKey) .withExec(action -> ExamSebRestrictionSettings.setSebRestriction(action, true, this.restService)) - .publishIf(() -> sebRestrictionAvailable && readonly && BooleanUtils.isFalse(exam.lmsSebRestriction)) + .publishIf(() -> sebRestrictionAvailable && readonly && modifyGrant && !importFromQuizData + && BooleanUtils.isFalse(isRestricted)) .newAction(ActionDefinition.EXAM_DISABLE_SEB_RESTRICTION) .withEntityKey(entityKey) .withExec(action -> ExamSebRestrictionSettings.setSebRestriction(action, false, this.restService)) - .publishIf(() -> sebRestrictionAvailable && readonly && BooleanUtils.isTrue(exam.lmsSebRestriction)); + .publishIf(() -> sebRestrictionAvailable && readonly && modifyGrant && !importFromQuizData + && BooleanUtils.isTrue(isRestricted)); // additional data in read-only view if (readonly && !importFromQuizData) { @@ -413,8 +422,7 @@ public class ExamForm implements TemplateComposer { this.widgetFactory.addFormSubContextHeader( content, CONFIG_LIST_TITLE_KEY, - CONFIG_LIST_TITLE_TOOLTIP_KEY - ); + CONFIG_LIST_TITLE_TOOLTIP_KEY); final EntityTable configurationTable = this.pageService.entityTableBuilder(this.restService.getRestCall(GetExamConfigMappingsPage.class)) @@ -506,8 +514,7 @@ public class ExamForm implements TemplateComposer { this.widgetFactory.addFormSubContextHeader( content, INDICATOR_LIST_TITLE_KEY, - INDICATOR_LIST_TITLE_TOOLTIP_KEY - ); + INDICATOR_LIST_TITLE_TOOLTIP_KEY); final EntityTable indicatorTable = this.pageService.entityTableBuilder(this.restService.getRestCall(GetIndicatorPage.class)) @@ -597,7 +604,7 @@ public class ExamForm implements TemplateComposer { .withURIVariable(API.PARAM_MODEL_ID, String.valueOf(exam.lmsSetupId)) .call() .onError(t -> log.error("Failed to check SEB restriction API: ", t)) - .map(lmsSetup -> lmsSetup.lmsType.features.contains(Features.SEA_RESTRICTION)) + .map(lmsSetup -> lmsSetup.lmsType.features.contains(Features.SEB_RESTRICTION)) .getOr(false); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/RegisterPage.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/RegisterPage.java index 07f2648e..c034f6c7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/RegisterPage.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/RegisterPage.java @@ -114,11 +114,18 @@ public class RegisterPage implements TemplateComposer { @Override public void compose(final PageContext pageContext) { + final Composite outer = new Composite(pageContext.getParent(), SWT.NONE); + final GridLayout outerLayout = new GridLayout(); + outerLayout.marginLeft = 50; + outerLayout.marginRight = 50; + outer.setLayout(outerLayout); + outer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + final Composite parent = PageService.createManagedVScrolledComposite( - pageContext.getParent(), + outer, scrolledComposite -> { final Composite result = new Composite(scrolledComposite, SWT.NONE); - result.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + result.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, true, true)); final GridLayout contentOutlaying = new GridLayout(); contentOutlaying.marginHeight = 0; contentOutlaying.marginWidth = 0; @@ -147,7 +154,6 @@ public class RegisterPage implements TemplateComposer { .sorted(ResourceService.RESOURCE_COMPARATOR) .collect(Collectors.toList()); - this.widgetFactory.labelLocalizedTitle(parent, TITLE_TEXT_KEY); // The UserAccount form diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageAction.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageAction.java index ecd02e16..b5f4adac 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageAction.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageAction.java @@ -198,6 +198,9 @@ public final class PageAction { PageAction.this.getName(), e.getMessage(), Utils.getErrorCauseMessage(e)); + PageAction.this.pageContext.notifyError( + PageContext.UNEXPECTED_ERROR_KEY, + e); return Result.ofError(e); } catch (final Exception e) { log.error("Failed to execute action: {} | error: {} | cause: {}", diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/CheckSebRestriction.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/CheckSebRestriction.java new file mode 100644 index 00000000..6844a180 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/CheckSebRestriction.java @@ -0,0 +1,41 @@ +/* + * 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.service.remote.webservice.api.exam; + +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.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class CheckSebRestriction extends RestCall { + + public CheckSebRestriction() { + super(new TypeKey<>( + CallType.UNDEFINED, + EntityType.EXAM_SEB_RESTRICTION, + new TypeReference() { + }), + HttpMethod.GET, + MediaType.APPLICATION_JSON_UTF8, + API.EXAM_ADMINISTRATION_ENDPOINT + + API.MODEL_ID_VAR_PATH_SEGMENT + + API.EXAM_ADMINISTRATION_CHECK_RESTRICTION_PATH_SEGMENT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/SingleSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/SingleSelection.java index 6c8ef8dd..97c43eb5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/SingleSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/SingleSelection.java @@ -19,6 +19,7 @@ import org.eclipse.swt.widgets.Listener; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.util.Tuple; +import ch.ethz.seb.sebserver.gbl.util.Utils; public final class SingleSelection extends Combo implements Selection { @@ -41,7 +42,7 @@ public final class SingleSelection extends Combo implements Selection { this.valueMapping.clear(); this.keyMapping.clear(); this.valueMapping.addAll(mapping.stream() - .map(t -> t._2) + .map(t -> Utils.truncateText(t._2, 100)) .collect(Collectors.toList())); this.keyMapping.addAll(mapping.stream() .map(t -> t._1) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java index ba88ca21..c6493e93 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java @@ -8,6 +8,44 @@ package ch.ethz.seb.sebserver.gui.widget; +import static ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService.POLYGLOT_WIDGET_FUNCTION_KEY; + +import java.io.InputStream; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; +import org.eclipse.rap.rwt.widgets.WidgetUtil; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.ColorDialog; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.DateTime; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.TabFolder; +import org.eclipse.swt.widgets.TabItem; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.swt.widgets.Widget; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; @@ -19,29 +57,6 @@ import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService; import ch.ethz.seb.sebserver.gui.service.page.PageService; import ch.ethz.seb.sebserver.gui.service.page.impl.DefaultPageLayout; import ch.ethz.seb.sebserver.gui.service.push.ServerPushService; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; -import org.eclipse.rap.rwt.widgets.WidgetUtil; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Device; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; - -import java.io.InputStream; -import java.util.Collection; -import java.util.List; -import java.util.Locale; -import java.util.function.Consumer; -import java.util.function.Supplier; - -import static ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService.POLYGLOT_WIDGET_FUNCTION_KEY; @Lazy @Service @@ -151,7 +166,7 @@ public class WidgetFactory { TEXT_H1("h1"), TEXT_H2("h2"), TEXT_H3("h3"), - SUBTITLE( "subtitle"), + SUBTITLE("subtitle"), IMAGE_BUTTON("imageButton"), TEXT_ACTION("action"), TEXT_READONLY("readonlyText"), @@ -179,7 +194,9 @@ public class WidgetFactory { LIST_NAVIGATION("list-nav"), PLAIN_PWD("pwdplain"), - COLOR_BOX("colorbox") + COLOR_BOX("colorbox"), + + REGISTER_FORM("register") ; @@ -228,8 +245,8 @@ public class WidgetFactory { labelLocalizedTitle.setLayoutData(gridData); // sub title if defined in language properties - LocTextKey subTitleTextKey = new LocTextKey(title.name + SUB_TITLE_TExT_SUFFIX); - if (i18nSupport.hasText(subTitleTextKey)) { + final LocTextKey subTitleTextKey = new LocTextKey(title.name + SUB_TITLE_TExT_SUFFIX); + if (this.i18nSupport.hasText(subTitleTextKey)) { final GridData gridDataSub = new GridData(SWT.FILL, SWT.FILL, true, false); final Label labelLocalizedSubTitle = labelLocalized( defaultPageLayout, @@ -241,20 +258,21 @@ public class WidgetFactory { return defaultPageLayout; } - public void addFormSubContextHeader(final Composite parent, final LocTextKey titleTextKey, final LocTextKey tooltipTextKey) { - GridData gridData = new GridData(SWT.FILL, SWT.BOTTOM, true, false); + public void addFormSubContextHeader(final Composite parent, final LocTextKey titleTextKey, + final LocTextKey tooltipTextKey) { + final GridData gridData = new GridData(SWT.FILL, SWT.BOTTOM, true, false); gridData.horizontalIndent = 8; gridData.verticalIndent = 10; - Label subContextLabel = labelLocalized( + final Label subContextLabel = labelLocalized( parent, CustomVariant.TEXT_H3, titleTextKey, tooltipTextKey); subContextLabel.setLayoutData(gridData); - GridData gridData1 = new GridData(SWT.FILL, SWT.BOTTOM, true, false); + final GridData gridData1 = new GridData(SWT.FILL, SWT.BOTTOM, true, false); gridData1.heightHint = 5; gridData1.horizontalIndent = 8; - Label subContextSeparator = labelSeparator(parent); + final Label subContextSeparator = labelSeparator(parent); subContextSeparator.setLayoutData(gridData1); } @@ -289,7 +307,7 @@ public class WidgetFactory { public Composite createWarningPanel(final Composite parent) { final Composite composite = new Composite(parent, SWT.NONE); - GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); + final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); composite.setLayoutData(gridData); final GridLayout gridLayout = new GridLayout(1, true); gridLayout.marginWidth = 20; @@ -379,7 +397,7 @@ public class WidgetFactory { public Label labelLocalizedTitle(final Composite content, final LocTextKey locTextKey) { final Label labelLocalized = labelLocalized(content, CustomVariant.TEXT_H1, locTextKey); - GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); + final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); labelLocalized.setLayoutData(gridData); return labelLocalized; } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java index b67b9206..d55e8671 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java @@ -107,7 +107,8 @@ public class ExamDAOImpl implements ExamDAO { .execute() : this.examRecordMapper.selectByExample() .build() - .execute()).flatMap(this::toDomainModel); + .execute()) + .flatMap(this::toDomainModel); } @Override @@ -210,9 +211,7 @@ public class ExamDAOImpl implements ExamDAO { (exam.status != null) ? exam.status.name() : null, - (exam.lmsSebRestriction != null) - ? BooleanUtils.toIntegerObject(exam.lmsSebRestriction) - : null, + 1, // seb restriction (deprecated) null, // updating null, // lastUpdate null // active @@ -259,6 +258,10 @@ public class ExamDAOImpl implements ExamDAO { // used to save instead of create a new one if (records != null && records.size() > 0) { final ExamRecord examRecord = records.get(0); + // if another institution tries to import an exam that already exists + if (!exam.institutionId.equals(examRecord.getInstitutionId())) { + throw new IllegalStateException("Exam cannot be imported twice from different institutions"); + } final ExamRecord newRecord = new ExamRecord( examRecord.getId(), null, null, null, null, null, @@ -266,7 +269,7 @@ public class ExamDAOImpl implements ExamDAO { null, // quitPassword null, // browser keys null, // status - null, // lmsSebRestriction + null, // lmsSebRestriction (deprecated) null, // updating null, // lastUpdate BooleanUtils.toIntegerObject(exam.active)); @@ -288,7 +291,7 @@ public class ExamDAOImpl implements ExamDAO { null, // quitPassword null, // browser keys (exam.status != null) ? exam.status.name() : ExamStatus.UP_COMING.name(), - BooleanUtils.toInteger(exam.lmsSebRestriction), + 1, // seb restriction (deprecated) BooleanUtils.toInteger(false), null, // lastUpdate BooleanUtils.toInteger(true)); @@ -752,7 +755,7 @@ public class ExamDAOImpl implements ExamDAO { record.getOwner(), supporter, status, - BooleanUtils.toBooleanObject((quizData != null) ? record.getLmsSebRestriction() : null), +// BooleanUtils.toBooleanObject((quizData != null) ? record.getLmsSebRestriction() : null), record.getBrowserKeys(), BooleanUtils.toBooleanObject((quizData != null) ? record.getActive() : null), record.getLastupdate()); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java index 0caf66cc..f325980d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java @@ -17,13 +17,15 @@ public interface ExamAdminService { * * @param exam The Exam to add the default indicator * @return the Exam with added default indicator */ - Result addDefaultIndicator(final Exam exam); + Result addDefaultIndicator(Exam exam); /** Applies all additional SEB restriction attributes that are defined by the * type of the LMS of a given Exam to this given Exam. - * + * * @param exam the Exam to apply all additional SEB restriction attributes * @return the Exam */ - Result applyAdditionalSEBRestrictions(final Exam exam); + Result applyAdditionalSEBRestrictions(Exam exam); + + Result isRestricted(Exam exam); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminServiceImpl.java index 617b5947..353808f9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminServiceImpl.java @@ -127,4 +127,15 @@ public class ExamAdminServiceImpl implements ExamAdminService { }); } + @Override + public Result isRestricted(final Exam exam) { + if (exam == null) { + return Result.of(false); + } + + return this.lmsAPIService + .getLmsAPITemplate(exam.lmsSetupId) + .map(lmsAPI -> !lmsAPI.getSebClientRestriction(exam).hasError()); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/SebRestrictionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/SebRestrictionServiceImpl.java index 06ef6b35..d5a5be0a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/SebRestrictionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/SebRestrictionServiceImpl.java @@ -121,7 +121,6 @@ public class SebRestrictionServiceImpl implements SebRestrictionService { null, null, null, null, null, null, null, null, null, null, null, exam.supporter, exam.status, - exam.lmsSebRestriction, (browserExamKeys != null && !browserExamKeys.isEmpty()) ? StringUtils.join(browserExamKeys, Constants.LIST_SEPARATOR_CHAR) : StringUtils.EMPTY, @@ -157,7 +156,7 @@ public class SebRestrictionServiceImpl implements SebRestrictionService { public Result applySebClientRestriction(final Exam exam) { if (!this.lmsAPIService .getLmsSetup(exam.lmsSetupId) - .getOrThrow().lmsType.features.contains(Features.SEA_RESTRICTION)) { + .getOrThrow().lmsType.features.contains(Features.SEB_RESTRICTION)) { return Result.of(exam); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamConfigUpdateServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamConfigUpdateServiceImpl.java index 597e5331..84978dd0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamConfigUpdateServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamConfigUpdateServiceImpl.java @@ -14,7 +14,6 @@ import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; -import org.apache.commons.lang3.BooleanUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; @@ -31,6 +30,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamConfigUpdateService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; @@ -46,19 +46,22 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService { private final ExamConfigurationMapDAO examConfigurationMapDAO; private final ExamSessionService examSessionService; private final ExamUpdateHandler examUpdateHandler; + private final ExamAdminService examAdminService; protected ExamConfigUpdateServiceImpl( final ExamDAO examDAO, final ConfigurationDAO configurationDAO, final ExamConfigurationMapDAO examConfigurationMapDAO, final ExamSessionService examSessionService, - final ExamUpdateHandler examUpdateHandler) { + final ExamUpdateHandler examUpdateHandler, + final ExamAdminService examAdminService) { this.examDAO = examDAO; this.configurationDAO = configurationDAO; this.examConfigurationMapDAO = examConfigurationMapDAO; this.examSessionService = examSessionService; this.examUpdateHandler = examUpdateHandler; + this.examAdminService = examAdminService; } // processing: @@ -127,7 +130,8 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService { // generate the new Config Key and update the Config Key within the LMSSetup API for each exam (delete old Key and add new Key) for (final Exam exam : exams) { - if (exam.getStatus() == ExamStatus.RUNNING && BooleanUtils.isTrue(exam.lmsSebRestriction)) { + if (exam.getStatus() == ExamStatus.RUNNING || this.examAdminService.isRestricted(exam).getOr(false)) { + this.examUpdateHandler .getSebRestrictionService() .applySebClientRestriction(exam) @@ -197,7 +201,7 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService { .getOrThrow(); // update seb client restriction if the feature is activated for the exam - if (exam.lmsSebRestriction) { + if (this.examAdminService.isRestricted(exam).getOr(false)) { this.examUpdateHandler .getSebRestrictionService() .applySebClientRestriction(exam) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java index abd5a627..31f45403 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java @@ -125,7 +125,7 @@ public class ExamSessionServiceImpl implements ExamSessionService { // if SEB restriction is not available no consistency violation message is added final LmsSetup lmsSetup = this.lmsAPIService.getLmsSetup(exam.lmsSetupId) .getOr(null); - if (lmsSetup != null && lmsSetup.lmsType.features.contains(Features.SEA_RESTRICTION)) { + if (lmsSetup != null && lmsSetup.lmsType.features.contains(Features.SEB_RESTRICTION)) { this.lmsAPIService.getLmsAPITemplate(exam.lmsSetupId) .map(t -> { if (t.testCourseRestrictionAPI().isOk()) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java index ed7ad4f0..cc734136 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java @@ -22,7 +22,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; -import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.mybatis.dynamic.sql.SqlTable; import org.slf4j.Logger; @@ -227,6 +226,24 @@ public class ExamAdministrationController extends EntityController { .getOrThrow(); } + @RequestMapping( + path = API.MODEL_ID_VAR_PATH_SEGMENT + + API.EXAM_ADMINISTRATION_CHECK_RESTRICTION_PATH_SEGMENT, + method = RequestMethod.GET, + produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public Boolean checkSebRestriction( + @PathVariable final Long modelId, + @RequestParam( + name = API.PARAM_INSTITUTION_ID, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId) { + + checkReadPrivilege(institutionId); + return this.examDAO.byPK(modelId) + .flatMap(this.examAdminService::isRestricted) + .getOrThrow(); + } + // **************************************************************************** // **** SEB Restriction @@ -266,7 +283,7 @@ public class ExamAdministrationController extends EntityController { return this.entityDAO.byPK(examId) .flatMap(this.authorization::checkModify) .flatMap(exam -> this.sebRestrictionService.saveSebRestrictionToExam(exam, sebRestriction)) - .flatMap(exam -> BooleanUtils.isTrue(exam.lmsSebRestriction) + .flatMap(exam -> this.examAdminService.isRestricted(exam).getOrThrow() ? this.applySebRestriction(exam, true) : Result.of(exam)) .getOrThrow(); @@ -397,7 +414,7 @@ public class ExamAdministrationController extends EntityController { final LmsSetup lmsSetup = this.lmsAPIService.getLmsSetup(exam.lmsSetupId) .getOrThrow(); - if (!lmsSetup.lmsType.features.contains(Features.SEA_RESTRICTION)) { + if (!lmsSetup.lmsType.features.contains(Features.SEB_RESTRICTION)) { return Result.ofError(new UnsupportedOperationException( "SEB Restriction feature not available for LMS type: " + lmsSetup.lmsType)); } @@ -405,7 +422,7 @@ public class ExamAdministrationController extends EntityController { if (restrict) { if (!this.lmsAPIService .getLmsSetup(exam.lmsSetupId) - .getOrThrow().lmsType.features.contains(Features.SEA_RESTRICTION)) { + .getOrThrow().lmsType.features.contains(Features.SEB_RESTRICTION)) { return Result.ofError(new APIMessageException( APIMessage.ErrorMessage.ILLEGAL_API_ARGUMENT diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java index fec9caa7..e552fe3a 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java @@ -809,7 +809,6 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { null, null, Utils.immutableCollectionOf(userId), ExamStatus.RUNNING, - true, null, true, null); diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java index 514d9d83..bbad5db8 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java @@ -65,7 +65,6 @@ public class ExamAPITest extends AdministrationAPIIntegrationTester { exam.owner, Arrays.asList("user5"), null, - true, null, true, null)) @@ -97,7 +96,6 @@ public class ExamAPITest extends AdministrationAPIIntegrationTester { exam.owner, Arrays.asList("user2"), null, - true, null, true, null))