FEAT : ajout partage simple

This commit is contained in:
2025-12-01 22:30:22 +01:00
parent 4a35c375ec
commit 109cc8065e
14 changed files with 169 additions and 29 deletions

View File

@@ -1,8 +1,11 @@
package local.epul4a.fotosharing.controller;
import local.epul4a.fotosharing.model.Partage;
import local.epul4a.fotosharing.model.Photo;
import local.epul4a.fotosharing.model.Utilisateur;
import local.epul4a.fotosharing.repository.PartageRepository;
import local.epul4a.fotosharing.repository.PhotoRepository;
import local.epul4a.fotosharing.repository.UtilisateurRepository;
import local.epul4a.fotosharing.security.CustomUserDetails;
import local.epul4a.fotosharing.service.CommentaireService;
import local.epul4a.fotosharing.service.PhotoService;
@@ -11,6 +14,7 @@ import org.springframework.core.io.PathResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
@@ -20,6 +24,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
@Controller
public class PhotoController {
@@ -27,11 +32,15 @@ public class PhotoController {
private final PhotoService photoService;
private final PhotoRepository photoRepository;
private final CommentaireService commentaireService;
private final UtilisateurRepository utilisateurRepository;
private final PartageRepository partageRepository;
public PhotoController(PhotoService photoService, PhotoRepository photoRepository, CommentaireService commentaireService) {
public PhotoController(PhotoService photoService, PhotoRepository photoRepository, CommentaireService commentaireService, UtilisateurRepository utilisateurRepository, PartageRepository partageRepository) {
this.photoService = photoService;
this.photoRepository = photoRepository;
this.commentaireService = commentaireService;
this.utilisateurRepository = utilisateurRepository;
this.partageRepository = partageRepository;
}
@GetMapping("/upload")
@@ -84,7 +93,13 @@ public class PhotoController {
@GetMapping("/mes-photos")
public String mesPhotos(Model model, Authentication authentication) {
String email = authentication.getName();
model.addAttribute("photos", photoService.listByOwner(email));
// photos que je possède
List<Photo> mesPhotos = photoService.listByOwner(email);
// photos partagées avec moi
List<Photo> photosPartagees = photoService.listSharedWith(email);
model.addAttribute("mesPhotos", mesPhotos);
model.addAttribute("photosPartagees", photosPartagees);
return "mes-photos";
}
@@ -95,6 +110,7 @@ public class PhotoController {
}
@GetMapping("/photo/{id}")
@PreAuthorize("@securityService.canAccessPhoto(authentication, #id)")
public String viewPhoto(@PathVariable Long id,
Model model,
Authentication auth) {
@@ -126,5 +142,30 @@ public class PhotoController {
return "redirect:/photo/" + id;
}
@PostMapping("/photo/{id}/share")
@PreAuthorize("@securityService.canAccessPhoto(authentication, #id)")
public String share(@PathVariable Long id,
@RequestParam String email,
Authentication auth) {
// Vérifier que c'est le propriétaire
Photo photo = photoRepository.findById(id).orElse(null);
if (photo == null) return "redirect:/galerie";
if (!photo.getProprietaire().getEmail().equals(auth.getName())) {
return "redirect:/photo/" + id + "?error=notowner";
}
// Trouver utilisateur
Utilisateur user = utilisateurRepository.findByEmail(email).orElse(null);
if (user == null) {
return "redirect:/photo/" + id + "?error=usernotfound";
}
// Ajouter partage
Partage p = new Partage();
p.setPhoto(photo);
p.setUtilisateur(user);
partageRepository.save(p);
return "redirect:/photo/" + id + "?shared=ok";
}
}

View File

@@ -0,0 +1,15 @@
package local.epul4a.fotosharing.repository;
import local.epul4a.fotosharing.model.Partage;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface PartageRepository extends JpaRepository<Partage, Long> {
// liste des partages pour une photo
List<Partage> findByPhoto_Id(Long photoId);
// vérifier si un utilisateur a accès partagé
boolean existsByPhoto_IdAndUtilisateur_Email(Long photoId, String email);
List<Partage> findByUtilisateur_Email(String email);
}

View File

@@ -1,29 +1,47 @@
package local.epul4a.fotosharing.security;
import local.epul4a.fotosharing.model.Photo;
import local.epul4a.fotosharing.repository.PartageRepository;
import local.epul4a.fotosharing.repository.PhotoRepository;
import org.springframework.stereotype.Service;
import javax.naming.ldap.PagedResultsControl;
import java.util.Optional;
import org.springframework.security.core.Authentication;
@Service("securityService")
public class SecurityService {
private final PhotoRepository photoRepository;
private final PartageRepository partageRepository;
public SecurityService(PhotoRepository photoRepository) {
public SecurityService(PhotoRepository photoRepository, PartageRepository partageRepository) {
this.photoRepository = photoRepository;
this.partageRepository = partageRepository;
}
public boolean canAccessPhoto(org.springframework.security.core.Authentication authentication, Long photoId) {
if (authentication == null || !authentication.isAuthenticated()) return false;
String username = authentication.getName(); // email
Optional<Photo> p = photoRepository.findById(photoId);
if (p.isEmpty()) return false;
Photo photo = p.get();
if (photo.getVisibilite() == Photo.Visibilite.PUBLIC) return true;
if (photo.getProprietaire() != null && photo.getProprietaire().getEmail().equals(username)) return true;
// TODO: vérifier table partage
public boolean canAccessPhoto(Authentication authentication, Long photoId) {
Photo photo = photoRepository.findById(photoId).orElse(null);
if (photo == null) return false;
// PUBLIC → accès total
if (photo.getVisibilite() == Photo.Visibilite.PUBLIC) {
return true;
}
// Pas connecté → rejeter tout sauf PUBLIC
if (authentication == null || !authentication.isAuthenticated()) {
return false;
}
String email = authentication.getName();
// Propriétaire → OK
if (photo.getProprietaire().getEmail().equals(email)) {
return true;
}
// SHARED → vérifier dans la table PARTAGE
if (photo.getVisibilite() == Photo.Visibilite.SHARED) {
return partageRepository.existsByPhoto_IdAndUtilisateur_Email(photoId, email);
}
// PRIVATE par défaut → refus
return false;
}
}

View File

@@ -11,5 +11,7 @@ public interface PhotoService {
Path loadAsPath(String uuidFile);
List<Photo> listByOwner(String email);
List<Photo> listPublicPhotos();
List<Photo> listSharedWith(String email);
}

View File

@@ -1,7 +1,9 @@
package local.epul4a.fotosharing.service.impl;
import local.epul4a.fotosharing.model.Partage;
import local.epul4a.fotosharing.model.Photo;
import local.epul4a.fotosharing.model.Utilisateur;
import local.epul4a.fotosharing.repository.PartageRepository;
import local.epul4a.fotosharing.repository.PhotoRepository;
import local.epul4a.fotosharing.repository.UtilisateurRepository;
import local.epul4a.fotosharing.service.PhotoService;
@@ -24,10 +26,12 @@ public class PhotoServiceImpl implements PhotoService {
private final PhotoRepository photoRepository;
private final UtilisateurRepository utilisateurRepository;
private final PartageRepository partageRepository;
public PhotoServiceImpl(PhotoRepository photoRepository, UtilisateurRepository utilisateurRepository) {
public PhotoServiceImpl(PhotoRepository photoRepository, UtilisateurRepository utilisateurRepository, PartageRepository partageRepository) {
this.photoRepository = photoRepository;
this.utilisateurRepository = utilisateurRepository;
this.partageRepository = partageRepository;
}
@Override
@@ -69,4 +73,14 @@ public class PhotoServiceImpl implements PhotoService {
public List<Photo> listPublicPhotos() {
return photoRepository.findByVisibilite(Photo.Visibilite.PUBLIC);
}
@Override
public List<Photo> listSharedWith(String email) {
List<Partage> partages = partageRepository.findByUtilisateur_Email(email);
return partages.stream()
.map(Partage::getPhoto)
.toList();
}
}

View File

@@ -5,23 +5,34 @@
<title>Mes photos</title>
</head>
<body>
<h1>Mes photos</h1>
<h2>Mes photos</h2>
<p><a th:href="@{/upload}">Uploader une photo</a></p>
<p><a th:href="@{/}">Retour accueil</a></p>
<div th:if="${#lists.isEmpty(photos)}">
<div th:if="${#lists.isEmpty(mesPhotos)}">
<p>Vous n'avez pas encore de photos.</p>
</div>
<ul th:if="${!#lists.isEmpty(photos)}">
<li th:each="p : ${photos}">
<span th:text="${p.nomFichierOriginal}">Nom du fichier</span>
<ul>
<li th:each="p : ${mesPhotos}">
<span th:text="${p.nomFichierOriginal}"></span>
<a th:href="@{'/photo/' + ${p.id}}">Voir</a>
</li>
</ul>
<h2>Photos partagées avec moi</h2>
<div th:if="${#lists.isEmpty(photosPartagees)}">
<p>Aucune photo partagée.</p>
</div>
<ul th:if="${!#lists.isEmpty(photosPartagees)}">
<li th:each="p : ${photosPartagees}">
<span th:text="${p.nomFichierOriginal}"></span>
<span style="color: green; font-weight: bold;">[SHARED]</span>
<a th:href="@{'/photo/' + ${p.id}}">Voir</a>
</li>
</ul>
<p><a th:href="@{/galerie}">Galerie publique</a></p>
</body>

View File

@@ -29,6 +29,20 @@
<li><strong>Propriétaire :</strong> <span th:text="${photo.proprietaire.email}"></span></li>
</ul>
<!-- Partage de la photo -->
<h2>Partager la photo</h2>
<div th:if="${currentUser == photo.proprietaire.email}">
<form th:action="@{'/photo/' + ${photo.id} + '/share'}" method="post">
<label>Email de l'utilisateur :</label>
<input type="email" name="email" required />
<button type="submit">Partager</button>
</form>
</div>
<div th:if="${currentUser != photo.proprietaire.email}">
<em>Seul le propriétaire peut partager cette photo.</em>
</div>
<!-- Commentaires -->
<h2>Commentaires</h2>
<div th:if="${#lists.isEmpty(commentaires)}">

View File

@@ -5,23 +5,34 @@
<title>Mes photos</title>
</head>
<body>
<h1>Mes photos</h1>
<h2>Mes photos</h2>
<p><a th:href="@{/upload}">Uploader une photo</a></p>
<p><a th:href="@{/}">Retour accueil</a></p>
<div th:if="${#lists.isEmpty(photos)}">
<div th:if="${#lists.isEmpty(mesPhotos)}">
<p>Vous n'avez pas encore de photos.</p>
</div>
<ul th:if="${!#lists.isEmpty(photos)}">
<li th:each="p : ${photos}">
<span th:text="${p.nomFichierOriginal}">Nom du fichier</span>
<ul>
<li th:each="p : ${mesPhotos}">
<span th:text="${p.nomFichierOriginal}"></span>
<a th:href="@{'/photo/' + ${p.id}}">Voir</a>
</li>
</ul>
<h2>Photos partagées avec moi</h2>
<div th:if="${#lists.isEmpty(photosPartagees)}">
<p>Aucune photo partagée.</p>
</div>
<ul th:if="${!#lists.isEmpty(photosPartagees)}">
<li th:each="p : ${photosPartagees}">
<span th:text="${p.nomFichierOriginal}"></span>
<span style="color: green; font-weight: bold;">[SHARED]</span>
<a th:href="@{'/photo/' + ${p.id}}">Voir</a>
</li>
</ul>
<p><a th:href="@{/galerie}">Galerie publique</a></p>
</body>

View File

@@ -29,6 +29,20 @@
<li><strong>Propriétaire :</strong> <span th:text="${photo.proprietaire.email}"></span></li>
</ul>
<!-- Partage de la photo -->
<h2>Partager la photo</h2>
<div th:if="${currentUser == photo.proprietaire.email}">
<form th:action="@{'/photo/' + ${photo.id} + '/share'}" method="post">
<label>Email de l'utilisateur :</label>
<input type="email" name="email" required />
<button type="submit">Partager</button>
</form>
</div>
<div th:if="${currentUser != photo.proprietaire.email}">
<em>Seul le propriétaire peut partager cette photo.</em>
</div>
<!-- Commentaires -->
<h2>Commentaires</h2>
<div th:if="${#lists.isEmpty(commentaires)}">