refactor pour intégrer l'architecture 3-tiers

This commit is contained in:
2025-12-02 23:21:44 +01:00
parent f0e20be396
commit 6a3569029d
41 changed files with 530 additions and 226 deletions

View File

@@ -1,49 +1,46 @@
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.dto.CommentaireDTO;
import local.epul4a.fotosharing.dto.PartageDTO;
import local.epul4a.fotosharing.dto.PhotoDTO;
import local.epul4a.fotosharing.service.CommentaireService;
import local.epul4a.fotosharing.service.PartageService;
import local.epul4a.fotosharing.service.PhotoService;
import local.epul4a.fotosharing.service.UtilisateurService;
import org.springframework.core.io.Resource;
import org.springframework.core.io.PathResource;
import org.springframework.data.domain.Page;
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;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
@Controller
public class PhotoController {
private final PhotoService photoService;
private final PhotoRepository photoRepository;
private final CommentaireService commentaireService;
private final UtilisateurRepository utilisateurRepository;
private final PartageRepository partageRepository;
private final PartageService partageService;
private final UtilisateurService utilisateurService;
public PhotoController(PhotoService photoService, PhotoRepository photoRepository, CommentaireService commentaireService, UtilisateurRepository utilisateurRepository, PartageRepository partageRepository) {
public PhotoController(
PhotoService photoService,
CommentaireService commentaireService,
PartageService partageService,
UtilisateurService utilisateurService
) {
this.photoService = photoService;
this.photoRepository = photoRepository;
this.commentaireService = commentaireService;
this.utilisateurRepository = utilisateurRepository;
this.partageRepository = partageRepository;
this.partageService = partageService;
this.utilisateurService = utilisateurService;
}
/* ========================== UPLOAD ========================== */
@GetMapping("/upload")
public String uploadForm() {
return "upload";
@@ -52,147 +49,142 @@ public class PhotoController {
@PostMapping("/upload")
public String doUpload(@RequestParam("file") MultipartFile file,
@RequestParam(value = "visibilite", defaultValue = "PRIVATE") String visibilite,
Authentication authentication,
Authentication auth,
Model model) {
try {
String email = authentication.getName(); // l'email de l'utilisateur connecté
Photo p = photoService.store(file, visibilite, email);
model.addAttribute("message", "Upload OK : " + p.getId());
return "redirect:/";
} catch (Exception e) {
model.addAttribute("error", e.getMessage());
photoService.store(file, visibilite, auth.getName());
return "redirect:/mes-photos";
} catch (Exception ex) {
model.addAttribute("error", ex.getMessage());
return "upload";
}
}
/* ========================== RAW IMAGE ========================== */
@GetMapping("/photo/{id}/raw")
public ResponseEntity<Resource> rawPhoto(@PathVariable("id") Long id) {
Photo photo = photoRepository.findById(id).orElse(null);
if (photo == null) {
public ResponseEntity<Resource> rawPhoto(@PathVariable Long id) {
PhotoDTO photo = photoService.getPhotoById(id);
if (photo == null)
return ResponseEntity.notFound().build();
}
Path p = photoService.loadAsPath(photo.getUuidFichier());
Resource r = new PathResource(p);
if (!r.exists()) {
Resource r = photoService.loadAsResource(photo.getUuidFichier());
if (!r.exists())
return ResponseEntity.notFound().build();
}
String contentType = "application/octet-stream";
try {
contentType = Files.probeContentType(p);
} catch (Exception ignored) {
}
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION,
"inline; filename=\"" + photo.getNomFichierOriginal() + "\"")
.contentType(MediaType.IMAGE_JPEG) // simplifiable, ou détecté dans service
.body(r);
}
/* ========================== MES PHOTOS ========================== */
@GetMapping("/mes-photos")
public String mesPhotos(
@RequestParam(name = "pagePrivees", defaultValue = "0") int pagePrivees,
@RequestParam(name = "pagePubliques", defaultValue = "0") int pagePubliques,
@RequestParam(name = "pagePartagees", defaultValue = "0") int pagePartagees,
@RequestParam(name = "pageMesPartagees", defaultValue = "0") int pageMesPartagees,
Model model,
Authentication auth
) {
String email = auth.getName();
// Chaque liste utilise sa propre pagination
model.addAttribute("photosPrivees", photoService.listPrivatePhotos(email, pagePrivees, 12));
model.addAttribute("photosPubliques", photoService.listPublicPhotos(email, pagePubliques, 12));
model.addAttribute("photosPartagees", photoService.listSharedWith(email, pagePartagees, 12));
model.addAttribute("mesPhotosPartagees", photoService.listSharedPhotos(email, pageMesPartagees, 12));
@RequestParam(defaultValue = "0") int pagePrivees,
@RequestParam(defaultValue = "0") int pagePubliques,
@RequestParam(defaultValue = "0") int pagePartagees,
@RequestParam(defaultValue = "0") int pageMesPartagees,
Authentication auth, Model model) {
String email = auth.getName();
model.addAttribute("photosPrivees",
photoService.listPrivatePhotos(email, pagePrivees, 12));
model.addAttribute("photosPubliques",
photoService.listPublicPhotos(email, pagePubliques, 12));
model.addAttribute("photosPartagees",
photoService.listSharedWith(email, pagePartagees, 12));
model.addAttribute("mesPhotosPartagees",
photoService.listSharedPhotos(email, pageMesPartagees, 12));
// Ajouter les 3 index séparés
model.addAttribute("pagePrivees", pagePrivees);
model.addAttribute("pagePubliques", pagePubliques);
model.addAttribute("pagePartagees", pagePartagees);
model.addAttribute("pageMesPartagees", pageMesPartagees);
return "mes-photos";
}
/* ========================== GALERIE ========================== */
@GetMapping("/galerie")
public String galerie(@RequestParam(defaultValue = "0") int page, Model model) {
Page<Photo> photosPage = photoService.listPublic(page, 12);
Page<PhotoDTO> photosPage = photoService.listPublic(page, 12);
model.addAttribute("photosPage", photosPage);
model.addAttribute("currentPage", page);
return "galerie";
}
/* ========================== DETAIL PHOTO ========================== */
@GetMapping("/photo/{id}")
@PreAuthorize("@securityService.canAccessPhoto(authentication, #id)")
public String viewPhoto(@PathVariable Long id,
@RequestParam(defaultValue = "0") int page,
Model model,
Authentication auth) {
Photo photo = photoRepository.findById(id).orElse(null);
if (photo == null) {
Authentication auth,
Model model) {
PhotoDTO photo = photoService.getPhotoById(id);
if (photo == null)
return "redirect:/galerie";
}
model.addAttribute("photo", photo);
// Pagination des commentaires
model.addAttribute("commentairesPage",
commentaireService.listByPhoto(id, page, 10));
Page<CommentaireDTO> commentaires =
commentaireService.listByPhoto(id, page, 10);
model.addAttribute("commentairesPage", commentaires);
model.addAttribute("currentPage", page);
// utilisateur connecté (peut être null)
String currentUser = (auth != null ? auth.getName() : null);
model.addAttribute("currentUser", currentUser);
// Liste des partages
List<Partage> partages = partageRepository.findByPhoto_Id(id);
model.addAttribute("partages", partages);
model.addAttribute("partages",
partageService.getPartagesForPhoto(id));
return "photo-detail";
}
/* ========================== COMMENTAIRES ========================== */
@PostMapping("/photo/{id}/comment")
public String addComment(@PathVariable Long id,
@RequestParam String contenu,
Authentication auth) {
if (auth == null) {
return "redirect:/login";
}
String email = auth.getName();
commentaireService.addComment(id, email, contenu);
commentaireService.addComment(id, auth.getName(), contenu);
return "redirect:/photo/" + id;
}
/* ========================== PARTAGE PHOTO ========================== */
@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);
public String sharePhoto(@PathVariable Long id,
@RequestParam String email,
@RequestParam String permission,
Authentication auth) {
partageService.share(id, email, permission, auth.getName());
return "redirect:/photo/" + id + "?shared=ok";
}
@GetMapping("/photo/{id}/unshare/{email}")
public String unshare(@PathVariable Long id, @PathVariable String email) {
photoService.unshare(id, email);
partageService.unshare(id, email);
return "redirect:/photo/" + id;
}
}

View File

@@ -0,0 +1,15 @@
package local.epul4a.fotosharing.dto;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Getter
@Setter
public class CommentaireDTO {
private Long id;
private String contenu;
private LocalDateTime dateCommentaire;
private UtilisateurDTO auteur;
}

View File

@@ -0,0 +1,13 @@
package local.epul4a.fotosharing.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class PartageDTO {
private Long id;
private UtilisateurDTO utilisateur;
private String permission; // READ / COMMENT / ADMIN
private PhotoDTO photo;
}

View File

@@ -0,0 +1,19 @@
package local.epul4a.fotosharing.dto;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Getter
@Setter
public class PhotoDTO {
private Long id;
private String nomFichierOriginal;
private String uuidFichier;
private LocalDateTime dateUpload;
private String visibilite;
private UtilisateurDTO proprietaire;
}

View File

@@ -0,0 +1,14 @@
package local.epul4a.fotosharing.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class UtilisateurDTO {
private Long id;
private String email;
private String nom;
private String prenom;
}

View File

@@ -0,0 +1,18 @@
package local.epul4a.fotosharing.mapper;
import local.epul4a.fotosharing.dto.CommentaireDTO;
import local.epul4a.fotosharing.model.Commentaire;
public class CommentaireMapper {
public static CommentaireDTO toDTO(Commentaire c) {
if (c == null) return null;
CommentaireDTO dto = new CommentaireDTO();
dto.setId(c.getId());
dto.setContenu(c.getContenu());
dto.setDateCommentaire(c.getDateCommentaire());
// Auteur via mapper dédié
dto.setAuteur(UtilisateurMapper.toDTO(c.getAuteur()));
return dto;
}
}

View File

@@ -0,0 +1,20 @@
package local.epul4a.fotosharing.mapper;
import local.epul4a.fotosharing.dto.PartageDTO;
import local.epul4a.fotosharing.model.Partage;
public class PartageMapper {
public static PartageDTO toDTO(Partage p) {
if (p == null) return null;
PartageDTO dto = new PartageDTO();
dto.setId(p.getId());
dto.setUtilisateur(UtilisateurMapper.toDTO(p.getUtilisateur()));
dto.setPhoto(PhotoMapper.toDTO(p.getPhoto()));
dto.setPermission(p.getPermission().name());
return dto;
}
}

View File

@@ -0,0 +1,28 @@
package local.epul4a.fotosharing.mapper;
import local.epul4a.fotosharing.dto.PhotoDTO;
import local.epul4a.fotosharing.dto.UtilisateurDTO;
import local.epul4a.fotosharing.model.Photo;
import local.epul4a.fotosharing.model.Utilisateur;
public class PhotoMapper {
public static PhotoDTO toDTO(Photo p) {
if (p == null) return null;
PhotoDTO dto = new PhotoDTO();
dto.setId(p.getId());
dto.setNomFichierOriginal(p.getNomFichierOriginal());
dto.setUuidFichier(p.getUuidFichier());
dto.setDateUpload(p.getDateUpload());
dto.setVisibilite(p.getVisibilite().name());
Utilisateur u = p.getProprietaire();
if (u != null) {
UtilisateurDTO uDTO = new UtilisateurDTO();
uDTO.setId(u.getId());
uDTO.setEmail(u.getEmail());
uDTO.setNom(u.getNom());
uDTO.setPrenom(u.getPrenom());
dto.setProprietaire(uDTO);
}
return dto;
}
}

View File

@@ -0,0 +1,17 @@
package local.epul4a.fotosharing.mapper;
import local.epul4a.fotosharing.dto.UtilisateurDTO;
import local.epul4a.fotosharing.model.Utilisateur;
public class UtilisateurMapper {
public static UtilisateurDTO toDTO(Utilisateur u) {
if (u == null) return null;
UtilisateurDTO dto = new UtilisateurDTO();
dto.setId(u.getId());
dto.setEmail(u.getEmail());
dto.setNom(u.getNom());
dto.setPrenom(u.getPrenom());
return dto;
}
}

View File

@@ -8,6 +8,11 @@ import lombok.Setter;
@Getter
@Setter
public class Partage {
public enum Permission {
READ,
COMMENT,
ADMIN
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -21,5 +26,7 @@ public class Partage {
@JoinColumn(name = "id_utilisateur")
private Utilisateur utilisateur;
// Getters & Setters
@Enumerated(EnumType.STRING)
private Permission permission = Permission.READ;
}

View File

@@ -1,5 +1,6 @@
package local.epul4a.fotosharing.service;
import local.epul4a.fotosharing.dto.CommentaireDTO;
import local.epul4a.fotosharing.model.Commentaire;
import org.springframework.data.domain.Page;
@@ -8,6 +9,6 @@ import java.util.List;
public interface CommentaireService {
List<Commentaire> listByPhoto(Long photoId);
void addComment(Long photoId, String email, String contenu);
Page<Commentaire> listByPhoto(Long photoId, int page, int size);
Page<CommentaireDTO> listByPhoto(Long photoId, int page, int size);
}

View File

@@ -0,0 +1,10 @@
package local.epul4a.fotosharing.service;
import local.epul4a.fotosharing.dto.PartageDTO;
import java.util.List;
public interface PartageService {
void share(Long photoId, String targetEmail, String permission, String ownerEmail);
void unshare(Long photoId, String targetEmail);
List<PartageDTO> getPartagesForPhoto(Long photoId);
}

View File

@@ -1,6 +1,8 @@
package local.epul4a.fotosharing.service;
import local.epul4a.fotosharing.dto.PhotoDTO;
import local.epul4a.fotosharing.model.Photo;
import org.springframework.core.io.Resource;
import org.springframework.data.domain.Page;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@@ -8,19 +10,14 @@ import java.nio.file.Path;
import java.util.List;
public interface PhotoService {
Photo store(MultipartFile file, String visibilite, String ownerEmail) throws IOException;
Path loadAsPath(String uuidFile);
List<Photo> listByOwner(String email);
List<Photo> listPublicPhotos();
List<Photo> listSharedWith(String email);
List<Photo> listPrivatePhotos(String email);
List<Photo> listPublicPhotos(String email);
void unshare(Long photoId, String email);
Page<Photo> listPublic(int page, int size);
Page<Photo> listPrivatePhotos(String email, int page, int size);
Page<Photo> listPublicPhotos(String email, int page, int size);
Page<Photo> listSharedWith(String email, int page, int size);
Page<Photo> listSharedPhotos(String email, int page, int size);
PhotoDTO store(MultipartFile file, String visibilite, String ownerEmail);
PhotoDTO getPhotoById(Long id);
Resource loadAsResource(String uuidFichier);
Page<PhotoDTO> listPublic(int page, int size);
Page<PhotoDTO> listPrivatePhotos(String email, int page, int size);
Page<PhotoDTO> listPublicPhotos(String email, int page, int size);
Page<PhotoDTO> listSharedWith(String email, int page, int size);
Page<PhotoDTO> listSharedPhotos(String email, int page, int size);

View File

@@ -0,0 +1,8 @@
package local.epul4a.fotosharing.service;
import local.epul4a.fotosharing.dto.UtilisateurDTO;
public interface UtilisateurService {
UtilisateurDTO findByEmail(String email);
boolean existsByEmail(String email);
}

View File

@@ -1,5 +1,7 @@
package local.epul4a.fotosharing.service.impl;
import local.epul4a.fotosharing.dto.CommentaireDTO;
import local.epul4a.fotosharing.mapper.CommentaireMapper;
import local.epul4a.fotosharing.model.Commentaire;
import local.epul4a.fotosharing.model.Photo;
import local.epul4a.fotosharing.model.Utilisateur;
@@ -51,9 +53,14 @@ public class CommentaireServiceImpl implements CommentaireService {
commentaireRepository.save(c);
}
@Override
public Page<Commentaire> listByPhoto(Long photoId, int page, int size) {
public Page<CommentaireDTO> listByPhoto(Long photoId, int page, int size) {
Pageable pageable = PageRequest.of(page, size);
return commentaireRepository.findByPhoto_Id(photoId, pageable);
Page<Commentaire> commentaires =
commentaireRepository.findByPhoto_Id(photoId, pageable);
return commentaires.map(CommentaireMapper::toDTO);
}
}

View File

@@ -0,0 +1,78 @@
package local.epul4a.fotosharing.service.impl;
import local.epul4a.fotosharing.dto.PartageDTO;
import local.epul4a.fotosharing.mapper.PartageMapper;
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.PartageService;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class PartageServiceImpl implements PartageService {
private final PartageRepository partageRepository;
private final PhotoRepository photoRepository;
private final UtilisateurRepository utilisateurRepository;
public PartageServiceImpl(
PartageRepository partageRepository,
PhotoRepository photoRepository,
UtilisateurRepository utilisateurRepository
) {
this.partageRepository = partageRepository;
this.photoRepository = photoRepository;
this.utilisateurRepository = utilisateurRepository;
}
@Override
public List<PartageDTO> getPartagesForPhoto(Long photoId) {
return partageRepository.findByPhoto_Id(photoId)
.stream()
.map(PartageMapper::toDTO)
.toList();
}
@Override
public void share(Long photoId, String targetEmail, String permissionStr, String ownerEmail) {
Photo photo = photoRepository.findById(photoId)
.orElseThrow(() -> new RuntimeException("Photo introuvable"));
if (!photo.getProprietaire().getEmail().equals(ownerEmail))
throw new RuntimeException("Vous n'êtes pas propriétaire");
Utilisateur target = utilisateurRepository.findByEmail(targetEmail)
.orElseThrow(() -> new RuntimeException("Utilisateur introuvable"));
if (partageRepository.existsByPhoto_IdAndUtilisateur_Email(photoId, targetEmail))
return;
Partage.Permission permission = Partage.Permission.valueOf(permissionStr);
Partage partage = new Partage();
partage.setPhoto(photo);
partage.setUtilisateur(target);
partage.setPermission(permission);
partageRepository.save(partage);
}
@Override
public void unshare(Long photoId, String targetEmail) {
Partage partage = partageRepository.findByPhoto_Id(photoId)
.stream()
.filter(p -> p.getUtilisateur().getEmail().equals(targetEmail))
.findFirst()
.orElse(null);
if (partage != null)
partageRepository.delete(partage);
}
}

View File

@@ -1,5 +1,7 @@
package local.epul4a.fotosharing.service.impl;
import local.epul4a.fotosharing.dto.PhotoDTO;
import local.epul4a.fotosharing.mapper.PhotoMapper;
import local.epul4a.fotosharing.model.Partage;
import local.epul4a.fotosharing.model.Photo;
import local.epul4a.fotosharing.model.Utilisateur;
@@ -8,15 +10,15 @@ import local.epul4a.fotosharing.repository.PhotoRepository;
import local.epul4a.fotosharing.repository.UtilisateurRepository;
import local.epul4a.fotosharing.service.PhotoService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.data.domain.*;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.util.List;
@@ -27,134 +29,130 @@ public class PhotoServiceImpl implements PhotoService {
@Value("${file.upload-dir}")
private String uploadDir;
private final PhotoRepository photoRepository;
private final UtilisateurRepository utilisateurRepository;
private final PartageRepository partageRepository;
public PhotoServiceImpl(PhotoRepository photoRepository, UtilisateurRepository utilisateurRepository, PartageRepository partageRepository) {
public PhotoServiceImpl(
PhotoRepository photoRepository,
UtilisateurRepository utilisateurRepository,
PartageRepository partageRepository) {
this.photoRepository = photoRepository;
this.utilisateurRepository = utilisateurRepository;
this.partageRepository = partageRepository;
}
//============= STORE PHOTO =============================
@Override
public Photo store(MultipartFile file, String visibilite, String ownerEmail) throws IOException {
if (file.isEmpty()) throw new IOException("Fichier vide");
// Vérifier taille / type si besoin (ici basique)
String original = StringUtils.cleanPath(file.getOriginalFilename());
String uuid = UUID.randomUUID().toString() + "-" + original;
Path uploadPath = Paths.get(uploadDir);
if (!Files.exists(uploadPath)) Files.createDirectories(uploadPath);
Path target = uploadPath.resolve(uuid);
Files.copy(file.getInputStream(), target, StandardCopyOption.REPLACE_EXISTING);
Photo p = new Photo();
p.setNomFichierOriginal(original);
p.setUuidFichier(uuid);
p.setDateUpload(LocalDateTime.now());
p.setVisibilite(Photo.Visibilite.valueOf(visibilite));
Utilisateur u = utilisateurRepository.findByEmail(ownerEmail).orElseThrow(() -> new IOException("Utilisateur introuvable"));
p.setProprietaire(u);
return photoRepository.save(p);
public PhotoDTO store(MultipartFile file, String visibilite, String ownerEmail) {
try {
if (file.isEmpty()) throw new IOException("Fichier vide");
String original = StringUtils.cleanPath(file.getOriginalFilename());
String uuid = UUID.randomUUID() + "-" + original;
Path uploadPath = Paths.get(uploadDir);
if (!Files.exists(uploadPath))
Files.createDirectories(uploadPath);
Files.copy(file.getInputStream(),
uploadPath.resolve(uuid),
StandardCopyOption.REPLACE_EXISTING);
Utilisateur owner = utilisateurRepository.findByEmail(ownerEmail)
.orElseThrow(() -> new RuntimeException("Utilisateur introuvable"));
Photo p = new Photo();
p.setNomFichierOriginal(original);
p.setUuidFichier(uuid);
p.setVisibilite(Photo.Visibilite.valueOf(visibilite));
p.setDateUpload(LocalDateTime.now());
p.setProprietaire(owner);
return PhotoMapper.toDTO(photoRepository.save(p));
} catch (Exception ex) {
throw new RuntimeException("Erreur upload : " + ex.getMessage(), ex);
}
}
@Override
public Path loadAsPath(String uuidFile) {
return Paths.get(uploadDir).resolve(uuidFile);
}
@Override
public List<Photo> listByOwner(String email) {
return photoRepository.findByProprietaire_Email(email);
}
//============= GET PHOTO BY ID ==========================
@Override
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();
}
@Override
public List<Photo> listPrivatePhotos(String email) {
return photoRepository.findByProprietaire_Email(email).stream()
.filter(p -> p.getVisibilite() == Photo.Visibilite.PRIVATE)
.toList();
}
@Override
public List<Photo> listPublicPhotos(String email) {
return photoRepository.findByProprietaire_Email(email).stream()
.filter(p -> p.getVisibilite() == Photo.Visibilite.PUBLIC)
.toList();
}
@Override
public void unshare(Long photoId, String email) {
Partage p = partageRepository.findByPhoto_Id(photoId).stream()
.filter(x -> x.getUtilisateur().getEmail().equals(email))
.findFirst()
public PhotoDTO getPhotoById(Long id) {
return photoRepository.findById(id)
.map(PhotoMapper::toDTO)
.orElse(null);
if (p != null) partageRepository.delete(p);
}
//============= LOAD RESOURCE ============================
@Override
public Page<Photo> listPublic(int page, int size) {
public Resource loadAsResource(String uuidFichier) {
try {
Path file = Paths.get(uploadDir).resolve(uuidFichier);
Resource resource = new UrlResource(file.toUri());
if (!resource.exists() || !resource.isReadable())
throw new RuntimeException("Impossible de lire le fichier : " + uuidFichier);
return resource;
} catch (MalformedURLException e) {
throw new RuntimeException("Fichier introuvable : " + uuidFichier);
}
}
//============= LIST PUBLIC PHOTOS =======================
@Override
public Page<PhotoDTO> listPublic(int page, int size) {
Pageable pageable = PageRequest.of(page, size);
return photoRepository.findByVisibilite(Photo.Visibilite.PUBLIC, pageable);
return photoRepository
.findByVisibilite(Photo.Visibilite.PUBLIC, pageable)
.map(PhotoMapper::toDTO);
}
//============= PRIVATE PHOTOS OF USER ===================
@Override
public Page<Photo> listPrivatePhotos(String email, int page, int size) {
public Page<PhotoDTO> listPrivatePhotos(String email, int page, int size) {
Pageable pageable = PageRequest.of(page, size);
return photoRepository.findByProprietaire_EmailAndVisibilite(
email,
Photo.Visibilite.PRIVATE,
pageable
);
return photoRepository
.findByProprietaire_EmailAndVisibilite(email, Photo.Visibilite.PRIVATE, pageable)
.map(PhotoMapper::toDTO);
}
//============= PUBLIC PHOTOS OF USER ====================
@Override
public Page<Photo> listPublicPhotos(String email, int page, int size) {
public Page<PhotoDTO> listPublicPhotos(String email, int page, int size) {
Pageable pageable = PageRequest.of(page, size);
return photoRepository.findByProprietaire_EmailAndVisibilite(
email,
Photo.Visibilite.PUBLIC,
pageable
);
return photoRepository
.findByProprietaire_EmailAndVisibilite(email, Photo.Visibilite.PUBLIC, pageable)
.map(PhotoMapper::toDTO);
}
//============= PHOTOS SHARED WITH USER ==================
@Override
public Page<Photo> listSharedWith(String email, int page, int size) {
List<Partage> partages = partageRepository.findByUtilisateur_Email(email);
List<Photo> photos = partages.stream().map(Partage::getPhoto).toList();
// convertir list en page manuellement
public Page<PhotoDTO> listSharedWith(String email, int page, int size) {
List<PhotoDTO> photos =
partageRepository.findByUtilisateur_Email(email)
.stream()
.map(Partage::getPhoto)
.map(PhotoMapper::toDTO)
.toList();
int start = page * size;
int end = Math.min(start + size, photos.size());
List<Photo> sublist = photos.subList(start, end);
return new PageImpl<>(sublist, PageRequest.of(page, size), photos.size());
List<PhotoDTO> sub = photos.subList(start, end);
return new PageImpl<>(sub, PageRequest.of(page, size), photos.size());
}
//============= PHOTOS SET AS SHARED BY USER =============
@Override
public Page<Photo> listSharedPhotos(String email, int page, int size) {
public Page<PhotoDTO> listSharedPhotos(String email, int page, int size) {
Pageable pageable = PageRequest.of(page, size);
return photoRepository.findByProprietaire_EmailAndVisibilite(
email,
Photo.Visibilite.SHARED,
pageable
);
return photoRepository
.findByProprietaire_EmailAndVisibilite(email, Photo.Visibilite.SHARED, pageable)
.map(PhotoMapper::toDTO);
}
}

View File

@@ -0,0 +1,30 @@
package local.epul4a.fotosharing.service.impl;
import local.epul4a.fotosharing.dto.UtilisateurDTO;
import local.epul4a.fotosharing.mapper.UtilisateurMapper;
import local.epul4a.fotosharing.model.Utilisateur;
import local.epul4a.fotosharing.repository.UtilisateurRepository;
import local.epul4a.fotosharing.service.UtilisateurService;
import org.springframework.stereotype.Service;
@Service
public class UtilisateurServiceImpl implements UtilisateurService {
private final UtilisateurRepository utilisateurRepository;
public UtilisateurServiceImpl(UtilisateurRepository utilisateurRepository) {
this.utilisateurRepository = utilisateurRepository;
}
@Override
public UtilisateurDTO findByEmail(String email) {
Utilisateur u = utilisateurRepository.findByEmail(email)
.orElse(null);
return UtilisateurMapper.toDTO(u);
}
@Override
public boolean existsByEmail(String email) {
return utilisateurRepository.findByEmail(email).isPresent();
}
}

View File

@@ -8,9 +8,6 @@
<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(mesPhotos)}">
<p>Vous n'avez pas encore de photos.</p>
</div>
<h2>Mes photos privées</h2>
<div>
<a th:each="p : ${photosPrivees.content}"
@@ -52,10 +49,21 @@
</div>
<div>
<a th:if="${mesPhotosPartagees.hasPrevious()}"
th:href="@{/mes-photos(pageMesPartagees=${pageMesPartagees - 1})}"></a>
th:href="@{/mes-photos(
pagePrivees=${pagePrivees},
pagePubliques=${pagePubliques},
pagePartagees=${pagePartagees},
pageMesPartagees=${pageMesPartagees - 1}
)}"></a>
<span th:text="'Page ' + (${pageMesPartagees}+1)"></span>
<a th:if="${mesPhotosPartagees.hasNext()}"
th:href="@{/mes-photos(pageMesPartagees=${pageMesPartagees + 1})}"></a>
th:href="@{/mes-photos(
pagePrivees=${pagePrivees},
pagePubliques=${pagePubliques},
pagePartagees=${pagePartagees},
pageMesPartagees=${pageMesPartagees + 1}
)}"></a>
</div>

View File

@@ -34,6 +34,8 @@
<ul>
<li th:each="p : ${partages}">
<span th:text="${p.utilisateur.email}"></span>
<span th:text="${p.permission}"></span>
<a th:href="@{'/photo/' + ${photo.id} + '/unshare/' + ${p.utilisateur.email}}"
style="color:red;">Retirer</a>
</li>
@@ -43,6 +45,12 @@
<form th:action="@{'/photo/' + ${photo.id} + '/share'}" method="post">
<label>Email de l'utilisateur :</label>
<input type="email" name="email" required />
<label>Permission :</label>
<select name="permission">
<option value="READ">Lecture</option>
<option value="COMMENT">Commenter</option>
<option value="ADMIN">Administrer</option>
</select>
<button type="submit">Partager</button>
</form>
</div>

View File

@@ -8,9 +8,6 @@
<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(mesPhotos)}">
<p>Vous n'avez pas encore de photos.</p>
</div>
<h2>Mes photos privées</h2>
<div>
<a th:each="p : ${photosPrivees.content}"
@@ -52,10 +49,21 @@
</div>
<div>
<a th:if="${mesPhotosPartagees.hasPrevious()}"
th:href="@{/mes-photos(pageMesPartagees=${pageMesPartagees - 1})}"></a>
th:href="@{/mes-photos(
pagePrivees=${pagePrivees},
pagePubliques=${pagePubliques},
pagePartagees=${pagePartagees},
pageMesPartagees=${pageMesPartagees - 1}
)}"></a>
<span th:text="'Page ' + (${pageMesPartagees}+1)"></span>
<a th:if="${mesPhotosPartagees.hasNext()}"
th:href="@{/mes-photos(pageMesPartagees=${pageMesPartagees + 1})}"></a>
th:href="@{/mes-photos(
pagePrivees=${pagePrivees},
pagePubliques=${pagePubliques},
pagePartagees=${pagePartagees},
pageMesPartagees=${pageMesPartagees + 1}
)}"></a>
</div>

View File

@@ -34,6 +34,8 @@
<ul>
<li th:each="p : ${partages}">
<span th:text="${p.utilisateur.email}"></span>
<span th:text="${p.permission}"></span>
<a th:href="@{'/photo/' + ${photo.id} + '/unshare/' + ${p.utilisateur.email}}"
style="color:red;">Retirer</a>
</li>
@@ -43,6 +45,12 @@
<form th:action="@{'/photo/' + ${photo.id} + '/share'}" method="post">
<label>Email de l'utilisateur :</label>
<input type="email" name="email" required />
<label>Permission :</label>
<select name="permission">
<option value="READ">Lecture</option>
<option value="COMMENT">Commenter</option>
<option value="ADMIN">Administrer</option>
</select>
<button type="submit">Partager</button>
</form>
</div>