diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccess.java index 71a88835..4867d15e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccess.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccess.java @@ -47,6 +47,7 @@ public class MoodleCourseAccess extends CourseAccess { private static final Logger log = LoggerFactory.getLogger(MoodleCourseAccess.class); private static final String MOODLE_QUIZ_START_URL_PATH = "mod/quiz/view.php?id="; + private static final String MOODLE_COURSE_START_URL_PATH = "course/view.php?id="; private static final String MOODLE_COURSE_API_FUNCTION_NAME = "core_course_get_courses"; private static final String MOODLE_USER_PROFILE_API_FUNCTION_NAME = "core_user_get_users_by_field"; private static final String MOODLE_QUIZ_API_FUNCTION_NAME = "mod_quiz_get_quizzes_by_courses"; @@ -55,6 +56,7 @@ public class MoodleCourseAccess extends CourseAccess { private final JSONMapper jsonMapper; private final LmsSetup lmsSetup; private final MoodleRestTemplateFactory moodleRestTemplateFactory; + private final boolean includeCourses; private MoodleAPIRestTemplate restTemplate; @@ -62,12 +64,14 @@ public class MoodleCourseAccess extends CourseAccess { final JSONMapper jsonMapper, final LmsSetup lmsSetup, final MoodleRestTemplateFactory moodleRestTemplateFactory, - final AsyncService asyncService) { + final AsyncService asyncService, + final boolean includeCourses) { super(asyncService); this.jsonMapper = jsonMapper; this.lmsSetup = lmsSetup; this.moodleRestTemplateFactory = moodleRestTemplateFactory; + this.includeCourses = includeCourses; } @Override @@ -222,7 +226,7 @@ public class MoodleCourseAccess extends CourseAccess { static Map additionalAttrs = new HashMap<>(); - private static List quizDataOf( + private List quizDataOf( final LmsSetup lmsSetup, final CourseData courseData, final String uriPrefix) { @@ -234,13 +238,13 @@ public class MoodleCourseAccess extends CourseAccess { additionalAttrs.put(QuizData.ATTR_ADDITIONAL_DISPLAY_NAME, courseData.display_name); additionalAttrs.put(QuizData.ATTR_ADDITIONAL_SUMMARY, courseData.summary); - return courseData.quizzes + final List courseAndQuiz = courseData.quizzes .stream() .map(courseQuizData -> { - final String startURI = uriPrefix + courseQuizData.id; + final String startURI = uriPrefix + courseQuizData.course_module; additionalAttrs.put(QuizData.ATTR_ADDITIONAL_TIME_LIMIT, String.valueOf(courseQuizData.time_limit)); return new QuizData( - courseQuizData.id, + courseData.id + ":" + courseQuizData.id, lmsSetup.getInstitutionId(), lmsSetup.id, lmsSetup.getLmsType(), @@ -252,6 +256,22 @@ public class MoodleCourseAccess extends CourseAccess { additionalAttrs); }) .collect(Collectors.toList()); + + if (this.includeCourses) { + courseAndQuiz.add(new QuizData( + courseData.id, + lmsSetup.getInstitutionId(), + lmsSetup.id, + lmsSetup.getLmsType(), + courseData.display_name, + courseData.full_name, + Utils.toDateTimeUTCUnix(courseData.start_date), + Utils.toDateTimeUTCUnix(courseData.end_date), + lmsSetup.lmsApiUrl + MOODLE_COURSE_START_URL_PATH + courseData.id, + additionalAttrs)); + } + + return courseAndQuiz; } private Result getRestTemplate() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseRestriction.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseRestriction.java index a8abe75f..179db87e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseRestriction.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseRestriction.java @@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle; import java.util.ArrayList; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.LinkedMultiValueMap; @@ -27,11 +28,27 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestT * http://yourmoodle.org/webservice/rest/server.php?wstoken={token}&moodlewsrestformat=json&wsfunction=seb_restriction&courseId=123 * * Response (JSON): - * {"courseId"="123", "configKeys"=["key1","key2","key3",...], "browserKeys"=["bkey1", "bkey2", "bkey3",...]} + * + *
+ * {
+ *   "courseId": "123",
+ *   "quizId": "456",
+ *   "configKeys": [
+ *       "key1",
+ *       "key2",
+ *       "key3"
+ *   ],
+ *   "browserKeys": [
+ *       "bkey1",
+ *       "bkey2",
+ *       "bkey3"
+ *  ]
+ * }
+ * 
* * Set keys: * POST: - * http://yourmoodle.org/webservice/rest/server.php?wstoken={token}&moodlewsrestformat=json&wsfunction=seb_restriction&courseId=123&configKey[0]=key1&configKey[1]=key2&browserKey[0]=bkey1&browserKey[1]=bkey2 + * http://yourmoodle.org/webservice/rest/server.php?wstoken={token}&moodlewsrestformat=json&wsfunction=seb_restriction_update&courseId=123&configKey[0]=key1&configKey[1]=key2&browserKey[0]=bkey1&browserKey[1]=bkey2 * * Delete all key (and remove restrictions): * POST: @@ -41,8 +58,11 @@ public class MoodleCourseRestriction { private static final Logger log = LoggerFactory.getLogger(MoodleCourseRestriction.class); private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION = "seb_restriction"; + private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION_CREATE = "seb_restriction_create"; + private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION_UPDATE = "seb_restriction_update"; private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION_DELETE = "seb_restriction_delete"; private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_COURSE_ID = "courseId"; + private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_QUIZ_ID = "quizId"; private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_CONFIG_KEY = "configKey"; private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_BROWSER_KEY = "browserKey"; @@ -64,10 +84,29 @@ public class MoodleCourseRestriction { return LmsSetupTestResult.ofQuizAccessAPIError("not available yet"); } - Result getSEBRestriction(final String courseId) { + Result getSEBRestriction( + final String externalId) { + + return Result.tryCatch(() -> { + final String[] courseQuizId = StringUtils.split(externalId, ":"); + if (courseQuizId.length > 1) { + // we only have the course id (this is a course) + return getSEBRestriction(courseQuizId[0], null) + .getOrThrow(); + } else { + // we have the course id and the quiz is (this is a quiz) + return getSEBRestriction(courseQuizId[0], courseQuizId[1]) + .getOrThrow(); + } + }); + } + + Result getSEBRestriction( + final String courseId, + final String quizId) { if (log.isDebugEnabled()) { - log.debug("GET SEB Client restriction on course: {}", courseId); + log.debug("GET SEB Client restriction on course: {} quiz: {}", courseId, quizId); } return Result.tryCatch(() -> { @@ -77,6 +116,9 @@ public class MoodleCourseRestriction { final MultiValueMap queryParams = new LinkedMultiValueMap<>(); queryParams.add(MOODLE_DEFAULT_COURSE_RESTRICTION_COURSE_ID, courseId); + if (quizId != null) { + queryParams.add(MOODLE_DEFAULT_COURSE_RESTRICTION_QUIZ_ID, quizId); + } final String resultJSON = template.callMoodleAPIFunction( MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION, @@ -92,43 +134,103 @@ public class MoodleCourseRestriction { }); } - Result putSEBRestriction( - final String courseId, + Result createSEBRestriction( + final String externalId, final MoodleSEBRestriction restriction) { - if (log.isDebugEnabled()) { - log.debug("PUT SEB Client restriction on course: {} : {}", courseId, restriction); - } - return Result.tryCatch(() -> { - - final MoodleAPIRestTemplate template = getRestTemplate() - .getOrThrow(); - - final MultiValueMap queryParams = new LinkedMultiValueMap<>(); - queryParams.add(MOODLE_DEFAULT_COURSE_RESTRICTION_COURSE_ID, courseId); - - final MultiValueMap queryAttributes = new LinkedMultiValueMap<>(); - queryAttributes.addAll( - MOODLE_DEFAULT_COURSE_RESTRICTION_CONFIG_KEY, - new ArrayList<>(restriction.configKeys)); - queryAttributes.addAll( - MOODLE_DEFAULT_COURSE_RESTRICTION_BROWSER_KEY, - new ArrayList<>(restriction.browserExamKeys)); - - template.callMoodleAPIFunction( - MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION, - queryParams, - queryAttributes); - - return true; + final String[] courseQuizId = StringUtils.split(externalId, ":"); + if (courseQuizId.length > 1) { + // we only have the course id (this is a course) + return createSEBRestriction(courseQuizId[0], null, restriction) + .getOrThrow(); + } else { + // we have the course id and the quiz is (this is a quiz) + return createSEBRestriction(courseQuizId[0], courseQuizId[1], restriction) + .getOrThrow(); + } }); } - Result deleteSEBRestriction(final String courseId) { + Result createSEBRestriction( + final String courseId, + final String quizId, + final MoodleSEBRestriction restriction) { if (log.isDebugEnabled()) { - log.debug("DELETE SEB Client restriction on course: {}", courseId); + log.debug("POST SEB Client restriction on course: {} quiz: restriction : {}", + courseId, + quizId, + restriction); + } + + return postSEBRestriction( + courseId, + quizId, + MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION_CREATE, + restriction); + } + + Result updateSEBRestriction( + final String externalId, + final MoodleSEBRestriction restriction) { + + return Result.tryCatch(() -> { + final String[] courseQuizId = StringUtils.split(externalId, ":"); + if (courseQuizId.length > 1) { + // we only have the course id (this is a course) + return updateSEBRestriction(courseQuizId[0], null, restriction) + .getOrThrow(); + } else { + // we have the course id and the quiz is (this is a quiz) + return updateSEBRestriction(courseQuizId[0], courseQuizId[1], restriction) + .getOrThrow(); + } + }); + } + + Result updateSEBRestriction( + final String courseId, + final String quizId, + final MoodleSEBRestriction restriction) { + + if (log.isDebugEnabled()) { + log.debug("POST SEB Client restriction on course: {} quiz: restriction : {}", + courseId, + quizId, + restriction); + } + + return postSEBRestriction( + courseId, + quizId, + MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION_UPDATE, + restriction); + } + + Result deleteSEBRestriction( + final String externalId) { + + return Result.tryCatch(() -> { + final String[] courseQuizId = StringUtils.split(externalId, ":"); + if (courseQuizId.length > 1) { + // we only have the course id (this is a course) + return deleteSEBRestriction(courseQuizId[0], null) + .getOrThrow(); + } else { + // we have the course id and the quiz is (this is a quiz) + return deleteSEBRestriction(courseQuizId[0], courseQuizId[1]) + .getOrThrow(); + } + }); + } + + Result deleteSEBRestriction( + final String courseId, + final String quizId) { + + if (log.isDebugEnabled()) { + log.debug("DELETE SEB Client restriction on course: {} quizId {}", courseId, quizId); } return Result.tryCatch(() -> { @@ -137,6 +239,7 @@ public class MoodleCourseRestriction { final MultiValueMap queryParams = new LinkedMultiValueMap<>(); queryParams.add(MOODLE_DEFAULT_COURSE_RESTRICTION_COURSE_ID, courseId); + queryParams.add(MOODLE_DEFAULT_COURSE_RESTRICTION_QUIZ_ID, quizId); template.callMoodleAPIFunction( MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION_DELETE, @@ -161,4 +264,40 @@ public class MoodleCourseRestriction { return Result.of(this.restTemplate); } + private Result postSEBRestriction( + final String courseId, + final String quizId, + final String function, + final MoodleSEBRestriction restriction) { + return Result.tryCatch(() -> { + + final MoodleAPIRestTemplate template = getRestTemplate() + .getOrThrow(); + + final MultiValueMap queryParams = new LinkedMultiValueMap<>(); + queryParams.add(MOODLE_DEFAULT_COURSE_RESTRICTION_COURSE_ID, courseId); + queryParams.add(MOODLE_DEFAULT_COURSE_RESTRICTION_QUIZ_ID, quizId); + + final MultiValueMap queryAttributes = new LinkedMultiValueMap<>(); + queryAttributes.addAll( + MOODLE_DEFAULT_COURSE_RESTRICTION_CONFIG_KEY, + new ArrayList<>(restriction.configKeys)); + queryAttributes.addAll( + MOODLE_DEFAULT_COURSE_RESTRICTION_BROWSER_KEY, + new ArrayList<>(restriction.browserExamKeys)); + + final String resultJSON = template.callMoodleAPIFunction( + function, + queryParams, + queryAttributes); + + final MoodleSEBRestriction restrictiondata = this.jsonMapper.readValue( + resultJSON, + new TypeReference() { + }); + + return restrictiondata; + }); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplate.java index bbfd2a0b..4f0fdebc 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplate.java @@ -115,7 +115,7 @@ public class MoodleLmsAPITemplate implements LmsAPITemplate { } return this.moodleCourseRestriction - .putSEBRestriction( + .updateSEBRestriction( externalExamId, MoodleSEBRestriction.from(sebRestrictionData)) .map(result -> sebRestrictionData); @@ -127,7 +127,8 @@ public class MoodleLmsAPITemplate implements LmsAPITemplate { log.debug("Release SEB Client restriction for Exam: {}", exam); } - return this.moodleCourseRestriction.deleteSEBRestriction(exam.externalId) + return this.moodleCourseRestriction + .deleteSEBRestriction(exam.externalId) .map(result -> exam); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplateFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplateFactory.java index 7c64426c..b7b255e0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplateFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplateFactory.java @@ -34,13 +34,15 @@ public class MoodleLmsAPITemplateFactory { private final ClientCredentialService clientCredentialService; private final ClientHttpRequestFactoryService clientHttpRequestFactoryService; private final String[] alternativeTokenRequestPaths; + private final boolean includeCourses; protected MoodleLmsAPITemplateFactory( final JSONMapper jsonMapper, final AsyncService asyncService, final ClientCredentialService clientCredentialService, final ClientHttpRequestFactoryService clientHttpRequestFactoryService, - @Value("${sebserver.webservice.lms.moodle.api.token.request.paths:}") final String alternativeTokenRequestPaths) { + @Value("${sebserver.webservice.lms.moodle.api.token.request.paths:}") final String alternativeTokenRequestPaths, + @Value("${sebserver.webservice.lms.moodle.api.includeCourses:false}") final boolean includeCourses) { this.jsonMapper = jsonMapper; this.asyncService = asyncService; @@ -49,6 +51,7 @@ public class MoodleLmsAPITemplateFactory { this.alternativeTokenRequestPaths = (alternativeTokenRequestPaths != null) ? StringUtils.split(alternativeTokenRequestPaths, Constants.LIST_SEPARATOR) : null; + this.includeCourses = includeCourses; } public Result create( @@ -71,7 +74,8 @@ public class MoodleLmsAPITemplateFactory { this.jsonMapper, lmsSetup, moodleRestTemplateFactory, - this.asyncService); + this.asyncService, + this.includeCourses); final MoodleCourseRestriction moodleCourseRestriction = new MoodleCourseRestriction( this.jsonMapper, diff --git a/src/main/resources/config/application-dev-ws.properties b/src/main/resources/config/application-dev-ws.properties index 1299fd10..ee91d785 100644 --- a/src/main/resources/config/application-dev-ws.properties +++ b/src/main/resources/config/application-dev-ws.properties @@ -49,6 +49,7 @@ sebserver.webservice.api.pagination.maxPageSize=500 # comma separated list of known possible OpenEdX API access token request endpoints sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token sebserver.webservice.lms.moodle.api.token.request.paths= +sebserver.webservice.lms.moodle.api.includeCourses=true sebserver.webservice.lms.address.alias=lms.mockup.com=lms.address.alias # NOTE: This is a temporary work-around for SEB Restriction API within Open edX SEB integration plugin to diff --git a/src/main/resources/config/application-ws.properties b/src/main/resources/config/application-ws.properties index 559f1061..2df4aa5c 100644 --- a/src/main/resources/config/application-ws.properties +++ b/src/main/resources/config/application-ws.properties @@ -51,4 +51,5 @@ sebserver.webservice.api.pagination.maxPageSize=500 # comma separated list of known possible OpenEdX API access token request endpoints sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token sebserver.webservice.lms.moodle.api.token.request.paths=/login/token.php +sebserver.webservice.lms.moodle.api.includeCourses=false sebserver.webservice.lms.address.alias= \ No newline at end of file