SEBSERV-105 GUI implementation and bug-fix

This commit is contained in:
anhefti 2020-01-13 15:08:08 +01:00
parent 151b6b734a
commit 45fb04955b
16 changed files with 236 additions and 91 deletions

View file

@ -35,6 +35,7 @@ public final class Constants {
public static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
public static final long DAY_IN_MILLIS = 24 * HOUR_IN_MILLIS;
public static final Character CARRIAGE_RETURN = '\n';
public static final Character CURLY_BRACE_OPEN = '{';
public static final Character CURLY_BRACE_CLOSE = '}';
public static final Character COLON = ':';

View file

@ -93,7 +93,7 @@ public class OpenEdxSebRestriction {
public final Collection<String> permissionComponents;
@JsonProperty(ATTR_USER_BANNING_ENABLED)
public final boolean banningEnabled;
public final Boolean banningEnabled;
@JsonCreator
OpenEdxSebRestriction(

View file

@ -175,6 +175,56 @@ public final class Utils {
.collect(Collectors.toList()));
}
public static Collection<String> getListOfLines(final String list) {
if (list == null) {
return Collections.emptyList();
}
return Arrays.asList(StringUtils.split(
streamlineCarriageReturn(list),
Constants.CARRIAGE_RETURN));
}
public static String convertCarriageReturnToListSeparator(final String value) {
if (value == null) {
return null;
}
return streamlineCarriageReturn(value.trim())
.replace(Constants.CARRIAGE_RETURN, Constants.LIST_SEPARATOR_CHAR);
}
public static String convertListSeparatorToCarriageReturn(final String value) {
if (value == null) {
return null;
}
return value
.trim()
.replace(Constants.LIST_SEPARATOR_CHAR, Constants.CARRIAGE_RETURN);
}
public static String streamlineCarriageReturn(final String value) {
if (value == null) {
return null;
}
return value.replace('\r', '\n')
.replace("\r\n", "\n");
}
public static Collection<String> getListFromString(final String list) {
return getListFromString(list, Constants.LIST_SEPARATOR);
}
public static Collection<String> getListFromString(final String list, final String separator) {
if (list == null) {
return Collections.emptyList();
}
return Arrays.asList(StringUtils.split(list, separator));
}
public static Result<Long> dateTimeStringToTimestamp(final String startTime) {
return Result.tryCatch(() -> {
return DateTime.parse(startTime, Constants.STANDARD_DATE_TIME_FORMATTER).getMillis();

View file

@ -394,6 +394,8 @@ public class ExamForm implements TemplateComposer {
// additional data in read-only view
if (readonly && !importFromQuizData) {
this.widgetFactory.labelSeparator(content);
// List of SEB Configuration
this.widgetFactory.labelLocalized(
content,
@ -479,6 +481,8 @@ public class ExamForm implements TemplateComposer {
.noEventPropagation()
.publishIf(() -> userGrantCheck.r() && configurationTable.hasAnyContent());
this.widgetFactory.labelSeparator(content);
// List of Indicators
this.widgetFactory.labelLocalized(
content,

View file

@ -8,6 +8,9 @@
package ch.ethz.seb.sebserver.gui.content;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@ -16,14 +19,14 @@ import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.widgets.Composite;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSebRestriction;
import ch.ethz.seb.sebserver.gbl.model.exam.SebRestriction;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.form.Form;
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
import ch.ethz.seb.sebserver.gui.form.FormHandle;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
@ -54,6 +57,10 @@ public class ExamSebRestrictionSettings {
new LocTextKey("sebserver.exam.form.sebrestriction.WHITELIST_PATHS");
private final static LocTextKey SEB_RESTRICTION_FORM_EDX_PERMISSIONS =
new LocTextKey("sebserver.exam.form.sebrestriction.PERMISSION_COMPONENTS");
private final static LocTextKey SEB_RESTRICTION_FORM_EDX_BLACKLIST_CHAPTERS =
new LocTextKey("sebserver.exam.form.sebrestriction.BLACKLIST_CHAPTERS");
private final static LocTextKey SEB_RESTRICTION_FORM_EDX_USER_BANNING_ENABLED =
new LocTextKey("sebserver.exam.form.sebrestriction.USER_BANNING_ENABLED");
static final String PAGE_CONTEXT_ATTR_LMS_TYPE = "ATTR_LMS_TYPE";
@ -66,7 +73,8 @@ public class ExamSebRestrictionSettings {
new ModalInputDialog<FormHandle<?>>(
action.pageContext().getParent().getShell(),
pageService.getWidgetFactory())
.setVeryLargeDialogWidth();
.setDialogWidth(740)
.setDialogHeight(400);
final SebRestrictionPropertiesForm bindFormContext = new SebRestrictionPropertiesForm(
pageService,
@ -96,19 +104,35 @@ public class ExamSebRestrictionSettings {
final LmsType lmsType = getLmsType(pageContext);
SebRestriction bodyValue = null;
try {
final JSONMapper jsonMapper = pageService.getJSONMapper();
if (lmsType == LmsType.OPEN_EDX) {
final OpenEdxSebRestriction edxProperties = jsonMapper.readValue(
formHandle.getFormBinding().getFormAsJson(),
OpenEdxSebRestriction.class);
bodyValue = SebRestriction.from(Long.parseLong(entityKey.modelId), edxProperties);
} else {
bodyValue = jsonMapper.readValue(
formHandle.getFormBinding().getFormAsJson(),
SebRestriction.class);
}
} catch (final Exception e) {
final Form form = formHandle.getForm();
final Collection<String> browserKeys = Utils.getListOfLines(
form.getFieldValue(SebRestriction.ATTR_BROWSER_KEYS));
final Map<String, String> additionalAttributes = new HashMap<>();
if (lmsType == LmsType.OPEN_EDX) {
additionalAttributes.put(
OpenEdxSebRestriction.ATTR_PERMISSION_COMPONENTS,
form.getFieldValue(OpenEdxSebRestriction.ATTR_PERMISSION_COMPONENTS));
additionalAttributes.put(
OpenEdxSebRestriction.ATTR_WHITELIST_PATHS,
form.getFieldValue(OpenEdxSebRestriction.ATTR_WHITELIST_PATHS));
additionalAttributes.put(
OpenEdxSebRestriction.ATTR_USER_BANNING_ENABLED,
form.getFieldValue(OpenEdxSebRestriction.ATTR_USER_BANNING_ENABLED));
additionalAttributes.put(
OpenEdxSebRestriction.ATTR_BLACKLIST_CHAPTERS,
Utils.convertCarriageReturnToListSeparator(
form.getFieldValue(OpenEdxSebRestriction.ATTR_BLACKLIST_CHAPTERS)));
}
bodyValue = new SebRestriction(
Long.parseLong(entityKey.modelId),
null,
browserKeys,
additionalAttributes);
} catch (final Exception e) {
e.printStackTrace();
}
return !pageService
@ -116,17 +140,8 @@ public class ExamSebRestrictionSettings {
.getBuilder(SaveSebRestriction.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.withBody(bodyValue)
//.withFormBinding(formHandle.getFormBinding())
.call()
.onError(formHandle::handleError)
.map(mapping -> {
pageService.executePageAction(
pageService.pageActionBuilder(pageContext.clearEntityKeys())
.newAction(ActionDefinition.EXAM_VIEW_FROM_LIST)
.withEntityKey(pageContext.getParentEntityKey())
.create());
return mapping;
})
.hasError();
}
@ -152,15 +167,22 @@ public class ExamSebRestrictionSettings {
final EntityKey entityKey = this.pageContext.getEntityKey();
final LmsType lmsType = getLmsType(this.pageContext);
final Composite content = this.pageService
.getWidgetFactory()
.createPopupScrollComposite(parent);
final SebRestriction sebRestriction = restService
.getBuilder(GetSebRestriction.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.call()
.getOrThrow();
final PageContext formContext = this.pageContext.clearEntityKeys();
final PageContext formContext = this.pageContext
.copyOf(content)
.clearEntityKeys();
final FormHandle<SebRestriction> formHandle = this.pageService.formBuilder(
formContext.copyOf(parent), 3)
formContext, 3)
.withDefaultSpanEmptyCell(0)
.withEmptyCellSeparation(false)
.readonly(false)
@ -168,14 +190,14 @@ public class ExamSebRestrictionSettings {
.addField(FormBuilder.text(
SebRestriction.ATTR_CONFIG_KEYS,
SEB_RESTRICTION_FORM_CONFIG_KEYS,
StringUtils.join(sebRestriction.getConfigKeys(), '\n'))
.asArea(25)
StringUtils.join(sebRestriction.getConfigKeys(), Constants.CARRIAGE_RETURN))
.asArea(50)
.readonly(true))
.addField(FormBuilder.text(
SebRestriction.ATTR_BROWSER_KEYS,
SEB_RESTRICTION_FORM_BROWSER_KEYS,
StringUtils.join(sebRestriction.getBrowserExamKeys(), '\n'))
StringUtils.join(sebRestriction.getBrowserExamKeys(), Constants.CARRIAGE_RETURN))
.asArea())
.addFieldIf(
@ -196,6 +218,26 @@ public class ExamSebRestrictionSettings {
.get(OpenEdxSebRestriction.ATTR_PERMISSION_COMPONENTS),
() -> resourceService.sebRestrictionPermissionResources()))
.addFieldIf(
() -> lmsType == LmsType.OPEN_EDX,
() -> FormBuilder.text(
OpenEdxSebRestriction.ATTR_BLACKLIST_CHAPTERS,
SEB_RESTRICTION_FORM_EDX_BLACKLIST_CHAPTERS,
Utils.convertListSeparatorToCarriageReturn(
sebRestriction
.getAdditionalProperties()
.get(OpenEdxSebRestriction.ATTR_BLACKLIST_CHAPTERS)))
.asArea())
.addFieldIf(
() -> lmsType == LmsType.OPEN_EDX,
() -> FormBuilder.checkbox(
OpenEdxSebRestriction.ATTR_USER_BANNING_ENABLED,
SEB_RESTRICTION_FORM_EDX_USER_BANNING_ENABLED,
sebRestriction
.getAdditionalProperties()
.get(OpenEdxSebRestriction.ATTR_USER_BANNING_ENABLED)))
.build();
return () -> formHandle;

View file

@ -149,6 +149,7 @@ public class SebExamConfigList implements TemplateComposer {
// configuration template table
widgetFactory.label(content, "");
widgetFactory.labelSeparator(content);
widgetFactory.labelLocalizedTitle(
content,
TITLE_TEMPLATE_TEXT_KEY);
@ -192,7 +193,8 @@ public class SebExamConfigList implements TemplateComposer {
.publishIf(examConfigGrant::iw)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST)
.withSelect(configTable::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
.withSelect(configTable::getSelection, PageAction::applySingleSelectionAsEntityKey,
EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> configTable.hasAnyContent())
.newAction(ActionDefinition.SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST)

View file

@ -289,6 +289,7 @@ public class SebExamConfigPropForm implements TemplateComposer {
if (isAttachedToExam) {
widgetFactory.labelSeparator(content);
widgetFactory.labelLocalized(
content,
CustomVariant.TEXT_H3,

View file

@ -250,12 +250,12 @@ public enum ActionDefinition {
ActionCategory.FORM),
EXAM_ENABLE_SEB_RESTRICTION(
new LocTextKey("sebserver.exam.action.sebrestriction.enable"),
ImageIcon.LOCK,
ImageIcon.UNLOCK,
PageStateDefinitionImpl.EXAM_VIEW,
ActionCategory.FORM),
EXAM_DISABLE_SEB_RESTRICTION(
new LocTextKey("sebserver.exam.action.sebrestriction.disable"),
ImageIcon.UNLOCK,
ImageIcon.LOCK,
PageStateDefinitionImpl.EXAM_VIEW,
ActionCategory.FORM),

View file

@ -611,7 +611,7 @@ public class ResourceService {
return Arrays.asList(WhiteListPath.values())
.stream()
.map(type -> new Tuple<>(
type.name(),
type.key,
this.i18nSupport.getText(SEB_RESTRICTION_WHITE_LIST_PREFIX + type.name(), type.key)))
.sorted(RESOURCE_COMPARATOR)
.collect(Collectors.toList());
@ -621,7 +621,7 @@ public class ResourceService {
return Arrays.asList(PermissionComponent.values())
.stream()
.map(type -> new Tuple<>(
type.name(),
type.key,
this.i18nSupport.getText(SEB_RESTRICTION_PERMISSIONS_PREFIX + type.name(), type.key)))
.sorted(RESOURCE_COMPARATOR)
.collect(Collectors.toList());

View file

@ -501,7 +501,7 @@ public class WidgetFactory {
public Label labelSeparator(final Composite parent) {
final Label label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
final GridData data = new GridData(SWT.FILL, SWT.TOP, true, true);
final GridData data = new GridData(SWT.FILL, SWT.TOP, true, false);
label.setLayoutData(data);
return label;
}

View file

@ -38,7 +38,7 @@ public interface AdditionalAttributesDAO {
* @param entityId the entity identifier (primary key)
* @param name the name of the attribute
* @param value the value of the attribute */
void saveAdditionalAttribute(
Result<AdditionalAttributeRecord> saveAdditionalAttribute(
EntityType type,
Long entityId,
String name,

View file

@ -59,53 +59,60 @@ public class AdditionalAttributesDAOImpl implements AdditionalAttributesDAO {
@Override
@Transactional
public void saveAdditionalAttribute(
public Result<AdditionalAttributeRecord> saveAdditionalAttribute(
final EntityType type,
final Long entityId,
final String name,
final String value) {
if (value == null) {
this.delete(entityId, name);
return;
}
return Result.tryCatch(() -> {
if (value == null) {
Result.ofError(new IllegalArgumentException(
"value cannot be null. Use delete to delete an additional attribute"));
}
final Optional<Long> id = this.additionalAttributeRecordMapperer
.selectIdsByExample()
.where(
AdditionalAttributeRecordDynamicSqlSupport.entityType,
SqlBuilder.isEqualTo(type.name()))
.and(
AdditionalAttributeRecordDynamicSqlSupport.entityId,
SqlBuilder.isEqualTo(entityId))
.and(
AdditionalAttributeRecordDynamicSqlSupport.name,
SqlBuilder.isEqualTo(name))
.build()
.execute()
.stream()
.findFirst();
final Optional<Long> id = this.additionalAttributeRecordMapperer
.selectIdsByExample()
.where(
AdditionalAttributeRecordDynamicSqlSupport.entityType,
SqlBuilder.isEqualTo(type.name()))
.and(
AdditionalAttributeRecordDynamicSqlSupport.entityId,
SqlBuilder.isEqualTo(entityId))
.and(
AdditionalAttributeRecordDynamicSqlSupport.name,
SqlBuilder.isEqualTo(name))
.build()
.execute()
.stream()
.findFirst();
if (id.isPresent()) {
final AdditionalAttributeRecord rec = new AdditionalAttributeRecord(
id.get(),
type.name(),
entityId,
name,
value);
this.additionalAttributeRecordMapperer
.updateByPrimaryKeySelective(rec);
} else {
final AdditionalAttributeRecord rec = new AdditionalAttributeRecord(
null,
type.name(),
entityId,
name,
value);
this.additionalAttributeRecordMapperer
.insert(rec);
}
if (id.isPresent()) {
final AdditionalAttributeRecord rec = new AdditionalAttributeRecord(
id.get(),
type.name(),
entityId,
name,
value);
this.additionalAttributeRecordMapperer
.updateByPrimaryKeySelective(rec);
return this.additionalAttributeRecordMapperer
.selectByPrimaryKey(rec.getId());
} else {
final AdditionalAttributeRecord rec = new AdditionalAttributeRecord(
null,
type.name(),
entityId,
name,
value);
this.additionalAttributeRecordMapperer
.insert(rec);
return this.additionalAttributeRecordMapperer
.selectByPrimaryKey(rec.getId());
}
});
}
@Override

View file

@ -206,11 +206,17 @@ public class ExamDAOImpl implements ExamDAO {
(exam.supporter != null)
? StringUtils.join(exam.supporter, Constants.LIST_SEPARATOR_CHAR)
: null,
(exam.type != null) ? exam.type.name() : null,
(exam.type != null)
? exam.type.name()
: null,
exam.quitPassword,
null, // browser keys
(exam.status != null) ? exam.status.name() : null,
BooleanUtils.toIntegerObject(exam.lmsSebRestriction),
exam.browserExamKeys,
(exam.status != null)
? exam.status.name()
: null,
(exam.lmsSebRestriction != null)
? BooleanUtils.toIntegerObject(exam.lmsSebRestriction)
: null,
null, // updating
null, // lastUpdate
null // active

View file

@ -29,10 +29,9 @@ public interface SebRestrictionService {
* and given additional restriction properties within the AdditionalAttrtibutes linked
* to the given Exam.
*
*
* @param exam
* @param sebRestriction
* @return */
* @param exam the Exam instance to save the SEB restrictions for
* @param sebRestriction SebRestriction data containing generic and LMS specific restriction attributes
* @return Result refer to the given Exam instance or to an error if happened */
Result<Exam> saveSebRestrictionToExam(Exam exam, SebRestriction sebRestriction);
/** Used to apply SEB Client restriction within the LMS API for a specified Exam.

View file

@ -118,7 +118,10 @@ public class SebRestrictionServiceImpl implements SebRestrictionService {
final Collection<String> browserExamKeys = sebRestriction.getBrowserExamKeys();
final Exam newExam = new Exam(
exam.id,
null, null, null, null, null, null, null, null, null, null, null, null, null, null,
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,

View file

@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@ -36,6 +37,7 @@ 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.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
@ -47,10 +49,12 @@ import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM;
import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.PageSortOrder;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSebRestriction;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.model.exam.SebRestriction;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.Features;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
@ -60,6 +64,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.Authorization
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.bulkaction.BulkActionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
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.dao.UserActivityLogDAO;
@ -79,6 +84,7 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
private final ExamDAO examDAO;
private final UserDAO userDAO;
private final AdditionalAttributesDAO additionalAttributesDAO;
private final LmsAPIService lmsAPIService;
private final ExamConfigService sebExamConfigService;
private final ExamSessionService examSessionService;
@ -95,7 +101,8 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
final UserDAO userDAO,
final ExamConfigService sebExamConfigService,
final ExamSessionService examSessionService,
final SebRestrictionService sebRestrictionService) {
final SebRestrictionService sebRestrictionService,
final AdditionalAttributesDAO additionalAttributesDAO) {
super(authorization,
bulkActionService,
@ -106,6 +113,7 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
this.examDAO = examDAO;
this.userDAO = userDAO;
this.additionalAttributesDAO = additionalAttributesDAO;
this.lmsAPIService = lmsAPIService;
this.sebExamConfigService = sebExamConfigService;
this.examSessionService = examSessionService;
@ -278,7 +286,6 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
@PathVariable(API.PARAM_MODEL_ID) final Long examlId) {
checkModifyPrivilege(institutionId);
return this.entityDAO.byPK(examlId)
.flatMap(this.authorization::checkModify)
.flatMap(exam -> this.applySebRestriction(exam, true))
@ -328,6 +335,33 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
.getOrThrow();
}
@Override
protected Result<Exam> notifyCreated(final Exam entity) {
return Result.tryCatch(() -> {
final LmsSetup lmsSetup = this.lmsAPIService.getLmsSetup(entity.lmsSetupId)
.getOrThrow();
// if we have an Open edX LMS involved, add additional initial SEB restriction attributes
if (lmsSetup.lmsType == LmsType.OPEN_EDX) {
final List<String> permissions = Arrays.asList(
OpenEdxSebRestriction.PermissionComponent.ALWAYS_ALLOW_STUFF.key,
OpenEdxSebRestriction.PermissionComponent.CHECK_CONFIG_KEY.key);
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.EXAM,
entity.id,
SebRestrictionService.SEB_RESTRICTION_ADDITIONAL_PROPERTY_NAME_PREFIX +
OpenEdxSebRestriction.ATTR_PERMISSION_COMPONENTS,
StringUtils.join(permissions, Constants.LIST_SEPARATOR_CHAR))
.getOrThrow();
}
return entity;
});
}
@Override
protected Result<Exam> validForCreate(final Exam entity) {
return super.validForCreate(entity)
@ -381,10 +415,6 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
"SEB Restriction feature not available for LMS type: " + lmsSetup.lmsType));
}
if (BooleanUtils.toBoolean(exam.lmsSebRestriction) == restrict) {
return Result.of(exam);
}
if (restrict) {
if (!this.lmsAPIService
.getLmsSetup(exam.lmsSetupId)