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 index a0c74f5..d14265d 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/EndPoints/Msg/ForumController.java +++ b/backend/src/main/java/ovh/herisson/Clyde/EndPoints/Msg/ForumController.java @@ -80,7 +80,7 @@ public class ForumController { 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)){ + if(!(f.getWriters().contains(u) || f.getCourse().getOwner().equals(u) || u.getRole() == Role.Admin)){ return new UnauthorizedResponse<>(null); } forumServ.createTopic(f, data); diff --git a/backend/src/main/java/ovh/herisson/Clyde/EndPoints/NotificationController.java b/backend/src/main/java/ovh/herisson/Clyde/EndPoints/NotificationController.java new file mode 100644 index 0000000..106b48a --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/EndPoints/NotificationController.java @@ -0,0 +1,61 @@ +package ovh.herisson.Clyde.EndPoints; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.springframework.http.HttpStatus; +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.RequestHeader; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import lombok.AllArgsConstructor; +import ovh.herisson.Clyde.Repositories.NotificationRepository; +import ovh.herisson.Clyde.Responses.UnauthorizedResponse; +import ovh.herisson.Clyde.Services.AuthenticatorService; +import ovh.herisson.Clyde.Tables.Notification; +import ovh.herisson.Clyde.Tables.User; +import ovh.herisson.Clyde.Tables.Notification.Status; + +@RestController +@AllArgsConstructor +@CrossOrigin(originPatterns = "*", allowCredentials = "true") +public class NotificationController { + + private AuthenticatorService authServ; + private NotificationRepository notifRepo; + + @GetMapping("/notifications") + public ResponseEntity> getNotifications(@RequestHeader("Authorization") String token){ + User u = authServ.getUserFromToken(token); + if(u == null){ + return new UnauthorizedResponse<>(null); + } + ArrayList ret = new ArrayList<>(); + for (Notification n : u.getNotifications()) { + if(!n.getStatus().equals(Status.Archived)){ + ret.add(n); + } + } + return new ResponseEntity<>(ret, HttpStatus.OK); + + } + + @PostMapping("/notifications/{id}") + public ResponseEntity archiveNotification(@RequestHeader("Authorization") String token, @PathVariable long id){ + User u = authServ.getUserFromToken(token); + Notification n = notifRepo.findById(id).orElse(null); + if(u == null || n.getUser() != u){ + return new UnauthorizedResponse<>(null); + } + + n.setStatus(Status.Archived); + notifRepo.save(n); + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/backend/src/main/java/ovh/herisson/Clyde/Repositories/NotificationRepository.java b/backend/src/main/java/ovh/herisson/Clyde/Repositories/NotificationRepository.java new file mode 100644 index 0000000..a9f9546 --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/Repositories/NotificationRepository.java @@ -0,0 +1,8 @@ +package ovh.herisson.Clyde.Repositories; + +import org.springframework.data.repository.CrudRepository; + +import ovh.herisson.Clyde.Tables.Notification; + +public interface NotificationRepository extends CrudRepository {} + diff --git a/backend/src/main/java/ovh/herisson/Clyde/Services/Msg/DiscussionService.java b/backend/src/main/java/ovh/herisson/Clyde/Services/Msg/DiscussionService.java index 9a471da..93703ef 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Services/Msg/DiscussionService.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Services/Msg/DiscussionService.java @@ -17,6 +17,8 @@ import org.springframework.stereotype.Service; import com.fasterxml.jackson.databind.util.JSONPObject; import ovh.herisson.Clyde.Repositories.Msg.DiscussionRepository; +import ovh.herisson.Clyde.Services.UserService; +import ovh.herisson.Clyde.Tables.Notification; import ovh.herisson.Clyde.Tables.User; import ovh.herisson.Clyde.Tables.Msg.Discussion; import ovh.herisson.Clyde.Tables.Msg.Message; @@ -26,6 +28,8 @@ public class DiscussionService { @Autowired private DiscussionRepository discRepo; + @Autowired + private UserService userServ; public Discussion create(String name, User author){ return discRepo.save(new Discussion(name, author)); @@ -42,6 +46,9 @@ public class DiscussionService { * Create a message and link it to it's discussion */ public Discussion CreateMessage(Discussion disc, Message msg){ + for(User u: disc.getMembers()){ + userServ.Notify(u, new Notification("msg.notification.new", msg.getContent(), "/#/msg")); + } disc.addMessage(msg); return discRepo.save(disc); } diff --git a/backend/src/main/java/ovh/herisson/Clyde/Services/NotificationService.java b/backend/src/main/java/ovh/herisson/Clyde/Services/NotificationService.java new file mode 100644 index 0000000..db0f02e --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/Services/NotificationService.java @@ -0,0 +1,9 @@ +package ovh.herisson.Clyde.Services; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class NotificationService { + +} diff --git a/backend/src/main/java/ovh/herisson/Clyde/Services/UserService.java b/backend/src/main/java/ovh/herisson/Clyde/Services/UserService.java index 9c6bd7f..638c0c9 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Services/UserService.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Services/UserService.java @@ -4,6 +4,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import ovh.herisson.Clyde.Tables.RegNoGenerator; import ovh.herisson.Clyde.Repositories.UserRepository; +import ovh.herisson.Clyde.Tables.Notification; import ovh.herisson.Clyde.Tables.Role; import ovh.herisson.Clyde.Tables.User; import java.util.*; @@ -139,4 +140,10 @@ public class UserService { public void delete(User user) { userRepo.delete(user); } + + public void Notify(User u, Notification n){ + n.setUser(u); + u.getNotifications().add(n); + userRepo.save(u); + } } 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 369a8bc..1093052 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Tables/Course.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/Course.java @@ -28,10 +28,11 @@ public class Course { private User owner; //// Extension Messagerie ///// - @OneToMany(cascade = CascadeType.ALL) + @OneToMany(mappedBy = "course", cascade = CascadeType.ALL) private List forums; public void addForum(Forum f){ + f.setCourse(this); forums.add(f); } /////////////////////////////// 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 index 75508aa..e03d0de 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Tables/Msg/Forum.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/Msg/Forum.java @@ -2,6 +2,8 @@ package ovh.herisson.Clyde.Tables.Msg; import java.util.List; +import com.fasterxml.jackson.annotation.JsonIgnore; + import jakarta.persistence.*; import lombok.Data; import ovh.herisson.Clyde.Tables.Course; @@ -16,6 +18,7 @@ public class Forum { private int id; @ManyToOne + @JsonIgnore private Course course; private String name; diff --git a/backend/src/main/java/ovh/herisson/Clyde/Tables/Notification.java b/backend/src/main/java/ovh/herisson/Clyde/Tables/Notification.java new file mode 100644 index 0000000..1ccddaf --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/Notification.java @@ -0,0 +1,53 @@ +package ovh.herisson.Clyde.Tables; + +import java.util.Date; + +import org.hibernate.annotations.CreationTimestamp; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import jakarta.annotation.Nullable; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@Entity +public class Notification { + + public enum Status { + Unread, + Read, + Archived + } + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private int id; + + private String subject; + + private String body; + + private Status status = Status.Unread; + + private String link; + + @ManyToOne + @JsonIgnore + private User user; + + @CreationTimestamp + private Date creation; + + public Notification(String subject, @Nullable String body, @Nullable String link){ + this.subject = subject; + this.body = body; + this.link = link; + } +} 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 08184c9..6b724de 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Tables/User.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/User.java @@ -2,18 +2,24 @@ package ovh.herisson.Clyde.Tables; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; +import lombok.Data; +import lombok.NoArgsConstructor; import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; import org.hibernate.annotations.GenericGenerator; import ovh.herisson.Clyde.Tables.Msg.Discussion; import ovh.herisson.Clyde.Tables.Msg.Message; +import ovh.herisson.Clyde.Tables.Notification.Status; import java.util.Date; import java.util.List; import com.fasterxml.jackson.annotation.JsonIgnore; + @Entity @Table(name = "Users") +@NoArgsConstructor +@Data public class User { @Id @GenericGenerator(name = "userGen", type = ovh.herisson.Clyde.Tables.RegNoGenerator.class) @@ -32,13 +38,19 @@ public class User { @JsonIgnore private String password; + @JsonIgnore + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) + private List notifications; + ////// Extension Messagerie ///// + @JsonIgnore @OneToMany(mappedBy = "author", cascade = CascadeType.ALL) private List msgs; - ///////////////////////////////// + @JsonIgnore @ManyToMany( mappedBy = "members" ) private List discussions; + ///////////////////////////////// public User(String lastName, String firstName, String email, String address, String country, Date birthDate, String profilePictureUrl, Role role, String password) @@ -70,84 +82,4 @@ public class User { this.role = Role.Student; this.identityCardUrl = identityCardUrl; } - public User() {} - - public Long getRegNo(){ - return this.regNo; - } - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - - public String getCountry() { - return country; - } - - public void setCountry(String country) { - this.country = country; - } - - public Date getBirthDate() { - return birthDate; - } - - public void setBirthDate(Date birthDate) { - this.birthDate = birthDate; - } - - public String getProfilePictureUrl(){return this.profilePictureUrl;} - - public void setProfilePictureUrl(String profilePictureUrl){ - this.profilePictureUrl = profilePictureUrl; - } - public ovh.herisson.Clyde.Tables.Role getRole() { - return role; - } - - public void setRole(ovh.herisson.Clyde.Tables.Role role) { - this.role = role; - } - public String getPassword(){ - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public void setIdentityCardUrl(String identityCardUrl) { - this.identityCardUrl = identityCardUrl; - } - - public String getIdentityCardUrl() { - return identityCardUrl; - } } diff --git a/frontend/public/i18n/EN.txt b/frontend/public/i18n/EN.txt index 6598207..34665e6 100644 --- a/frontend/public/i18n/EN.txt +++ b/frontend/public/i18n/EN.txt @@ -120,6 +120,7 @@ Curriculum=curriculum Credits=Credits InscriptionService=I.S. faculty=Faculty +msg.notification.new=You have a new message forum.create=Create forum forum.create.name=New forum's name forum.post.create.name=New post's title diff --git a/frontend/public/i18n/FR.txt b/frontend/public/i18n/FR.txt index a09c338..09d8e13 100644 --- a/frontend/public/i18n/FR.txt +++ b/frontend/public/i18n/FR.txt @@ -120,6 +120,7 @@ Curriculum=Cursus Credits=Credits InscriptionService=S.I. faculty=Faculté +msg.notification.new=Vous avez un nouveau message! forum.create=Créer un forum forum.create.name=Nom du forum forum.post.create.name=Titre du post diff --git a/frontend/src/App.vue b/frontend/src/App.vue index daf1064..45bdeb2 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -3,6 +3,7 @@ import { ref } from 'vue' import i18n, { setLang } from './i18n.js' import { isLogged } from '@/rest/Users.js' + import { notifications, fetchNotifications, archiveNotification } from '@/rest/notifications.js' import { appList, currentView } from '@/rest/apps.js' var prevURL; @@ -14,16 +15,20 @@ window.onhashchange = function() { } const Logged = ref(isLogged()); +if(Logged.value){ + fetchNotifications(); +} + window.addEventListener('hashchange', () => { if((location.hash === "#/home" && prevURL === "#/login") || (location.hash === "#/home" && prevURL === "#/profil")){ window.location.reload(); } }); const home=ref(i18n("app.home")) - const notifications=ref(i18n("app.notifications")) const settings=ref(i18n("app.settings")) const login=ref(i18n("app.login")) const active=ref(false) + const notification = ref(false) const apps = ref([]) appList().then(e => apps.value = e) @@ -44,14 +49,17 @@ window.addEventListener('hashchange', () => {
  • -
    +
  • -
  • - -
    +
  • + +
    +
      +
    • {{ i18n(notif.subject) }} - {{ notif.body }}
    • +
  • - +