diff --git a/pom.xml b/pom.xml
index a6d3caf1..c1e57cd2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,7 +16,7 @@
jar
- 1.6.0
+ 1.6.1
${sebserver-version}
${sebserver-version}
UTF-8
diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserFeatures.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserFeatures.java
index cd95dbbd..d98ec9ea 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserFeatures.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserFeatures.java
@@ -35,7 +35,7 @@ public class UserFeatures {
LMS_SETUP_MOODLE_PLUGIN("lms.setup.type.MOODLE_PLUGIN"),
LMS_SETUP_OPEN_EDX("lms.setup.type.OPEN_EDX"),
LMS_SETUP_ANS("lms.setup.type.ANS_DELFT"),
- LMS_SETUP_OPEN_OLAT("lms.setup.type.OPEN_OLAT"),
+ LMS_SETUP_OLAT("lms.setup.type.OLAT"),
QUIZ_LOOKUP("lms.quiz.lookup"),
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigForm.java
index 369bfb2b..5f85cfea 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigForm.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigForm.java
@@ -416,7 +416,7 @@ public class SEBClientConfigForm implements TemplateComposer {
SEBClientConfig.ATTR_EXAM_SELECTION,
FORM_EXAM_SELECTION_TEXT_KEY,
StringUtils.join(clientConfig.selectedExams, Constants.LIST_SEPARATOR),
- () -> pageService.getResourceService().getExamLogSelectionResources())
+ () -> pageService.getResourceService().getActiveExamResources())
.withInputSpan(5))
.withDefaultSpanEmptyCell(1);
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java
index c12c7892..3c31a084 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java
@@ -717,6 +717,24 @@ public class ResourceService {
.collect(Collectors.toList());
}
+ public List> getActiveExamResources() {
+ final UserInfo userInfo = this.currentUser.get();
+ return this.restService.getBuilder(GetExams.class)
+ .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.getInstitutionId()))
+ .withQueryParam(Exam.FILTER_CACHED_QUIZZES, Constants.TRUE_STRING)
+ .call()
+ .getOr(Collections.emptyList())
+ .stream()
+ .filter(exam -> exam != null &&
+ exam.getStatus() != ExamStatus.FINISHED &&
+ exam.getStatus() != ExamStatus.ARCHIVED)
+ .map(exam -> new Tuple<>(
+ exam.getModelId(),
+ StringUtils.isBlank(exam.name) ? exam.externalId : exam.name))
+ .sorted(RESOURCE_COMPARATOR)
+ .collect(Collectors.toList());
+ }
+
public List> getExamResources() {
final UserInfo userInfo = this.currentUser.get();
return this.restService.getBuilder(GetExamNames.class)
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/ClipboardSEBSettingsGUICheck.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/ClipboardSEBSettingsGUICheck.java
new file mode 100644
index 00000000..0461cae4
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/ClipboardSEBSettingsGUICheck.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019 ETH Zürich, IT Services
+ *
+ * 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.datalayer.checks;
+
+import static ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.OrientationRecordDynamicSqlSupport.*;
+import static ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.OrientationRecordDynamicSqlSupport.title;
+
+import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
+import ch.ethz.seb.sebserver.gbl.util.Result;
+import ch.ethz.seb.sebserver.webservice.DBIntegrityCheck;
+import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.OrientationRecordDynamicSqlSupport;
+import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.OrientationRecordMapper;
+import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.OrientationRecord;
+import ch.ethz.seb.sebserver.webservice.servicelayer.dao.OrientationDAO;
+import org.mybatis.dynamic.sql.SqlBuilder;
+import org.mybatis.dynamic.sql.update.UpdateDSL;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+@Lazy
+@Component
+@WebServiceProfile
+public class ClipboardSEBSettingsGUICheck implements DBIntegrityCheck {
+
+ public static final Logger INIT_LOGGER = LoggerFactory.getLogger("ch.ethz.seb.SEB_SERVER_INIT");
+
+ private final OrientationRecordMapper orientationRecordMapper;
+
+ public ClipboardSEBSettingsGUICheck(final OrientationRecordMapper orientationRecordMapper) {
+ this.orientationRecordMapper = orientationRecordMapper;
+ }
+
+ @Override
+ public String name() {
+ return "ClipboardSEBSettingsGUICheck";
+ }
+
+ @Override
+ public String description() {
+ return "Check if clipboardPolicy SEB Setting is missing in the GUI and if so add it to GUI";
+ }
+
+ @Override
+ public Result applyCheck(boolean tryFix) {
+ return Result.tryCatch(() -> {
+ // check if clipboardPolicy SEB Setting is missing
+ final Long count = orientationRecordMapper.countByExample()
+ .where(templateId, SqlBuilder.isEqualTo(0L))
+ .and(configAttributeId, SqlBuilder.isEqualTo(1201L))
+ .build()
+ .execute();
+
+ if (count != null && count.intValue() > 0) {
+ return "clipboardPolicy SEB Setting detected in GUI";
+ }
+
+ INIT_LOGGER.info("--------> Missing clipboardPolicy SEB Setting in GUI detected. Add it");
+
+ // move allowedSEBVersion setting
+ UpdateDSL.updateWithMapper(orientationRecordMapper::update, orientationRecord)
+ .set(yPosition).equalTo(21)
+ .where(templateId, SqlBuilder.isEqualTo(0L))
+ .and(configAttributeId, SqlBuilder.isEqualTo(1578L))
+ .build()
+ .execute();
+
+ // add clipboardPolicy setting
+ orientationRecordMapper.insert(new OrientationRecord(
+ null,
+ 1201L,
+ 0L,
+ 9L,
+ "clipboardPolicy",
+ 7,
+ 18,
+ 5,
+ 2,
+ "NONE"
+ ));
+
+ return "Missing clipboardPolicy SEB Setting in GUI successfully added";
+ });
+ }
+}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/DowngradeSEBSettingsCheck.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/DowngradeSEBSettingsCheck.java
index 7ed8a967..bb234a77 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/DowngradeSEBSettingsCheck.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/DowngradeSEBSettingsCheck.java
@@ -103,7 +103,7 @@ public class DowngradeSEBSettingsCheck implements DBIntegrityCheck {
.map(OrientationRecord::getConfigAttributeId)
.collect(Collectors.toList());
- if (attributeIds.isEmpty()) {
+ if (attributeIds.isEmpty() || attributeIds.get(0).intValue() == 1201) {
return "No additional SEB Settings orientations for downgrading found.";
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleUtils.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleUtils.java
index e2750f4b..83cf6d0f 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleUtils.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleUtils.java
@@ -56,13 +56,23 @@ public abstract class MoodleUtils {
private static String maskShortName(final String shortname) {
return shortname
.replace(Constants.SEMICOLON.toString(), "_SC_")
- .replace(Constants.COLON.toString(), "_COLON_");
+ .replace(Constants.COLON.toString(), "_COLON_")
+ .replace(Constants.SLASH.toString(), "_SL_")
+ .replace(Constants.BACKSLASH.toString(), "_BSL_")
+ .replace(Constants.AMPERSAND.toString(), "_AMP_")
+ .replace(Constants.ANGLE_BRACE_OPEN.toString(), "_AO_")
+ .replace(Constants.ANGLE_BRACE_CLOSE.toString(), "_AC_");
}
private static String unmaskShortName(final String shortname) {
return shortname
.replace("_SC_", Constants.SEMICOLON.toString())
- .replace("_COLON_", Constants.COLON.toString());
+ .replace("_COLON_", Constants.COLON.toString())
+ .replace("_SL_", Constants.SLASH.toString())
+ .replace("_BSL_", Constants.BACKSLASH.toString())
+ .replace("_AMP_", Constants.AMPERSAND.toString())
+ .replace("_AO_", Constants.ANGLE_BRACE_OPEN.toString())
+ .replace("_AC_", Constants.ANGLE_BRACE_CLOSE.toString());
}
public static String getQuizId(final String internalQuizId) {
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java
index 589f6763..922a0900 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java
@@ -275,7 +275,7 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
final DateTime quizFromTime = (filterMap != null) ? filterMap.getQuizFromTime() : null;
final long fromCutTime = (quizFromTime != null) ? Utils.toUnixTimeInSeconds(quizFromTime) : -1;
- String url = "/restapi/assessment_modes/seb?";
+ String url = "/restapi/repo/assessmentmodes?";
if (fromCutTime != -1) {
url = String.format("%sdateFrom=%s&", url, fromCutTime);
}
@@ -295,8 +295,8 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
lmsSetup.getLmsType(),
a.name,
a.description,
- Utils.toDateTimeUTC(a.dateFrom),
- Utils.toDateTimeUTC(a.dateTo),
+ Utils.toDateTimeUTC(a.begin - a.leadTime * 1000 * 60),
+ Utils.toDateTimeUTC(a.end + a.followupTime * 1000 * 60),
examUrl(a.repositoryEntryKey),
new HashMap());
})
@@ -316,7 +316,7 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
private QuizData quizById(final OlatLmsRestTemplate restTemplate, final String id) {
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
- final String url = String.format("/restapi/assessment_modes/%s", id);
+ final String url = String.format("/restapi/repo/assessmentmodes/%s", id);
final AssessmentData a = this.apiGet(restTemplate, url, AssessmentData.class);
return new QuizData(
String.format("%d", a.key),
@@ -325,26 +325,26 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
lmsSetup.getLmsType(),
a.name,
a.description,
- Utils.toDateTimeUTC(a.dateFrom),
- Utils.toDateTimeUTC(a.dateTo),
+ Utils.toDateTimeUTC(a.begin - a.leadTime * 1000 * 60),
+ Utils.toDateTimeUTC(a.end + a.followupTime * 1000 * 60),
examUrl(a.repositoryEntryKey),
new HashMap());
}
private ExamineeAccountDetails getExamineeById(final RestTemplate restTemplate, final String id) {
- final String url = String.format("/restapi/users/%s/name_username", id);
+ final String url = String.format("/restapi/users/%s", id);
final UserData u = this.apiGet(restTemplate, url, UserData.class);
final Map attrs = new HashMap<>();
return new ExamineeAccountDetails(
String.valueOf(u.key),
u.lastName + ", " + u.firstName,
- u.username,
- "OLAT API does not provide email addresses",
+ u.login,
+ u.email,
attrs);
}
private SEBRestriction getRestrictionForAssignmentId(final RestTemplate restTemplate, final String id) {
- final String url = String.format("/restapi/assessment_modes/%s/seb_restriction", id);
+ final String url = String.format("/restapi/repo/assessmentmodes/%s/seb", id);
final RestrictionData r = this.apiGet(restTemplate, url, RestrictionData.class);
final HashMap additionalAttributes = new HashMap<>();
if (StringUtils.isNotBlank(r.quitLink)) {
@@ -362,7 +362,7 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
final String id,
final SEBRestriction restriction) {
- final String url = String.format("/restapi/assessment_modes/%s/seb_restriction", id);
+ final String url = String.format("/restapi/repo/assessmentmodes/%s/seb", id);
final RestrictionDataPost post = new RestrictionDataPost();
post.browserExamKeys = new ArrayList<>(restriction.browserExamKeys);
post.configKeys = new ArrayList<>(restriction.configKeys);
@@ -377,7 +377,7 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
}
private SEBRestriction deleteRestrictionForAssignmentId(final RestTemplate restTemplate, final String id) {
- final String url = String.format("/restapi/assessment_modes/%s/seb_restriction", id);
+ final String url = String.format("/restapi/repo/assessmentmodes/%s/seb", id);
final RestrictionData r = this.apiDelete(restTemplate, url, RestrictionData.class);
// OLAT returns RestrictionData with null values upon deletion.
// We return it here for consistency, even though SEB server does not need it
@@ -473,7 +473,7 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
.getOrThrow();
final ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
- details.setAccessTokenUri(lmsSetup.lmsApiUrl + "/restapi/auth/");
+ details.setAccessTokenUri(lmsSetup.lmsApiUrl + "/restapi/auth/{username}?password={password}");
details.setClientId(plainClientId.toString());
details.setClientSecret(plainClientSecret.toString());
@@ -489,4 +489,5 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
});
}
+
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsData.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsData.java
index 7a7eeb2d..050e695a 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsData.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsData.java
@@ -20,22 +20,24 @@ public final class OlatLmsData {
/*
* OLAT API example:
* {
- * "courseName": "course 1",
- * "dateFrom": 1624420800000,
- * "dateTo": 1624658400000,
+ * "begin": 1624420800000,
+ * "end": 1624658400000,
* "description": "",
* "key": 6356992,
* “repositoryEntryKey”: 462324,
- * "name": "SEB test"
+ * "name": "SEB test",
+ * "leadTime": 15,
+ * "followupTime", 5
* }
*/
public long key;
public long repositoryEntryKey;
public String name;
public String description;
- public String courseName;
- public long dateFrom;
- public long dateTo;
+ public Long begin;
+ public Long end;
+ public long leadTime;
+ public long followupTime;
}
@JsonIgnoreProperties(ignoreUnknown = true)
@@ -46,13 +48,15 @@ public final class OlatLmsData {
* "firstName": "OpenOLAT",
* "key": 360448,
* "lastName": "Administrator",
- * "username": "administrator"
+ * "login": "administrator",
+ * "email": "admin@example.org"
* }
*/
public long key;
public String firstName;
public String lastName;
- public String username;
+ public String login;
+ public String email;
}
@JsonIgnoreProperties(ignoreUnknown = true)
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsRestTemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsRestTemplate.java
index 046c6f07..f2fd8601 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsRestTemplate.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsRestTemplate.java
@@ -16,6 +16,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -116,14 +117,14 @@ public class OlatLmsRestTemplate extends RestTemplate {
// Authenticate with OLAT and store the received X-OLAT-TOKEN
this.token = "authenticating";
final String authUrl = this.details.getAccessTokenUri();
- final Map credentials = new HashMap<>();
- credentials.put("username", this.details.getClientId());
- credentials.put("password", this.details.getClientSecret());
+ final Map parameters = new HashMap<>();
+ parameters.put("username", this.details.getClientId());
+ parameters.put("password", this.details.getClientSecret());
final HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("content-type", "application/json");
- final HttpEntity