diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/MoodleCourse.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/moodle/MoodleCourse.java similarity index 89% rename from backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/MoodleCourse.java rename to backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/moodle/MoodleCourse.java index f6bd9a22..1f1ca589 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/MoodleCourse.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/moodle/MoodleCourse.java @@ -1,11 +1,11 @@ -package de.htwg_konstanz.mobilelearning.helper; +package de.htwg_konstanz.mobilelearning.helper.moodle; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @JsonIgnoreProperties(ignoreUnknown = true) -class MoodleCourse { - public Integer id; +public class MoodleCourse { + public String id; public String fullname; public String shortname; public String summary; @@ -16,7 +16,7 @@ class MoodleCourse { public Integer enddate; public MoodleCourse( - @JsonProperty("id") Integer id, + @JsonProperty("id") String id, @JsonProperty("fullname") String fullname, @JsonProperty("shortname") String shortname, @JsonProperty("summary") String summary, @@ -37,6 +37,10 @@ public MoodleCourse( this.enddate = enddate; } + public String getId() { + return id; + } + @Override public String toString() { return "MoodleCourse{" + diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/MoodleInterface.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/moodle/MoodleInterface.java similarity index 79% rename from backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/MoodleInterface.java rename to backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/moodle/MoodleInterface.java index 45cc0660..56b219aa 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/MoodleInterface.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/moodle/MoodleInterface.java @@ -1,4 +1,4 @@ -package de.htwg_konstanz.mobilelearning.helper; +package de.htwg_konstanz.mobilelearning.helper.moodle; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; @@ -17,7 +17,7 @@ public class MoodleInterface { private Integer userId; public MoodleInterface(String username, - String password) { + String password) { this.username = username; this.password = password; } @@ -25,20 +25,24 @@ public MoodleInterface(String username, public Boolean login() { ObjectMapper mapper = new ObjectMapper(); - + try { - + // get the token from // https://moodle.htwg-konstanz.de/moodle/login/token.php?username=USERNAME&password=PASSWORD&service=SERVICESHORTNAME CloseableHttpClient client = HttpClients.createDefault(); - HttpGet request = new HttpGet("https://moodle.htwg-konstanz.de/moodle/login/token.php?username=" + this.username + "&password=" + this.password + "&service=moodle_mobile_app"); - MoodleTokenResponse tokenResponse = mapper.readValue(client.execute(request).getEntity().getContent(), MoodleTokenResponse.class); + HttpGet request = new HttpGet("https://moodle.htwg-konstanz.de/moodle/login/token.php?username=" + + this.username + "&password=" + this.password + "&service=moodle_mobile_app"); + MoodleTokenResponse tokenResponse = mapper.readValue(client.execute(request).getEntity().getContent(), + MoodleTokenResponse.class); this.token = tokenResponse.token; - // System.out.println("Successfully logged in as " + this.username + " with token " + this.token.substring(0, 5) + "..."); + // System.out.println("Successfully logged in as " + this.username + " with + // token " + this.token.substring(0, 5) + "..."); // get user id String wsFunction = "core_webservice_get_site_info"; - request = new HttpGet("https://moodle.htwg-konstanz.de/moodle/webservice/rest/server.php?wstoken=" + this.token + "&wsfunction=" + wsFunction + "&moodlewsrestformat=json"); + request = new HttpGet("https://moodle.htwg-konstanz.de/moodle/webservice/rest/server.php?wstoken=" + + this.token + "&wsfunction=" + wsFunction + "&moodlewsrestformat=json"); String response = EntityUtils.toString(client.execute(request).getEntity()); MoodleUserIdResponse userIdResponse = mapper.readValue(response, MoodleUserIdResponse.class); Integer userId = userIdResponse.userid; @@ -47,13 +51,14 @@ public Boolean login() { // get all courses wsFunction = "core_enrol_get_users_courses"; - request = new HttpGet("https://moodle.htwg-konstanz.de/moodle/webservice/rest/server.php?wstoken=" + this.token + "&wsfunction=" + wsFunction + "&userid=" + userId + "&moodlewsrestformat=json"); + request = new HttpGet("https://moodle.htwg-konstanz.de/moodle/webservice/rest/server.php?wstoken=" + + this.token + "&wsfunction=" + wsFunction + "&userid=" + userId + "&moodlewsrestformat=json"); response = EntityUtils.toString(client.execute(request).getEntity()); MoodleCourse[] courses = mapper.readValue(response, MoodleCourse[].class); this.courses = List.of(courses); // System.out.println("Got courses: "); // System.out.println(this.courses); - + } catch (Exception e) { System.out.println("Error while logging into moodle: " + e.getMessage()); return false; @@ -70,6 +75,13 @@ public List getCourses() { return this.courses; } + // if token is not set login first + if (this.token == null) { + if (!this.login()) { + return List.of(); + } + } + ObjectMapper mapper = new ObjectMapper(); try { @@ -88,7 +100,7 @@ public List getCourses() { } catch (Exception e) { System.out.println("Error while getting courses from moodle: " + e.getMessage()); - return null; + return List.of(); } } diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/MoodleTokenResponse.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/moodle/MoodleTokenResponse.java similarity index 85% rename from backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/MoodleTokenResponse.java rename to backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/moodle/MoodleTokenResponse.java index f3d23b34..16cc8b31 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/MoodleTokenResponse.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/moodle/MoodleTokenResponse.java @@ -1,4 +1,4 @@ -package de.htwg_konstanz.mobilelearning.helper; +package de.htwg_konstanz.mobilelearning.helper.moodle; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/MoodleUserIdResponse.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/moodle/MoodleUserIdResponse.java similarity index 86% rename from backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/MoodleUserIdResponse.java rename to backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/moodle/MoodleUserIdResponse.java index 65efcb86..cd8127b8 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/MoodleUserIdResponse.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/moodle/MoodleUserIdResponse.java @@ -1,4 +1,4 @@ -package de.htwg_konstanz.mobilelearning.helper; +package de.htwg_konstanz.mobilelearning.helper.moodle; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/models/Course.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/models/Course.java index c01f2264..25404015 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/models/Course.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/models/Course.java @@ -20,7 +20,9 @@ public class Course implements Serializable { public String name; public String description; public List owners; + public List students; public String key; + public String moodleCourseId; // feedback public List feedbackForms; @@ -39,11 +41,13 @@ public Course(String name, String description) { this.name = name; this.description = description; this.owners = new ArrayList(); + this.students = new ArrayList(); this.feedbackForms = new ArrayList(); this.feedbackQuestions = new ArrayList(); this.quizForms = new ArrayList(); this.quizQuestions = new ArrayList(); this.key = ""; + this.moodleCourseId = ""; } // id @@ -89,6 +93,41 @@ public boolean isOwner(User user) { return this.owners.contains(user.getId()); } + // students + public List getStudents() { + return this.students; + } + + public void addStudent(ObjectId student) { + if (!this.students.contains(student)) { + this.students.add(student); + } + } + + public void removeStudent(ObjectId student) { + try { + this.students.remove(student); + } catch (Exception e) { + System.out.println("not in list"); + } + } + + public void setStudents(List students) { + this.students = students; + } + + public boolean isStudent(String userId) { + return this.students.contains(new ObjectId(userId)); + } + + public boolean isStudent(ObjectId userId) { + return this.students.contains(userId); + } + + public boolean isStudent(User user) { + return this.students.contains(user.getId()); + } + // description public String getDescription() { return this.description; @@ -291,6 +330,14 @@ public String getKey() { return this.key; } + public void setMoodleCourseId(String moodleCourseId) { + this.moodleCourseId = moodleCourseId; + } + + public String getMoodleCourseId() { + return this.moodleCourseId; + } + public static Course fromApiCourse(ApiCourse apiCourse) throws IllegalArgumentException { // validate input @@ -310,6 +357,7 @@ public static Course fromApiCourse(ApiCourse apiCourse) throws IllegalArgumentEx // create course Course course = new Course(apiCourse.getName(), apiCourse.getDescription()); course.setKey(apiCourse.getKey()); + course.setMoodleCourseId(apiCourse.getMoodleCourseId()); // create the feedback forms for (ApiFeedbackForm apiFeedbackForm : apiCourse.getFeedbackForms()) { @@ -344,6 +392,7 @@ public void updateFromApiCourse(ApiCourse apiCourse) { // update course this.setName(apiCourse.getName()); this.setDescription(apiCourse.getDescription()); + this.setMoodleCourseId(apiCourse.getMoodleCourseId()); // update feedback forms for (ApiFeedbackForm apiFeedbackForm : apiCourse.getFeedbackForms()) { @@ -368,4 +417,6 @@ public void updateFromApiCourse(ApiCourse apiCourse) { } } + + } \ No newline at end of file diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/models/auth/User.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/models/auth/User.java index 6a57f6e1..b50b8aaa 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/models/auth/User.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/models/auth/User.java @@ -12,6 +12,7 @@ public class User { public String username; public String password; public List roles; + public List courses; public User() { } @@ -23,6 +24,7 @@ public User(String email, String name, String username, String password) { this.username = username; this.password = password; this.roles = new ArrayList(); + this.courses = new ArrayList(); // check if email, name and username have ": " in it and if so, only take the part after it if (this.email.contains(": ")) { @@ -129,4 +131,40 @@ public void setId(ObjectId id) { this.id = id; } + // courses + public List getCourses() { + return this.courses; + } + + public void setCourses(List courses) { + this.courses = courses; + } + + public void addCourse(ObjectId course) { + if (!this.courses.contains(course)) { + this.courses.add(course); + } + } + + public void removeCourse(ObjectId course) { + try { + this.courses.remove(course); + } catch (Exception e) { + System.out.println("not in list"); + } + } + + public boolean hasCourse(ObjectId course) { + return this.courses.contains(course); + } + + public boolean hasCourse(String courseId) { + return this.courses.contains(new ObjectId(courseId)); + } + + public void clearCourses() { + this.courses.clear(); + } + + } diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/repositories/CourseRepository.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/repositories/CourseRepository.java index 3f0943f1..d5a3290f 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/repositories/CourseRepository.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/repositories/CourseRepository.java @@ -2,10 +2,13 @@ +import java.util.List; + import org.bson.types.ObjectId; import de.htwg_konstanz.mobilelearning.models.Course; import de.htwg_konstanz.mobilelearning.models.Form; +import de.htwg_konstanz.mobilelearning.models.auth.User; import de.htwg_konstanz.mobilelearning.models.feedback.FeedbackForm; import de.htwg_konstanz.mobilelearning.models.quiz.QuizForm; import io.quarkus.mongodb.panache.PanacheMongoRepository; @@ -26,6 +29,10 @@ public Course findByQuizFormConnectCode(Integer connectCode) { return find("quizForms.connectCode", connectCode).firstResult(); } + public Course findByMoodleCourseId(String moodleCourseId) { + return find("moodleCourseId", moodleCourseId).firstResult(); + } + public Course findByFormConnectCode(Integer connectCode) { // TODO: make this more efficient Course course = findByFeedbackFormConnectCode(connectCode); @@ -73,4 +80,12 @@ public Course findByKey(String key) { return find("key", key).firstResult(); } + public List listAllForStudent(User user) { + return find("students", user.getId()).list(); + } + + public List listAllForOwner(User user) { + return find("owners", user.getId()).list(); + } + } diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/CourseService.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/CourseService.java index 40257492..ebc7bfb7 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/CourseService.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/CourseService.java @@ -1,13 +1,19 @@ package de.htwg_konstanz.mobilelearning.services; +import java.util.ArrayList; import java.util.List; import org.bson.types.ObjectId; +import org.eclipse.microprofile.jwt.JsonWebToken; import org.jboss.resteasy.reactive.RestPath; +import de.htwg_konstanz.mobilelearning.helper.moodle.MoodleCourse; +import de.htwg_konstanz.mobilelearning.helper.moodle.MoodleInterface; import de.htwg_konstanz.mobilelearning.models.Course; +import de.htwg_konstanz.mobilelearning.models.auth.User; import de.htwg_konstanz.mobilelearning.models.auth.UserRole; import de.htwg_konstanz.mobilelearning.repositories.CourseRepository; +import de.htwg_konstanz.mobilelearning.repositories.UserRepository; import jakarta.annotation.security.RolesAllowed; import jakarta.inject.Inject; import jakarta.ws.rs.GET; @@ -24,6 +30,12 @@ public class CourseService { @Inject private CourseRepository courseRepository; + @Inject + private UserRepository userRepository; + + @Inject + JsonWebToken jwt; + @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{courseId}") @@ -39,7 +51,15 @@ public Course getCourse(@RestPath String courseId) { @Produces(MediaType.APPLICATION_JSON) @RolesAllowed({ UserRole.PROF, UserRole.STUDENT }) public List getCourses() { - List courses = courseRepository.listAll(); + + // update the courses linked to the user + User user = userRepository.findByUsername(jwt.getName()); + if (user == null) { + throw new NotFoundException("User not found."); + } + updateCourseLinkedToUser(user); + + List courses = courseRepository.listAllForStudent(user); courses.forEach(course -> { course.feedbackForms.forEach(form -> { form.questions = List.of(); @@ -106,6 +126,55 @@ public void deleteAllCourses() { courseRepository.deleteAll(); } + public void updateCourseLinkedToUser(User user) { + + // TEMP: mock the special users (Prof, Student, Admin) + if (user.getUsername().equals("Prof") || user.getUsername().equals("Student") || user.getUsername().equals("Admin")) { + return; + } + + // use the moodle interface to get the courses linked to the user + MoodleInterface moodle = new MoodleInterface(user.getUsername(), user.getPassword()); + List moodleCourses = moodle.getCourses(); + + // update the courses linked to the user + List previousCourses = user.getCourses(); + + // case 1: the user was added to a new course (-> add the course to the user and the student to the course) + for (MoodleCourse moodleCourse : moodleCourses) { + Course course = courseRepository.findByMoodleCourseId(moodleCourse.getId()); + if (course == null) { + continue; + } + if (!user.hasCourse(course.getId()) || !course.isStudent(user.getId())) { + user.addCourse(course.getId()); + course.addStudent(user.getId()); + courseRepository.update(course); + } + }; + + // case 2: the user has a course that is not in the moodle courses (-> remove the course from the user and the student from the course) + List moodleCourseIds = moodleCourses.stream().map(moodleCourse -> moodleCourse.getId()).toList(); + List coursesToRemove = new ArrayList(); + for (ObjectId courseId : previousCourses) { + Course course = courseRepository.findById(courseId); + if (course == null) { + continue; + } + if (!moodleCourseIds.contains(course.getMoodleCourseId())) { + coursesToRemove.add(course); + } + }; + for (Course course : coursesToRemove) { + course.removeStudent(user.getId()); + courseRepository.update(course); + user.removeCourse(course.getId()); + } + + // update the user + userRepository.update(user); + } + // for testing public CourseRepository getCourseRepository() { return courseRepository; diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/api/ApiService.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/api/ApiService.java index e992a8dc..00a7878c 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/api/ApiService.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/api/ApiService.java @@ -66,7 +66,7 @@ public List updateCourses(List courses) { } } - return courseRepository.findAll().list(); + return courseRepository.listAllForOwner(user); } } diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/api/models/ApiCourse.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/api/models/ApiCourse.java index 07cdfd9e..6cfed40c 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/api/models/ApiCourse.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/api/models/ApiCourse.java @@ -8,6 +8,7 @@ public class ApiCourse { public List feedbackForms; public List quizForms; public String key; + public String moodleCourseId; public ApiCourse() { } @@ -47,4 +48,8 @@ public List getQuizForms() { public String getKey() { return key; } + + public String getMoodleCourseId() { + return moodleCourseId; + } } diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/auth/UserService.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/auth/UserService.java index b7a1152e..3cbde27e 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/auth/UserService.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/auth/UserService.java @@ -128,13 +128,15 @@ public Response login(@RestHeader("Authorization") String authorization) throws userFromLdap.getEmail(), userFromLdap.getName(), userFromLdap.getUsername(), - userFromLdap.getPassword() + password ); newUser.setRoles(userFromLdap.getRoles()); userRepository.persist(newUser); user = newUser; } else { user = existingUser; + user.setPassword(password); + userRepository.update(user); } // return jwt token diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/feedback/socket/LiveFeedbackSocket.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/feedback/socket/LiveFeedbackSocket.java index 8f6cad77..afde7ca3 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/feedback/socket/LiveFeedbackSocket.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/feedback/socket/LiveFeedbackSocket.java @@ -21,7 +21,6 @@ import de.htwg_konstanz.mobilelearning.repositories.UserRepository; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import jakarta.websocket.Endpoint; import jakarta.websocket.OnClose; import jakarta.websocket.OnError; import jakarta.websocket.OnMessage; @@ -71,6 +70,12 @@ public void onOpen(Session session, @PathParam("courseId") String courseId, @Pat System.out.println("Form ID: " + formId); System.out.println("User ID: " + userId); + // check if user is student of the course + if (!course.isStudent(userId)) { + System.out.println("User is not a student of the course"); + return; + } + // check if the user is a participant or a owner (by checking if the user is owner of the course) Boolean isOwner = course.isOwner(userId); SocketConnectionType type = isOwner ? SocketConnectionType.OWNER : SocketConnectionType.PARTICIPANT; diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java index 2678c1ae..948ded6f 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java @@ -86,6 +86,12 @@ public void onOpen( System.out.println("Form ID: " + formId); System.out.println("User ID: " + userId); + // check if user is student of the course + if (!course.isStudent(userId)) { + System.out.println("User is not a student of the course"); + return; + } + // check if the user is a participant or a owner (by checking if the user is owner of the course) SocketConnectionType type = isOwner ? SocketConnectionType.OWNER : SocketConnectionType.PARTICIPANT;