diff --git a/backend/src/main/java/ovh/herisson/Clyde/EndPoints/CourseController.java b/backend/src/main/java/ovh/herisson/Clyde/EndPoints/CourseController.java index 3dd81fd..c021e0e 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/EndPoints/CourseController.java +++ b/backend/src/main/java/ovh/herisson/Clyde/EndPoints/CourseController.java @@ -13,6 +13,7 @@ import ovh.herisson.Clyde.Tables.UserCurriculum; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; @RestController @@ -149,13 +150,13 @@ public class CourseController { //Get all the courses followed by an user - @GetMapping("/usercourses/{userId}") - public ResponseEntity> getAllUserCourses(@PathVariable long userId){ - User u = userService.getUserById(userId); + @GetMapping("/usercourses") + public ResponseEntity> getAllUserCourses(@RequestHeader("Authorization") String token){ + User u = authServ.getUserFromToken(token); //We get all the actual curriculums of the user - ArrayList userCurricula = userCurriculumService.findByStudentAndActual(u, true); - ArrayList toReturn = new ArrayList<>(); + List userCurricula = userCurriculumService.findByStudentAndActual(u, true); + List toReturn = new ArrayList<>(); //We iterate through all the curriculums and we extract the courses for (int i = 0; i < userCurricula.size(); i++){ diff --git a/backend/src/main/java/ovh/herisson/Clyde/EndPoints/Msg/ForumController.java b/backend/src/main/java/ovh/herisson/Clyde/EndPoints/Msg/ForumController.java new file mode 100644 index 0000000..a0c74f5 --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/EndPoints/Msg/ForumController.java @@ -0,0 +1,113 @@ +package ovh.herisson.Clyde.EndPoints.Msg; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.websocket.server.PathParam; +import lombok.AllArgsConstructor; +import ovh.herisson.Clyde.Repositories.CourseRepository; +import ovh.herisson.Clyde.Repositories.Msg.AnswerRepository; +import ovh.herisson.Clyde.Repositories.Msg.ForumRepository; +import ovh.herisson.Clyde.Repositories.Msg.TopicRepository; +import ovh.herisson.Clyde.Responses.UnauthorizedResponse; +import ovh.herisson.Clyde.Services.AuthenticatorService; +import ovh.herisson.Clyde.Services.CourseService; +import ovh.herisson.Clyde.Services.Msg.ForumService; +import ovh.herisson.Clyde.Tables.Course; +import ovh.herisson.Clyde.Tables.Role; +import ovh.herisson.Clyde.Tables.Token; +import ovh.herisson.Clyde.Tables.User; +import ovh.herisson.Clyde.Tables.Msg.Answer; +import ovh.herisson.Clyde.Tables.Msg.Forum; +import ovh.herisson.Clyde.Tables.Msg.Topic; + +@RestController +@CrossOrigin(originPatterns = "*", allowCredentials = "true") +@AllArgsConstructor +public class ForumController { + + private CourseRepository courseRepo; + private AuthenticatorService authServ; + private ForumService forumServ; + private ForumRepository forumRepo; + private TopicRepository topicRepo; + + //// Endpoints to get and create new forums + + @GetMapping("/forums/{id}") + public ResponseEntity> getForumFromCourseId(@RequestHeader("Authorization") String token, @PathVariable long id){ + User u = authServ.getUserFromToken(token); + if(u == null){ + return new UnauthorizedResponse<>(null); + } + return new ResponseEntity<>(courseRepo.findById(id).getForums(), HttpStatus.OK); + } + + @PostMapping("/forums/{id}") + public ResponseEntity createForumOfCourse(@RequestHeader("Authorization") String token, @PathVariable long id, @RequestBody Forum data){ + User u = authServ.getUserFromToken(token); + Course c = courseRepo.findById(id); + if(!(c.getOwner().equals(u) || u.getRole() == Role.Admin)){ + return new UnauthorizedResponse<>(null); + } + forumServ.createForum(c, data); + return new ResponseEntity<>(HttpStatus.ACCEPTED); + } + + //// Endpoints to get and create forum's topic + + @GetMapping("/forum/{id}") + public ResponseEntity> getTopicsFromForumId(@RequestHeader("Authorization") String token, @PathVariable long id){ + User u = authServ.getUserFromToken(token); + if(u == null){ + return new UnauthorizedResponse<>(null); + } + return new ResponseEntity<>(forumRepo.findById(id).orElse(null).getTopics(), HttpStatus.OK); + } + + @PostMapping("/forum/{id}") + public ResponseEntity postTopicToForum(@RequestHeader("Authorization") String token, @PathVariable long id, @RequestBody Topic data){ + User u = authServ.getUserFromToken(token); + Forum f = forumRepo.findById(id).orElse(null); + if(!(f.getWriters().contains(u) || u.getRole() == Role.Admin)){ + return new UnauthorizedResponse<>(null); + } + forumServ.createTopic(f, data); + return new ResponseEntity<>(HttpStatus.ACCEPTED); + } + + //// Endpoints related to topics and messages + + @GetMapping("/forum/post/{id}") + public ResponseEntity getPost(@RequestHeader("Authorization") String token, @PathVariable long id){ + User u = authServ.getUserFromToken(token); + if(u == null){ + return new UnauthorizedResponse<>(null); + } + Topic t = topicRepo.findById(id).orElse(null); + return new ResponseEntity<>(t, HttpStatus.OK); + } + + @PostMapping("/forum/post/{id}") + public ResponseEntity postTopicToForum(@RequestHeader("Authorization") String token, @PathVariable long id, @RequestBody Answer data){ + User u = authServ.getUserFromToken(token); + Topic t = topicRepo.findById(id).orElse(null); + if(t.isLocked() && u.getRole() != Role.Admin){ + return new UnauthorizedResponse<>(null); + } + System.out.println(data); + forumServ.answerTopic(t, data, u); + return new ResponseEntity<>(HttpStatus.ACCEPTED); + } +} diff --git a/backend/src/main/java/ovh/herisson/Clyde/Repositories/Msg/AnswerRepository.java b/backend/src/main/java/ovh/herisson/Clyde/Repositories/Msg/AnswerRepository.java new file mode 100644 index 0000000..0ca5e22 --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/Repositories/Msg/AnswerRepository.java @@ -0,0 +1,10 @@ +package ovh.herisson.Clyde.Repositories.Msg; + +import org.springframework.data.repository.CrudRepository; + +import ovh.herisson.Clyde.Tables.Msg.Answer; + +public interface AnswerRepository extends CrudRepository { + +} + diff --git a/backend/src/main/java/ovh/herisson/Clyde/Repositories/Msg/ForumRepository.java b/backend/src/main/java/ovh/herisson/Clyde/Repositories/Msg/ForumRepository.java new file mode 100644 index 0000000..07afe54 --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/Repositories/Msg/ForumRepository.java @@ -0,0 +1,9 @@ +package ovh.herisson.Clyde.Repositories.Msg; + +import org.springframework.data.repository.CrudRepository; + +import ovh.herisson.Clyde.Tables.Msg.Forum; + +public interface ForumRepository extends CrudRepository { + +} diff --git a/backend/src/main/java/ovh/herisson/Clyde/Repositories/Msg/TopicRepository.java b/backend/src/main/java/ovh/herisson/Clyde/Repositories/Msg/TopicRepository.java new file mode 100644 index 0000000..905cd01 --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/Repositories/Msg/TopicRepository.java @@ -0,0 +1,10 @@ +package ovh.herisson.Clyde.Repositories.Msg; + +import org.springframework.data.repository.CrudRepository; + +import ovh.herisson.Clyde.Tables.Msg.Topic; + +public interface TopicRepository extends CrudRepository { + +} + diff --git a/backend/src/main/java/ovh/herisson/Clyde/Services/Msg/ForumService.java b/backend/src/main/java/ovh/herisson/Clyde/Services/Msg/ForumService.java new file mode 100644 index 0000000..02fca22 --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/Services/Msg/ForumService.java @@ -0,0 +1,38 @@ +package ovh.herisson.Clyde.Services.Msg; + +import org.springframework.stereotype.Service; + +import lombok.AllArgsConstructor; +import ovh.herisson.Clyde.Repositories.CourseRepository; +import ovh.herisson.Clyde.Repositories.Msg.ForumRepository; +import ovh.herisson.Clyde.Repositories.Msg.TopicRepository; +import ovh.herisson.Clyde.Tables.Course; +import ovh.herisson.Clyde.Tables.User; +import ovh.herisson.Clyde.Tables.Msg.Answer; +import ovh.herisson.Clyde.Tables.Msg.Forum; +import ovh.herisson.Clyde.Tables.Msg.Topic; + +@Service +@AllArgsConstructor +public class ForumService { + + private CourseRepository courseRepo; + private ForumRepository forumRepo; + private TopicRepository topicRepo; + + public void createForum(Course c, Forum f){ + c.addForum(f); + courseRepo.save(c); + } + + public void createTopic(Forum f, Topic data) { + f.addTopic(data); + forumRepo.save(f); + } + + public void answerTopic(Topic t, Answer data, User u) { + data.setAuthor(u); + t.addAnswer(data); + topicRepo.save(t); + } +} diff --git a/backend/src/main/java/ovh/herisson/Clyde/Services/ProtectionService.java b/backend/src/main/java/ovh/herisson/Clyde/Services/ProtectionService.java index 1c9b944..8c3d847 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Services/ProtectionService.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Services/ProtectionService.java @@ -51,7 +51,7 @@ public class ProtectionService { HashMap toReturn = new HashMap<>(); - toReturn.put("courseId",course.getCourseID()); + toReturn.put("courseID",course.getCourseID()); toReturn.put("credits",course.getCredits()); toReturn.put("title", course.getTitle()); toReturn.put("owner", userWithoutPassword(course.getOwner())); diff --git a/backend/src/main/java/ovh/herisson/Clyde/Tables/Course.java b/backend/src/main/java/ovh/herisson/Clyde/Tables/Course.java index df0421d..369a8bc 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Tables/Course.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/Course.java @@ -1,10 +1,20 @@ package ovh.herisson.Clyde.Tables; import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ovh.herisson.Clyde.Tables.Msg.Forum; + +import java.util.List; + import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; @Entity +@Data +@NoArgsConstructor +@AllArgsConstructor public class Course { @Id @GeneratedValue(strategy = GenerationType.AUTO) @@ -17,39 +27,18 @@ public class Course { @JoinColumn(name = "Users") private User owner; + //// Extension Messagerie ///// + @OneToMany(cascade = CascadeType.ALL) + private List forums; + + public void addForum(Forum f){ + forums.add(f); + } + /////////////////////////////// + public Course(int credits, String title, User owner){ this.credits = credits; this.title = title; this.owner = owner; } - - public Course() {} - - public int getCourseID() { - return courseID; - } - - public int getCredits() { - return credits; - } - - public void setCredits(int credits){ - this.credits = credits; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title){ - this.title = title; - } - - public User getOwner() { - return owner; - } - - public void setOwner(User owner) { - this.owner = owner; - } } diff --git a/backend/src/main/java/ovh/herisson/Clyde/Tables/Msg/Answer.java b/backend/src/main/java/ovh/herisson/Clyde/Tables/Msg/Answer.java new file mode 100644 index 0000000..4101abb --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/Msg/Answer.java @@ -0,0 +1,29 @@ +package ovh.herisson.Clyde.Tables.Msg; + + +import java.util.Date; + +import org.hibernate.annotations.CreationTimestamp; + +import jakarta.persistence.*; +import lombok.Data; +import ovh.herisson.Clyde.Tables.User; + +@Entity +@Data +public class Answer { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private int id; + + @CreationTimestamp + private Date creation; + + private String content; + + @ManyToOne(cascade=CascadeType.ALL) + private User author; + + private boolean anonymous; + +} diff --git a/backend/src/main/java/ovh/herisson/Clyde/Tables/Msg/Forum.java b/backend/src/main/java/ovh/herisson/Clyde/Tables/Msg/Forum.java new file mode 100644 index 0000000..75508aa --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/Msg/Forum.java @@ -0,0 +1,35 @@ +package ovh.herisson.Clyde.Tables.Msg; + +import java.util.List; + +import jakarta.persistence.*; +import lombok.Data; +import ovh.herisson.Clyde.Tables.Course; +import ovh.herisson.Clyde.Tables.User; + +@Entity +@Data +public class Forum { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private int id; + + @ManyToOne + private Course course; + + private String name; + + @OneToMany(cascade = CascadeType.ALL) + private List topics; + + public void addTopic(Topic t) { + topics.add(t); + } + + @OneToMany + private List writers; // User who are authorized to create a post + + @OneToMany + private List register; +} diff --git a/backend/src/main/java/ovh/herisson/Clyde/Tables/Msg/Topic.java b/backend/src/main/java/ovh/herisson/Clyde/Tables/Msg/Topic.java new file mode 100644 index 0000000..3641f5f --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/Msg/Topic.java @@ -0,0 +1,31 @@ +package ovh.herisson.Clyde.Tables.Msg; + +import java.util.List; + +import jakarta.persistence.*; +import lombok.Data; +import ovh.herisson.Clyde.Tables.User; + +@Entity +@Data +public class Topic { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private int id; + + private String subject, content; + + @ManyToOne + private User author; + + @OneToMany(cascade = CascadeType.ALL) + private List answers; + + public void addAnswer(Answer a){ + answers.add(a); + } + + private boolean locked; // true if new messages can be posted + +} diff --git a/backend/src/main/java/ovh/herisson/Clyde/Tables/User.java b/backend/src/main/java/ovh/herisson/Clyde/Tables/User.java index b21738e..3fe8559 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Tables/User.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/User.java @@ -11,6 +11,7 @@ import ovh.herisson.Clyde.Tables.Msg.Message; import java.util.Date; import java.util.List; +import com.fasterxml.jackson.annotation.JsonIgnore; @Entity @Table(name = "Users") public class User { @@ -27,7 +28,6 @@ public class User { private Date birthDate; private String profilePictureUrl; private Role role; - @JsonIgnore private String password; diff --git a/backend/src/main/java/ovh/herisson/Clyde/Tables/UserCurriculum.java b/backend/src/main/java/ovh/herisson/Clyde/Tables/UserCurriculum.java index 395eca7..34caf26 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Tables/UserCurriculum.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/UserCurriculum.java @@ -1,10 +1,17 @@ package ovh.herisson.Clyde.Tables; import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; @Entity +@Data +@AllArgsConstructor +@NoArgsConstructor public class UserCurriculum { @Id @GeneratedValue(strategy = GenerationType.AUTO) @@ -26,48 +33,10 @@ public class UserCurriculum { //True if the user has that curriculum at the moment false if not private boolean actual; - public UserCurriculum(User user, Curriculum curriculum, int year, boolean actual){ - this.user = user; - this.curriculum = curriculum; - this.year = year; - this.actual = actual; - } - - public UserCurriculum() {} - - public int getId() { - return id; - } - - public User getUser() { - return user; - } - - public void setUser(User user) { - this.user = user; - } - - public Curriculum getCurriculum() { - return curriculum; - } - - public void setCurriculum(Curriculum curriculum) { - this.curriculum = curriculum; - } - - public int getYear() { - return year; - } - - public void setYear(int year) { - this.year = year; - } - - public void setActual(boolean actual) { - this.actual = actual; - } - - public boolean isActual() { - return actual; - } + public UserCurriculum(User u, Curriculum cu, int year, boolean actual){ + this.user = u; + this.curriculum = cu; + this.year = year; + this.actual = actual; + } } diff --git a/frontend/public/i18n/EN.txt b/frontend/public/i18n/EN.txt index fcdd3b9..0d7fa4a 100644 --- a/frontend/public/i18n/EN.txt +++ b/frontend/public/i18n/EN.txt @@ -60,6 +60,9 @@ Curriculum=curriculum Credits=Credits InscriptionService=I.S. faculty=Faculty +forum.create=Create forum +forum.create.name=New forum's name +forum.post.create.name=New post's title firstname/name=Firstname/Name regNo=regNo From=From diff --git a/frontend/public/i18n/FR.txt b/frontend/public/i18n/FR.txt index a7e4349..dd4e591 100644 --- a/frontend/public/i18n/FR.txt +++ b/frontend/public/i18n/FR.txt @@ -60,6 +60,9 @@ Curriculum=Cursus Credits=Credits InscriptionService=S.I. faculty=Faculté +forum.create=Créer un forum +forum.create.name=Nom du forum +forum.post.create.name=Titre du post firstname/name=Prénom/Nom regNo=Matricule From=De @@ -140,4 +143,4 @@ reregsup=M'inscrire dans un cursus supplémentaire chcur=Changer d'un cursus vers un autre iwouldlike=Je voudrais : newcurr=Nouveau cursus -cursusprereq=Le cursus que vous avez selectionné a des prérequis assurez vous que votre dossier de parcours est a jour dans votre profil \ No newline at end of file +cursusprereq=Le cursus que vous avez selectionné a des prérequis assurez vous que votre dossier de parcours est a jour dans votre profil diff --git a/frontend/src/Apps/Forums.vue b/frontend/src/Apps/Forums.vue new file mode 100644 index 0000000..d2004c5 --- /dev/null +++ b/frontend/src/Apps/Forums.vue @@ -0,0 +1,215 @@ + + + + + + + + diff --git a/frontend/src/rest/apps.js b/frontend/src/rest/apps.js index eb13476..a7b33a3 100644 --- a/frontend/src/rest/apps.js +++ b/frontend/src/rest/apps.js @@ -9,6 +9,7 @@ import Courses from "@/Apps/ManageCourses.vue" import Users from "@/Apps/UsersList.vue" import Students from "@/Apps/StudentsList.vue" import Msg from "@/Apps/Msg.vue" +import Forums from '@/Apps/Forums.vue' import Payments from "@/Apps/Inscription/PaymentInfo.vue"; import ManageRequests from "@/Apps/Inscription/ManageRequests.vue"; @@ -20,13 +21,14 @@ const apps = { '/users-list' : Users, '/students-list' : Students, '/msg' : Msg, + '/forums': Forums, '/payments': Payments } const appsList = { 'Msg': { path: '#/msg', icon: 'fa-comment', text: i18n("app.messages") }, 'Notification': { path: '#/notifs', icon: 'fa-bell', text: i18n("app.notifications") }, - 'Forum': { path: '#/forum', icon: 'fa-envelope', text: i18n("app.forum") }, + 'Forum': { path: '#/forums', icon: 'fa-envelope', text: i18n("app.forum") }, 'Schedule': { path: '#/schedule', icon: 'fa-calendar-days', text: i18n("app.schedules") }, 'Requests': { path: '#/requests', icon: 'fa-users', text: "Requests" }, 'ManageCourses': { path: '#/manage-courses', icon: 'fa-book', text: i18n("app.manage.courses") }, diff --git a/frontend/src/rest/courses.js b/frontend/src/rest/courses.js index e482e6b..92677e9 100644 --- a/frontend/src/rest/courses.js +++ b/frontend/src/rest/courses.js @@ -76,6 +76,6 @@ export async function alterCourse(id, changes){ * Return a list containing all the actual courses of a user */ -export async function getUserActualCourses(userId){ - return restGet("/usercourses/"+userId) -} \ No newline at end of file +export async function getUserActualCourses(){ + return restGet("/usercourses") +} diff --git a/frontend/src/rest/forum.js b/frontend/src/rest/forum.js new file mode 100644 index 0000000..637245f --- /dev/null +++ b/frontend/src/rest/forum.js @@ -0,0 +1,50 @@ +/******************************************************* + * File: forum.js + * Author: Anthony Debucquoy + * Scope: Extension messagerie + * Description: Forum related functions and calls + *******************************************************/ + +import { ref } from 'vue' +import { restGet, restPost, restDelete, restPatch } from './restConsumer.js' + +/** + * List forums of a course + */ +export async function getForumsOfCourse(id){ + ForumsOfCurrentCourse.value = await restGet("/forums/" + id) +} + +export const ForumsOfCurrentCourse = ref(); + +export function createForum(id, name){ + restPost("/forums/" + id, {name: name}).then(_ => getForumsOfCourse(id)); +} + +/** + * List post of a specified forum + */ +export async function getPostsOfForum(id){ + if(id != null){ + PostsOfCurrentForum.value = await restGet("/forum/" + id); + } +} + +export function createPost(id, subject, content){ + restPost("/forum/" + id, {subject: subject, content: content}).then(_ => getPostsOfForum(id)); +} + +export const PostsOfCurrentForum = ref(); + +/** + * Get a post and its responses + */ +export async function fetchPost(id){ + fetchedPost.value = await restGet("/forum/post/" + id); +} + +export function sendAnswer(id, content){ + restPost("/forum/post/" + id, {content: content}).then(_ => fetchPost(id)) +} + +export const fetchedPost = ref();