seb restriction switch, minor fixes for export
This commit is contained in:
parent
2d016ed7fc
commit
78ffa7bc2c
18 changed files with 263 additions and 109 deletions
|
@ -108,6 +108,7 @@ public final class API {
|
|||
public static final String EXAM_ADMINISTRATION_ENDPOINT = "/exam";
|
||||
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_INDICATOR_ENDPOINT = "/indicator";
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam;
|
|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamConfigMappingsPage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicatorPage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SetExamSebRestriction;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.GetQuizData;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.ImportAsExam;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
||||
|
@ -308,7 +309,17 @@ public class ExamForm implements TemplateComposer {
|
|||
.withEntityKey(entityKey)
|
||||
.withAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA, String.valueOf(importFromQuizData))
|
||||
.withExec(this.cancelModifyFunction())
|
||||
.publishIf(() -> !readonly);
|
||||
.publishIf(() -> !readonly)
|
||||
|
||||
.newAction(ActionDefinition.EXAM_ENABLE_SEB_RESTRICTION)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(action -> setSebRestriction(action, true))
|
||||
.publishIf(() -> readonly && BooleanUtils.isFalse(exam.lmsSebRestriction))
|
||||
|
||||
.newAction(ActionDefinition.EXAM_DISABLE_SEB_RESTRICTION)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(action -> setSebRestriction(action, false))
|
||||
.publishIf(() -> readonly && BooleanUtils.isTrue(exam.lmsSebRestriction));
|
||||
|
||||
// additional data in read-only view
|
||||
if (readonly && !importFromQuizData) {
|
||||
|
@ -491,6 +502,20 @@ public class ExamForm implements TemplateComposer {
|
|||
message));
|
||||
}
|
||||
|
||||
private PageAction setSebRestriction(final PageAction action, final boolean sebRestriction) {
|
||||
this.restService.getBuilder(SetExamSebRestriction.class)
|
||||
.withURIVariable(
|
||||
API.PARAM_MODEL_ID,
|
||||
action.pageContext().getAttribute(AttributeKeys.ENTITY_ID))
|
||||
.withQueryParam(
|
||||
Domain.EXAM.ATTR_LMS_SEB_RESTRICTION,
|
||||
sebRestriction ? Constants.TRUE_STRING : Constants.FALSE_STRING)
|
||||
.call()
|
||||
.onError(t -> action.pageContext().notifyError(t));
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
private PageAction viewExamConfigPageAction(final EntityTable<ExamConfigurationMap> table) {
|
||||
|
||||
return this.pageService.pageActionBuilder(table.getPageContext()
|
||||
|
|
|
@ -237,6 +237,7 @@ public class SebExamConfigPropForm implements TemplateComposer {
|
|||
urlLauncher.openURL(downloadURL);
|
||||
return action;
|
||||
})
|
||||
.noEventPropagation()
|
||||
.publishIf(() -> modifyGrant && isReadonly)
|
||||
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_GET_CONFIG_KEY)
|
||||
|
|
|
@ -226,6 +226,16 @@ public enum ActionDefinition {
|
|||
ImageIcon.TOGGLE_ON,
|
||||
PageStateDefinitionImpl.EXAM_VIEW,
|
||||
ActionCategory.FORM),
|
||||
EXAM_ENABLE_SEB_RESTRICTION(
|
||||
new LocTextKey("sebserver.exam.action.sebrestriction.enable"),
|
||||
ImageIcon.TOGGLE_OFF,
|
||||
PageStateDefinitionImpl.EXAM_VIEW,
|
||||
ActionCategory.FORM),
|
||||
EXAM_DISABLE_SEB_RESTRICTION(
|
||||
new LocTextKey("sebserver.exam.action.deasebrestriction.disable"),
|
||||
ImageIcon.TOGGLE_ON,
|
||||
PageStateDefinitionImpl.EXAM_VIEW,
|
||||
ActionCategory.FORM),
|
||||
|
||||
EXAM_CONFIGURATION_NEW(
|
||||
new LocTextKey("sebserver.exam.configuration.action.list.new"),
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.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.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 SetExamSebRestriction extends RestCall<Exam> {
|
||||
|
||||
public SetExamSebRestriction() {
|
||||
super(new TypeKey<>(
|
||||
CallType.SAVE,
|
||||
EntityType.EXAM,
|
||||
new TypeReference<Exam>() {
|
||||
}),
|
||||
HttpMethod.PATCH,
|
||||
MediaType.APPLICATION_JSON_UTF8,
|
||||
API.EXAM_ADMINISTRATION_ENDPOINT
|
||||
+ API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.EXAM_ADMINISTRATION_SEB_RESTRICTION_PATH_SEGMENT);
|
||||
}
|
||||
|
||||
}
|
|
@ -54,4 +54,6 @@ public interface ExamDAO extends ActivatableEntityDAO<Exam, Exam>, BulkActionSup
|
|||
|
||||
Result<Boolean> upToDate(Long examId, String lastUpdate);
|
||||
|
||||
Result<Exam> setSebRestriction(Long id, boolean sebRestriction);
|
||||
|
||||
}
|
||||
|
|
|
@ -190,6 +190,24 @@ public class ExamDAOImpl implements ExamDAO {
|
|||
.onError(TransactionHandler::rollback);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<Exam> setSebRestriction(final Long examId, final boolean sebRestriction) {
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
final ExamRecord examRecord = new ExamRecord(
|
||||
examId,
|
||||
null, null, null, null, null, null, null, null, null,
|
||||
BooleanUtils.toInteger(sebRestriction),
|
||||
null, null, null);
|
||||
|
||||
this.examRecordMapper.updateByPrimaryKeySelective(examRecord);
|
||||
return this.examRecordMapper.selectByPrimaryKey(examId);
|
||||
})
|
||||
.flatMap(this::toDomainModel)
|
||||
.onError(TransactionHandler::rollback);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<Exam> createNew(final Exam exam) {
|
||||
|
|
|
@ -8,43 +8,57 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SebRestrictionData;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class OpenEdxSebClientRestriction {
|
||||
|
||||
@JsonProperty("CONFIG_KEYS")
|
||||
private static final String ATTR_USER_BANNING_ENABLED = "USER_BANNING_ENABLED";
|
||||
private static final String ATTR_SEB_PERMISSION_COMPONENTS = "SEB_PERMISSION_COMPONENTS";
|
||||
private static final String ATTR_BLACKLIST_CHAPTERS = "BLACKLIST_CHAPTERS";
|
||||
private static final String ATTR_WHITELIST_PATHS = "WHITELIST_PATHS";
|
||||
private static final String ATTR_BROWSER_KEYS = "BROWSER_KEYS";
|
||||
private static final String ATTR_CONFIG_KEYS = "CONFIG_KEYS";
|
||||
|
||||
@JsonProperty(ATTR_CONFIG_KEYS)
|
||||
public final Collection<String> configKeys;
|
||||
|
||||
@JsonProperty("BROWSER_KEYS")
|
||||
@JsonProperty(ATTR_BROWSER_KEYS)
|
||||
public final Collection<String> browserExamKeys;
|
||||
|
||||
@JsonProperty("WHITELIST_PATHS")
|
||||
@JsonProperty(ATTR_WHITELIST_PATHS)
|
||||
public final Collection<String> whiteListPaths;
|
||||
|
||||
@JsonProperty("BLACKLIST_CHAPTERS")
|
||||
@JsonProperty(ATTR_BLACKLIST_CHAPTERS)
|
||||
public final Collection<String> blacklistChapters;
|
||||
|
||||
@JsonProperty("SEB_PERMISSION_COMPONENTS")
|
||||
@JsonProperty(ATTR_SEB_PERMISSION_COMPONENTS)
|
||||
public final Collection<String> permissionComponents;
|
||||
|
||||
@JsonProperty("USER_BANNING_ENABLED")
|
||||
@JsonProperty(ATTR_USER_BANNING_ENABLED)
|
||||
public final boolean banningEnabled;
|
||||
|
||||
@JsonCreator
|
||||
protected OpenEdxSebClientRestriction(
|
||||
@JsonProperty("CONFIG_KEYS") final Collection<String> configKeys,
|
||||
@JsonProperty("BROWSER_KEYS") final Collection<String> browserExamKeys,
|
||||
@JsonProperty("WHITELIST_PATHS") final Collection<String> whiteListPaths,
|
||||
@JsonProperty("BLACKLIST_CHAPTERS") final Collection<String> blacklistChapters,
|
||||
@JsonProperty("SEB_PERMISSION_COMPONENTS") final Collection<String> permissionComponents,
|
||||
@JsonProperty("USER_BANNING_ENABLED") final boolean banningEnabled) {
|
||||
public OpenEdxSebClientRestriction(
|
||||
@JsonProperty(ATTR_CONFIG_KEYS) final Collection<String> configKeys,
|
||||
@JsonProperty(ATTR_BROWSER_KEYS) final Collection<String> browserExamKeys,
|
||||
@JsonProperty(ATTR_WHITELIST_PATHS) final Collection<String> whiteListPaths,
|
||||
@JsonProperty(ATTR_BLACKLIST_CHAPTERS) final Collection<String> blacklistChapters,
|
||||
@JsonProperty(ATTR_SEB_PERMISSION_COMPONENTS) final Collection<String> permissionComponents,
|
||||
@JsonProperty(ATTR_USER_BANNING_ENABLED) final boolean banningEnabled) {
|
||||
|
||||
this.configKeys = Utils.immutableCollectionOf(configKeys);
|
||||
this.browserExamKeys = Utils.immutableCollectionOf(browserExamKeys);
|
||||
|
@ -54,6 +68,37 @@ public class OpenEdxSebClientRestriction {
|
|||
this.banningEnabled = banningEnabled;
|
||||
}
|
||||
|
||||
public OpenEdxSebClientRestriction(final SebRestrictionData data) {
|
||||
this.configKeys = Utils.immutableCollectionOf(data.configKeys);
|
||||
this.browserExamKeys = Utils.immutableCollectionOf(data.browserExamKeys);
|
||||
|
||||
final String whiteListPaths = data.additionalAttributes.get(ATTR_WHITELIST_PATHS);
|
||||
if (StringUtils.isNotBlank(whiteListPaths)) {
|
||||
this.whiteListPaths = Utils.immutableCollectionOf(Arrays.asList(
|
||||
StringUtils.split(whiteListPaths, Constants.LIST_SEPARATOR)));
|
||||
} else {
|
||||
this.whiteListPaths = Collections.emptyList();
|
||||
}
|
||||
|
||||
final String blacklistChapters = data.additionalAttributes.get(ATTR_BLACKLIST_CHAPTERS);
|
||||
if (StringUtils.isNotBlank(blacklistChapters)) {
|
||||
this.blacklistChapters = Utils.immutableCollectionOf(Arrays.asList(
|
||||
StringUtils.split(blacklistChapters, Constants.LIST_SEPARATOR)));
|
||||
} else {
|
||||
this.blacklistChapters = Collections.emptyList();
|
||||
}
|
||||
|
||||
final String permissionComponents = data.additionalAttributes.get(ATTR_SEB_PERMISSION_COMPONENTS);
|
||||
if (StringUtils.isNotBlank(permissionComponents)) {
|
||||
this.permissionComponents = Utils.immutableCollectionOf(Arrays.asList(
|
||||
StringUtils.split(permissionComponents, Constants.LIST_SEPARATOR)));
|
||||
} else {
|
||||
this.permissionComponents = Collections.emptyList();
|
||||
}
|
||||
|
||||
this.banningEnabled = BooleanUtils.toBoolean(data.additionalAttributes.get(ATTR_USER_BANNING_ENABLED));
|
||||
}
|
||||
|
||||
public Collection<String> getConfigKeys() {
|
||||
return this.configKeys;
|
||||
}
|
||||
|
|
|
@ -174,7 +174,7 @@ public class TableConverter implements AttributeValueConverter {
|
|||
|
||||
final ConfigurationAttribute attr = attrItr.next();
|
||||
ConfigurationValue value = rowValues.stream()
|
||||
.filter(val -> attr.id.equals(val.attributeId))
|
||||
.filter(val -> val != null && attr.id.equals(val.attributeId))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ public interface ExamSessionService {
|
|||
* @param examId the identifier of the Exam to check
|
||||
* @return Result of one APIMessage per consistency check if the check failed. An empty Collection of everything is
|
||||
* okay. */
|
||||
Result<Collection<APIMessage>> checkRunningExamConsystency(Long examId);
|
||||
Result<Collection<APIMessage>> checkRunningExamConsistency(Long examId);
|
||||
|
||||
/** Checks if a specified Exam has at least a default SEB Exam configuration attached.
|
||||
*
|
||||
|
|
|
@ -10,7 +10,6 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -130,38 +129,23 @@ 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)
|
||||
final Collection<Long> updatedExams = updateLmsSebRestriction(exams)
|
||||
.stream()
|
||||
.map(Result::get)
|
||||
.filter(Objects::nonNull)
|
||||
.map(Exam::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Successfully updated ConfigKey for Exams: {}", updatedExams);
|
||||
for (final Exam exam : exams) {
|
||||
if (exam.getStatus() == ExamStatus.RUNNING) {
|
||||
this.updateSebClientRestriction(exam)
|
||||
.onError(t -> log.error("Failed to update SEB Client restriction for Exam: {}", exam, t));
|
||||
}
|
||||
}
|
||||
|
||||
// evict each Exam from cache and release the update-lock on DB
|
||||
final Collection<Long> evictedExams = evictFromCache(exams)
|
||||
.stream()
|
||||
.filter(Result::hasValue)
|
||||
.map(Result::get)
|
||||
.map(Exam::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Successfully evicted Exams from cache: {}", evictedExams);
|
||||
for (final Exam exam : exams) {
|
||||
this.examSessionService.flushCache(exam)
|
||||
.onError(t -> log.error("Failed to flush Exam from cache: {}", exam, t));
|
||||
}
|
||||
|
||||
// release the update-locks on involved exams
|
||||
final Collection<Long> releasedLocks = releaseUpdateLocks(examIdsFirstCheck, updateId)
|
||||
.stream()
|
||||
.map(Result::getOrThrow)
|
||||
.map(Exam::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Successfully released update-locks on Exams: {}", releasedLocks);
|
||||
for (final Long examId : examIdsFirstCheck) {
|
||||
this.examDAO.releaseLock(examId, updateId)
|
||||
.onError(t -> log.error("Failed to release lock for Exam: {}", examId, t));
|
||||
}
|
||||
|
||||
return examIdsFirstCheck;
|
||||
|
@ -296,43 +280,6 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService {
|
|||
return this.examUpdateHandler.releaseSebClientRestriction(exam);
|
||||
}
|
||||
|
||||
private void checkIntegrityDoubleCheck(
|
||||
final Collection<Long> examIdsFirstCheck,
|
||||
final Collection<Long> examIdsSecondCheck) {
|
||||
|
||||
if (examIdsFirstCheck.size() != examIdsSecondCheck.size()) {
|
||||
throw new IllegalStateException("Running Exam integrity check missmatch. examIdsFirstCheck: "
|
||||
+ examIdsFirstCheck + " examIdsSecondCheck: " + examIdsSecondCheck);
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<Result<Exam>> lockForUpdate(final Collection<Long> examIds, final String update) {
|
||||
return examIds.stream()
|
||||
.map(id -> this.examDAO.placeLock(id, update))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Collection<Result<Exam>> releaseUpdateLocks(final Collection<Long> examIds, final String update) {
|
||||
return examIds.stream()
|
||||
.map(id -> this.examDAO.releaseLock(id, update))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Collection<Result<Exam>> updateLmsSebRestriction(final Collection<Exam> exams) {
|
||||
return exams
|
||||
.stream()
|
||||
.filter(exam -> exam.getStatus() == ExamStatus.RUNNING)
|
||||
.map(this::updateSebClientRestriction)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Collection<Result<Exam>> evictFromCache(final Collection<Exam> exams) {
|
||||
return exams
|
||||
.stream()
|
||||
.map(this.examSessionService::flushCache)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Collection<Long>> checkRunningExamIntegrity(final Long configurationNodeId) {
|
||||
final Collection<Long> involvedExams = this.examConfigurationMapDAO
|
||||
|
@ -397,4 +344,20 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService {
|
|||
return false;
|
||||
}
|
||||
|
||||
private void checkIntegrityDoubleCheck(
|
||||
final Collection<Long> examIdsFirstCheck,
|
||||
final Collection<Long> examIdsSecondCheck) {
|
||||
|
||||
if (examIdsFirstCheck.size() != examIdsSecondCheck.size()) {
|
||||
throw new IllegalStateException("Running Exam integrity check missmatch. examIdsFirstCheck: "
|
||||
+ examIdsFirstCheck + " examIdsSecondCheck: " + examIdsSecondCheck);
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<Result<Exam>> lockForUpdate(final Collection<Long> examIds, final String update) {
|
||||
return examIds.stream()
|
||||
.map(id -> this.examDAO.placeLock(id, update))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result<Collection<APIMessage>> checkRunningExamConsystency(final Long examId) {
|
||||
public Result<Collection<APIMessage>> checkRunningExamConsistency(final Long examId) {
|
||||
return Result.tryCatch(() -> {
|
||||
final Collection<APIMessage> result = new ArrayList<>();
|
||||
|
||||
|
|
|
@ -120,10 +120,6 @@ class ExamUpdateHandler {
|
|||
return Result.of(exam);
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Apply SEB Client restrictions for exam: {}", exam);
|
||||
}
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
final SebRestrictionData sebRestrictionData = createSebRestrictionData(exam);
|
||||
|
@ -153,10 +149,6 @@ class ExamUpdateHandler {
|
|||
|
||||
final SebRestrictionData sebRestrictionData = createSebRestrictionData(exam);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Update SEB Client restrictions for exam: {}", exam);
|
||||
}
|
||||
|
||||
return this.lmsAPIService
|
||||
.getLmsAPITemplate(exam.lmsSetupId)
|
||||
.flatMap(lmsTemplate -> lmsTemplate.updateSebClientRestriction(sebRestrictionData))
|
||||
|
@ -176,19 +168,6 @@ class ExamUpdateHandler {
|
|||
.flatMap(template -> template.releaseSebClientRestriction(exam));
|
||||
}
|
||||
|
||||
// @Transactional
|
||||
// public <T> Result<T> updateExamConfigurationMappingChange(
|
||||
// final ExamConfigurationMap mapping,
|
||||
// final Function<ExamConfigurationMap, Result<T>> changeAction,
|
||||
// final Exam exam) {
|
||||
//
|
||||
// return Result.tryCatch(() -> {
|
||||
//
|
||||
// return result;
|
||||
// })
|
||||
// .onError(TransactionHandler::rollback);
|
||||
// }
|
||||
|
||||
private SebRestrictionData createSebRestrictionData(final Exam exam) {
|
||||
final Collection<String> configKeys = this.sebExamConfigService
|
||||
.generateConfigKeys(exam.institutionId, exam.id)
|
||||
|
@ -206,7 +185,7 @@ class ExamUpdateHandler {
|
|||
exam,
|
||||
configKeys,
|
||||
browserExamKeys,
|
||||
// TODO when we have more restriction details available form the Exam, put it to the map
|
||||
// TODO when we have more restriction details available from the Exam, put it to the map
|
||||
Collections.emptyMap());
|
||||
return sebRestrictionData;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.util.stream.Collectors;
|
|||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.mybatis.dynamic.sql.SqlTable;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -60,6 +61,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
|||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamConfigUpdateService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
|
||||
|
||||
|
@ -75,6 +77,7 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
|||
private final LmsAPIService lmsAPIService;
|
||||
private final SebExamConfigService sebExamConfigService;
|
||||
private final ExamSessionService examSessionService;
|
||||
private final ExamConfigUpdateService examConfigUpdateService;
|
||||
|
||||
public ExamAdministrationController(
|
||||
final AuthorizationService authorization,
|
||||
|
@ -86,7 +89,8 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
|||
final LmsAPIService lmsAPIService,
|
||||
final UserDAO userDAO,
|
||||
final SebExamConfigService sebExamConfigService,
|
||||
final ExamSessionService examSessionService) {
|
||||
final ExamSessionService examSessionService,
|
||||
final ExamConfigUpdateService examConfigUpdateService) {
|
||||
|
||||
super(authorization,
|
||||
bulkActionService,
|
||||
|
@ -100,6 +104,7 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
|||
this.lmsAPIService = lmsAPIService;
|
||||
this.sebExamConfigService = sebExamConfigService;
|
||||
this.examSessionService = examSessionService;
|
||||
this.examConfigUpdateService = examConfigUpdateService;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -197,9 +202,39 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
|||
+ API.EXAM_ADMINISTRATION_CONSISTENCY_CHECK_PATH_SEGMENT,
|
||||
method = RequestMethod.GET,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public Collection<APIMessage> checkExamConsistency(@PathVariable final Long modelId) {
|
||||
public Collection<APIMessage> checkExamConsistency(
|
||||
@PathVariable final Long modelId,
|
||||
@RequestParam(
|
||||
name = API.PARAM_INSTITUTION_ID,
|
||||
required = true,
|
||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId) {
|
||||
|
||||
checkModifyPrivilege(institutionId);
|
||||
return this.examSessionService
|
||||
.checkRunningExamConsystency(modelId)
|
||||
.checkRunningExamConsistency(modelId)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.EXAM_ADMINISTRATION_SEB_RESTRICTION_PATH_SEGMENT,
|
||||
method = RequestMethod.PATCH,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public Exam switchSebRestriction(
|
||||
@PathVariable final Long modelId,
|
||||
@RequestParam(name = Domain.EXAM.ATTR_LMS_SEB_RESTRICTION, required = true) final boolean sebRestriction,
|
||||
@RequestParam(
|
||||
name = API.PARAM_INSTITUTION_ID,
|
||||
required = true,
|
||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId) {
|
||||
|
||||
checkModifyPrivilege(institutionId);
|
||||
|
||||
return this.entityDAO.byPK(modelId)
|
||||
.flatMap(this.authorization::checkModify)
|
||||
.flatMap(this::checkNoActiveSebClientConnections)
|
||||
.flatMap(exam -> this.setSebRestriction(exam, sebRestriction))
|
||||
.flatMap(this.userActivityLogDAO::logModify)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
|
@ -291,4 +326,35 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
|||
return exam;
|
||||
}
|
||||
|
||||
private Result<Exam> checkNoActiveSebClientConnections(final Exam exam) {
|
||||
if (this.examConfigUpdateService.hasActiveSebClientConnections(exam.id)) {
|
||||
return Result.ofError(new APIMessageException(
|
||||
APIMessage.ErrorMessage.INTEGRITY_VALIDATION
|
||||
.of("Exam currently has active SEB Client connections.")));
|
||||
}
|
||||
|
||||
return Result.of(exam);
|
||||
}
|
||||
|
||||
private Result<Exam> setSebRestriction(final Exam exam, final boolean sebRestriction) {
|
||||
return Result.tryCatch(() -> {
|
||||
if (BooleanUtils.toBoolean(exam.lmsSebRestriction) == sebRestriction) {
|
||||
return exam;
|
||||
}
|
||||
|
||||
final Exam examUpdate = this.examDAO.setSebRestriction(exam.id, sebRestriction)
|
||||
.getOrThrow();
|
||||
|
||||
if (sebRestriction) {
|
||||
this.examConfigUpdateService.applySebClientRestriction(examUpdate)
|
||||
.getOrThrow();
|
||||
} else {
|
||||
this.examConfigUpdateService.releaseSebClientRestriction(examUpdate)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
return examUpdate;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -262,7 +262,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
|
|||
(519, 'enableF11', 'CHECKBOX', null, null, null, null, 'false'),
|
||||
(520, 'enableF12', 'CHECKBOX', null, null, null, null, 'false'),
|
||||
|
||||
(800, 'browserMessagingSocket', 'TEXT_FIELD', null, null, null, null, 'ws:\\localhost:8706'),
|
||||
(800, 'browserMessagingSocket', 'TEXT_FIELD', null, null, null, null, 'ws://localhost:8706'),
|
||||
(801, 'browserMessagingPingTime', 'INTEGER', null, null, null, null, '120000'),
|
||||
(802, 'allowPreferencesWindow', 'CHECKBOX', null, null, null, null, 'true'),
|
||||
(803, 'useAsymmetricOnlyEncryption', 'CHECKBOX', null, null, null, null, 'false'),
|
||||
|
|
|
@ -235,7 +235,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
|
|||
(519, 'enableF11', 'CHECKBOX', null, null, null, null, 'false'),
|
||||
(520, 'enableF12', 'CHECKBOX', null, null, null, null, 'false'),
|
||||
|
||||
(800, 'browserMessagingSocket', 'TEXT_FIELD', null, null, null, null, 'ws:\\localhost:8706'),
|
||||
(800, 'browserMessagingSocket', 'TEXT_FIELD', null, null, null, null, 'ws://localhost:8706'),
|
||||
(801, 'browserMessagingPingTime', 'INTEGER', null, null, null, null, '120000'),
|
||||
(802, 'allowPreferencesWindow', 'CHECKBOX', null, null, null, null, 'true'),
|
||||
(803, 'useAsymmetricOnlyEncryption', 'CHECKBOX', null, null, null, null, 'false'),
|
||||
|
|
|
@ -301,6 +301,8 @@ sebserver.exam.action.import=Import From Quizzes
|
|||
sebserver.exam.action.save=Save Exam
|
||||
sebserver.exam.action.activate=Activate Exam
|
||||
sebserver.exam.action.deactivate=Deactivate Exam
|
||||
sebserver.exam.action.sebrestriction.enable=Enable SEB Restriction
|
||||
sebserver.exam.action.sebrestriction.disable=Disable SEB Restriction
|
||||
|
||||
sebserver.exam.info.pleaseSelect=Please Select an Exam first
|
||||
|
||||
|
|
|
@ -243,7 +243,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
|
|||
(519, 'enableF11', 'CHECKBOX', null, null, null, null, 'false'),
|
||||
(520, 'enableF12', 'CHECKBOX', null, null, null, null, 'false'),
|
||||
|
||||
(800, 'browserMessagingSocket', 'TEXT_FIELD', null, null, null, null, 'ws:\\localhost:8706'),
|
||||
(800, 'browserMessagingSocket', 'TEXT_FIELD', null, null, null, null, 'ws://localhost:8706'),
|
||||
(801, 'browserMessagingPingTime', 'INTEGER', null, null, null, null, '120000'),
|
||||
(802, 'allowPreferencesWindow', 'CHECKBOX', null, null, null, null, 'true'),
|
||||
(803, 'useAsymmetricOnlyEncryption', 'CHECKBOX', null, null, null, null, 'false'),
|
||||
|
|
Loading…
Reference in a new issue