FEAT : Ajout du mécanisme d'album avec partage et visibilité

This commit is contained in:
2025-12-03 12:46:02 +01:00
parent 66045aaf14
commit ff088361a0
43 changed files with 1080 additions and 56 deletions

View File

@@ -0,0 +1,169 @@
package local.epul4a.fotosharing.controller;
import local.epul4a.fotosharing.dto.AlbumDTO;
import local.epul4a.fotosharing.dto.PhotoDTO;
import local.epul4a.fotosharing.service.AlbumService;
import local.epul4a.fotosharing.service.PartageService;
import local.epul4a.fotosharing.service.PhotoService;
import org.springframework.data.domain.Page;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Controller
public class AlbumController {
private final AlbumService albumService;
private final PhotoService photoService;
private final PartageService partageService;
public AlbumController(AlbumService albumService,PhotoService photoService,PartageService partageService) {
this.albumService = albumService;
this.photoService = photoService;
this.partageService = partageService;
}
/* ============================ MES ALBUMS ============================ */
@GetMapping("/mes-albums")
public String mesAlbums(
@RequestParam(defaultValue = "0") int page,
Authentication auth,
Model model
) {
String email = auth.getName();
Page<AlbumDTO> albums = albumService.listMyAlbums(email, page, 10);
List<AlbumDTO> sharedAlbums = albumService.listAlbumsSharedWithMe(email);
model.addAttribute("albums", albums);
model.addAttribute("currentPage", page);
model.addAttribute("sharedAlbums", sharedAlbums);
return "mes-albums";
}
/* ============================ CREATION ALBUM ============================ */
@PostMapping("/albums/create")
public String createAlbum(
@RequestParam String nom,
@RequestParam(required = false) String description,
@RequestParam(defaultValue = "PRIVATE") String visibilite,
Authentication auth
) {
albumService.createAlbum(nom, description, visibilite, auth.getName());
return "redirect:/mes-albums";
}
/* ============================ DETAIL ALBUM ============================ */
@GetMapping("/album/{id}")
public String viewAlbum(
@PathVariable Long id,
Authentication auth,
Model model
) {
String email = auth.getName();
AlbumDTO album = albumService.getAlbum(id);
if (album == null)
return "redirect:/mes-albums";
model.addAttribute("album", album);
model.addAttribute("myPhotos", photoService.listAllPhotosOfUser(email));
model.addAttribute("partages", partageService.getAlbumPartages(id));
boolean canAdmin = partageService.canAdminAlbum(id, email);
model.addAttribute("canAdmin", canAdmin);
return "album-detail";
}
/* ============================ AJOUT PHOTO ============================ */
@PostMapping("/album/{id}/add")
public String addPhoto(
@PathVariable Long id,
@RequestParam Long photoId,
Authentication auth
) {
try {
albumService.addPhoto(id, photoId, auth.getName());
return "redirect:/album/" + id + "?added=ok";
} catch (RuntimeException ex) {
return "redirect:/album/" + id + "?error=" + URLEncoder.encode(ex.getMessage(), StandardCharsets.UTF_8);
}
}
/* ============================ RETIRER PHOTO ============================ */
@GetMapping("/album/{id}/remove/{photoId}")
public String removePhoto(
@PathVariable Long id,
@PathVariable Long photoId,
Authentication auth
) {
albumService.removePhoto(id, photoId, auth.getName());
return "redirect:/album/" + id;
}
/* ============================ SUPPRESSION ALBUM ============================ */
@GetMapping("/album/{id}/delete")
public String deleteAlbum(
@PathVariable Long id,
Authentication auth
) {
albumService.deleteAlbum(id, auth.getName());
return "redirect:/mes-albums";
}
/* ============================ PARTAGE ALBUM ============================ */
@PostMapping("/album/{id}/share")
public String shareAlbum(
@PathVariable Long id,
@RequestParam String email,
@RequestParam String permission,
Authentication auth
) {
try {
albumService.shareAlbum(id, email, permission, auth.getName());
return "redirect:/album/" + id + "?shared=ok";
} catch (Exception e) {
return "redirect:/album/" + id + "?error=" + e.getMessage();
}
}
/* ============================ SUPPRIMER PARTAGE ALBUM ============================ */
@GetMapping("/album/{id}/share/remove/{email}")
public String removeShareAlbum(
@PathVariable Long id,
@PathVariable String email,
Authentication auth
) {
albumService.removeAlbumShare(id, email, auth.getName());
return "redirect:/album/" + id + "?removed=ok";
}
/* ============================ MAJ PARTAGE ALBUM ============================ */
@PostMapping("/album/{id}/share/update")
public String updateAlbumShare(
@PathVariable Long id,
@RequestParam String email,
@RequestParam String permission,
Authentication auth
) {
albumService.updateAlbumPermission(id, email, permission, auth.getName());
return "redirect:/album/" + id;
}
/* ============================ SUPPRIMER PARTAGE ALBUM ============================ */
@GetMapping("/album/{id}/unshare/{email}")
public String unshareAlbum(
@PathVariable Long id,
@PathVariable String email,
Authentication auth
) {
albumService.removeAlbumShare(id, email, auth.getName());
return "redirect:/album/" + id;
}
}

View File

@@ -0,0 +1,20 @@
package local.epul4a.fotosharing.dto;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
import java.util.List;
@Getter
@Setter
public class AlbumDTO {
private Long id;
private String nom;
private String description;
private String visibilite;
private LocalDateTime dateCreation;
private UtilisateurDTO proprietaire;
// Liste des photos pas les IDs pour éviter les boucles
private List<PhotoDTO> photos;
}

View File

@@ -10,4 +10,8 @@ public class PartageDTO {
private UtilisateurDTO utilisateur; private UtilisateurDTO utilisateur;
private String permission; // READ / COMMENT / ADMIN private String permission; // READ / COMMENT / ADMIN
private PhotoDTO photo; private PhotoDTO photo;
public String getUtilisateurEmail() {
return utilisateur != null ? utilisateur.getEmail() : null;
}
} }

View File

@@ -0,0 +1,27 @@
package local.epul4a.fotosharing.mapper;
import local.epul4a.fotosharing.dto.AlbumDTO;
import local.epul4a.fotosharing.dto.PhotoDTO;
import local.epul4a.fotosharing.model.Album;
import java.util.stream.Collectors;
public class AlbumMapper {
public static AlbumDTO toDTO(Album album) {
if (album == null) return null;
AlbumDTO dto = new AlbumDTO();
dto.setId(album.getId());
dto.setNom(album.getNom());
dto.setDescription(album.getDescription());
dto.setVisibilite(album.getVisibilite().name());
dto.setDateCreation(album.getDateCreation());
dto.setProprietaire(UtilisateurMapper.toDTO(album.getProprietaire()));
dto.setPhotos(
album.getPhotos()
.stream()
.map(PhotoMapper::toDTO)
.collect(Collectors.toList())
);
return dto;
}
}

View File

@@ -0,0 +1,52 @@
package local.epul4a.fotosharing.model;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;
@Entity
@Getter
@Setter
public class Album {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String nom;
@Column(length = 1000)
private String description;
@ManyToOne(optional = false)
private Utilisateur proprietaire;
private LocalDateTime dateCreation;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Visibilite visibilite;
@ManyToMany
@JoinTable(
name = "album_photo",
joinColumns = @JoinColumn(name = "album_id"),
inverseJoinColumns = @JoinColumn(name = "photo_id")
)
private Set<Photo> photos = new HashSet<>();
@OneToMany(mappedBy = "album", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Partage> partages = new HashSet<>();
public enum Visibilite {
PRIVATE,
SHARED,
PUBLIC
}
}

View File

@@ -8,25 +8,25 @@ import lombok.Setter;
@Getter @Getter
@Setter @Setter
public class Partage { public class Partage {
public enum Permission {
READ,
COMMENT,
ADMIN
}
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; private Long id;
@ManyToOne @ManyToOne
@JoinColumn(name = "id_photo")
private Photo photo;
@ManyToOne
@JoinColumn(name = "id_utilisateur")
private Utilisateur utilisateur; private Utilisateur utilisateur;
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private Permission permission = Permission.READ; private Permission permission;
@ManyToOne
private Photo photo; // nullable si partage dalbum
@ManyToOne
private Album album; // nullable si partage de photo
public enum Permission {
READ, COMMENT, ADMIN
} }
}

View File

@@ -5,7 +5,8 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.HashSet;
import java.util.Set;
@Entity @Entity
@Getter @Getter
@@ -28,5 +29,9 @@ public class Photo {
@JoinColumn(name = "id_utilisateur") @JoinColumn(name = "id_utilisateur")
private Utilisateur proprietaire; private Utilisateur proprietaire;
// getters & setters @ManyToMany(mappedBy = "photos")
private Set<Album> albums = new HashSet<>();
@OneToMany(mappedBy = "photo", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Partage> partages = new HashSet<>();
} }

View File

@@ -0,0 +1,18 @@
package local.epul4a.fotosharing.repository;
import local.epul4a.fotosharing.model.Album;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface AlbumRepository extends JpaRepository<Album, Long> {
// Albums dont je suis propriétaire
List<Album> findByProprietaire_Email(String email);
Page<Album> findByProprietaire_Email(String email, Pageable pageable);
// Albums publics
Page<Album> findByVisibilite(Album.Visibilite visibilite, Pageable pageable);
List<Album> findByPartages_Utilisateur_Email(String email);
}

View File

@@ -7,10 +7,16 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
public interface PartageRepository extends JpaRepository<Partage, Long> { public interface PartageRepository extends JpaRepository<Partage, Long> {
// PHOTO
List<Partage> findByPhoto_Id(Long photoId); List<Partage> findByPhoto_Id(Long photoId);
boolean existsByPhoto_IdAndUtilisateur_Email(Long photoId, String email); boolean existsByPhoto_IdAndUtilisateur_Email(Long photoId, String email);
Partage findByPhoto_IdAndUtilisateur_Email(Long photoId, String email);
// ALBUM
List<Partage> findByAlbum_Id(Long albumId);
boolean existsByAlbum_IdAndUtilisateur_Email(Long albumId, String email);
Partage findByAlbum_IdAndUtilisateur_Email(Long albumId, String email);
List<Partage> findByUtilisateur_Email(String email); List<Partage> findByUtilisateur_Email(String email);
Optional<Partage> findByPhoto_IdAndUtilisateur_Email(Long photoId, String email);
int countByPhoto_Id(Long photoId); int countByPhoto_Id(Long photoId);
} }

View File

@@ -0,0 +1,24 @@
package local.epul4a.fotosharing.service;
import local.epul4a.fotosharing.dto.AlbumDTO;
import local.epul4a.fotosharing.dto.PartageDTO;
import org.springframework.data.domain.Page;
import java.util.List;
public interface AlbumService {
AlbumDTO createAlbum(String nom, String description, String visibilite, String ownerEmail);
Page<AlbumDTO> listMyAlbums(String email, int page, int size);
Page<AlbumDTO> listPublicAlbums(int page, int size);
AlbumDTO getAlbum(Long id);
void addPhoto(Long albumId, Long photoId, String ownerEmail);
void removePhoto(Long albumId, Long photoId, String ownerEmail);
void deleteAlbum(Long albumId, String ownerEmail);
void shareAlbum(Long albumId, String targetEmail, String permission, String ownerEmail);
void updateAlbumPermission(Long albumId, String targetEmail, String permission, String ownerEmail);
void removeAlbumShare(Long albumId, String targetEmail, String ownerEmail);
List<PartageDTO> getAlbumPartages(Long albumId);
List<AlbumDTO> listAlbumsSharedWithMe(String email);
}

View File

@@ -15,5 +15,13 @@ public interface PartageService {
boolean canAdmin(Long photoId, String email); boolean canAdmin(Long photoId, String email);
void updatePermission(Long photoId, String targetEmail, String newPermission, String ownerEmail); void updatePermission(Long photoId, String targetEmail, String newPermission, String ownerEmail);
int countShares(Long photoId); int countShares(Long photoId);
void shareAlbum(Long albumId, String targetEmail, String permission, String ownerEmail);
void updateAlbumPermission(Long albumId, String targetEmail, String newPermission, String ownerEmail);
boolean canViewAlbum(Long albumId, String email);
boolean canCommentAlbum(Long albumId, String email);
boolean canAdminAlbum(Long albumId, String email);
List<PartageDTO> getAlbumPartages(Long albumId);
void removeAlbumShare(Long albumId, String targetEmail, String ownerEmail);
} }

View File

@@ -18,6 +18,8 @@ public interface PhotoService {
Page<PhotoDTO> listPublicPhotos(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> listSharedWith(String email, int page, int size);
Page<PhotoDTO> listSharedPhotos(String email, int page, int size); Page<PhotoDTO> listSharedPhotos(String email, int page, int size);
List<PhotoDTO> listAllPhotosOfUser(String email);

View File

@@ -0,0 +1,209 @@
package local.epul4a.fotosharing.service.impl;
import local.epul4a.fotosharing.dto.AlbumDTO;
import local.epul4a.fotosharing.mapper.AlbumMapper;
import local.epul4a.fotosharing.model.Album;
import local.epul4a.fotosharing.model.Partage;
import local.epul4a.fotosharing.model.Photo;
import local.epul4a.fotosharing.model.Utilisateur;
import local.epul4a.fotosharing.repository.AlbumRepository;
import local.epul4a.fotosharing.repository.PartageRepository;
import local.epul4a.fotosharing.repository.PhotoRepository;
import local.epul4a.fotosharing.repository.UtilisateurRepository;
import local.epul4a.fotosharing.service.AlbumService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import local.epul4a.fotosharing.service.PartageService;
import local.epul4a.fotosharing.dto.PartageDTO;
import java.util.List;
import java.time.LocalDateTime;
@Service
public class AlbumServiceImpl implements AlbumService {
private final AlbumRepository albumRepository;
private final UtilisateurRepository utilisateurRepository;
private final PhotoRepository photoRepository;
private final PartageService partageService;
private final PartageRepository partageRepository;
public AlbumServiceImpl(
AlbumRepository albumRepository,
UtilisateurRepository utilisateurRepository,
PhotoRepository photoRepository,
PartageService partageService,
PartageRepository partageRepository
) {
this.albumRepository = albumRepository;
this.utilisateurRepository = utilisateurRepository;
this.photoRepository = photoRepository;
this.partageService = partageService;
this.partageRepository = partageRepository;
}
@Override
public AlbumDTO createAlbum(String nom, String description, String visibilite, String ownerEmail) {
Utilisateur user = utilisateurRepository.findByEmail(ownerEmail)
.orElseThrow(() -> new RuntimeException("Utilisateur introuvable"));
Album album = new Album();
album.setNom(nom);
album.setDescription(description);
album.setVisibilite(Album.Visibilite.valueOf(visibilite));
album.setProprietaire(user);
album.setDateCreation(LocalDateTime.now());
return AlbumMapper.toDTO(albumRepository.save(album));
}
@Override
public Page<AlbumDTO> listMyAlbums(String email, int page, int size) {
return albumRepository.findByProprietaire_Email(email, PageRequest.of(page, size))
.map(AlbumMapper::toDTO);
}
@Override
public Page<AlbumDTO> listPublicAlbums(int page, int size) {
return albumRepository.findByVisibilite(Album.Visibilite.PUBLIC, PageRequest.of(page, size))
.map(AlbumMapper::toDTO);
}
@Override
public AlbumDTO getAlbum(Long id) {
return albumRepository.findById(id)
.map(AlbumMapper::toDTO)
.orElse(null);
}
@Override
public void addPhoto(Long albumId, Long photoId, String requesterEmail) {
Album album = albumRepository.findById(albumId)
.orElseThrow(() -> new RuntimeException("Album introuvable"));
//Propriétaire
boolean isOwner = album.getProprietaire().getEmail().equals(requesterEmail);
//admin du partage
boolean isAdmin = album.getPartages().stream()
.anyMatch(p ->
p.getUtilisateur().getEmail().equals(requesterEmail)
&& p.getPermission() == Partage.Permission.ADMIN
);
if (!isOwner && !isAdmin)
throw new RuntimeException("Vous navez pas les droits pour modifier cet album");
Photo photo = photoRepository.findById(photoId)
.orElseThrow(() -> new RuntimeException("Photo introuvable"));
//Protection anti-doublons
boolean exists = album.getPhotos()
.stream()
.anyMatch(p -> p.getId().equals(photoId));
if (exists)
throw new RuntimeException("Cette photo est déjà dans cet album");
album.getPhotos().add(photo);
albumRepository.save(album);
// Synchroniser les partages sur la photo pour les utilisateurs de l'album
album.getPartages().forEach(p -> {
// On vérifie si l'utilisateur n'a PAS déjà un partage sur cette photo
boolean existeDeja = photo.getPartages().stream()
.anyMatch(pp -> pp.getUtilisateur().getEmail().equals(p.getUtilisateur().getEmail()));
if (!existeDeja) {
// On crée un partage photo identique à l'autorisation de l'album
partageService.share(photo.getId(), p.getUtilisateur().getEmail(), p.getPermission().name(), requesterEmail);
}
});
// S'assurer que le propriétaire a aussi accès
boolean ownerHasAccess = photo.getPartages().stream()
.anyMatch(p -> p.getUtilisateur().getEmail().equals(album.getProprietaire().getEmail()));
if (!ownerHasAccess) {
partageService.share(photo.getId(), album.getProprietaire().getEmail(), "ADMIN", requesterEmail);
}
}
@Override
public void removePhoto(Long albumId, Long photoId, String ownerEmail) {
Album album = albumRepository.findById(albumId)
.orElseThrow(() -> new RuntimeException("Album introuvable"));
if (!album.getProprietaire().getEmail().equals(ownerEmail))
throw new RuntimeException("Vous n'êtes pas propriétaire de lalbum");
Photo photo = photoRepository.findById(photoId)
.orElseThrow(() -> new RuntimeException("Photo introuvable"));
album.getPhotos().remove(photo);
albumRepository.save(album);
}
@Override
public void deleteAlbum(Long albumId, String ownerEmail) {
Album album = albumRepository.findById(albumId)
.orElseThrow(() -> new RuntimeException("Album introuvable"));
if (!album.getProprietaire().getEmail().equals(ownerEmail))
throw new RuntimeException("Vous n'êtes pas propriétaire de lalbum");
albumRepository.delete(album);
}
@Override
public void shareAlbum(Long albumId, String targetEmail, String permission, String ownerEmail) {
partageService.shareAlbum(albumId, targetEmail, permission, ownerEmail);
}
@Override
public void updateAlbumPermission(Long albumId, String targetEmail, String permission, String ownerEmail) {
partageService.updateAlbumPermission(albumId, targetEmail, permission, ownerEmail);
// --- Synchroniser les permissions sur toutes les photos de lalbum ---
Album album = albumRepository.findById(albumId)
.orElseThrow(() -> new RuntimeException("Album introuvable"));
for (Photo photo : album.getPhotos()) {
// Chercher un partage photo existant
Partage partagePhoto = photo.getPartages().stream()
.filter(p -> p.getUtilisateur().getEmail().equals(targetEmail))
.findFirst()
.orElse(null);
if (partagePhoto != null) {
// Mettre à jour la permission
partagePhoto.setPermission(Partage.Permission.valueOf(permission));
partageRepository.save(partagePhoto);
} else {
// Si pas encore partagé → création
partageService.share(
photo.getId(),
targetEmail,
permission,
ownerEmail
);
}
}
}
@Override
public void removeAlbumShare(Long albumId, String targetEmail, String ownerEmail) {
partageService.removeAlbumShare(albumId, targetEmail, ownerEmail);
}
@Override
public List<PartageDTO> getAlbumPartages(Long albumId) {
return partageService.getAlbumPartages(albumId);
}
@Override
public List<AlbumDTO> listAlbumsSharedWithMe(String email) {
return albumRepository.findByPartages_Utilisateur_Email(email)
.stream()
.filter(a -> !a.getProprietaire().getEmail().equals(email))
.map(AlbumMapper::toDTO)
.toList();
}
}

View File

@@ -2,9 +2,11 @@ package local.epul4a.fotosharing.service.impl;
import local.epul4a.fotosharing.dto.PartageDTO; import local.epul4a.fotosharing.dto.PartageDTO;
import local.epul4a.fotosharing.mapper.PartageMapper; import local.epul4a.fotosharing.mapper.PartageMapper;
import local.epul4a.fotosharing.model.Album;
import local.epul4a.fotosharing.model.Partage; import local.epul4a.fotosharing.model.Partage;
import local.epul4a.fotosharing.model.Photo; import local.epul4a.fotosharing.model.Photo;
import local.epul4a.fotosharing.model.Utilisateur; import local.epul4a.fotosharing.model.Utilisateur;
import local.epul4a.fotosharing.repository.AlbumRepository;
import local.epul4a.fotosharing.repository.PartageRepository; import local.epul4a.fotosharing.repository.PartageRepository;
import local.epul4a.fotosharing.repository.PhotoRepository; import local.epul4a.fotosharing.repository.PhotoRepository;
import local.epul4a.fotosharing.repository.UtilisateurRepository; import local.epul4a.fotosharing.repository.UtilisateurRepository;
@@ -19,15 +21,18 @@ public class PartageServiceImpl implements PartageService {
private final PartageRepository partageRepository; private final PartageRepository partageRepository;
private final PhotoRepository photoRepository; private final PhotoRepository photoRepository;
private final UtilisateurRepository utilisateurRepository; private final UtilisateurRepository utilisateurRepository;
private final AlbumRepository albumRepository;
public PartageServiceImpl( public PartageServiceImpl(
PartageRepository partageRepository, PartageRepository partageRepository,
PhotoRepository photoRepository, PhotoRepository photoRepository,
UtilisateurRepository utilisateurRepository UtilisateurRepository utilisateurRepository,
AlbumRepository albumRepository
) { ) {
this.partageRepository = partageRepository; this.partageRepository = partageRepository;
this.photoRepository = photoRepository; this.photoRepository = photoRepository;
this.utilisateurRepository = utilisateurRepository; this.utilisateurRepository = utilisateurRepository;
this.albumRepository = albumRepository;
} }
@Override @Override
@@ -62,11 +67,7 @@ public class PartageServiceImpl implements PartageService {
@Override @Override
public void unshare(Long photoId, String targetEmail) { public void unshare(Long photoId, String targetEmail) {
Partage partage = partageRepository.findByPhoto_Id(photoId) Partage partage = partageRepository.findByPhoto_Id(photoId).stream().filter(p -> p.getUtilisateur().getEmail().equals(targetEmail)).findFirst().orElse(null);
.stream()
.filter(p -> p.getUtilisateur().getEmail().equals(targetEmail))
.findFirst()
.orElse(null);
if (partage != null) if (partage != null)
partageRepository.delete(partage); partageRepository.delete(partage);
@@ -82,9 +83,7 @@ public class PartageServiceImpl implements PartageService {
// Photo publique => tout le monde peut voir // Photo publique => tout le monde peut voir
if (photo.getVisibilite() == Photo.Visibilite.PUBLIC) if (photo.getVisibilite() == Photo.Visibilite.PUBLIC)
return true; return true;
Partage partage = partageRepository Partage partage = partageRepository.findByPhoto_IdAndUtilisateur_Email(photoId, email);
.findByPhoto_IdAndUtilisateur_Email(photoId, email)
.orElse(null);
return partage != null; // READ / COMMENT / ADMIN return partage != null; // READ / COMMENT / ADMIN
} }
@@ -95,9 +94,7 @@ public class PartageServiceImpl implements PartageService {
// propriétaire = admin total // propriétaire = admin total
if (photo.getProprietaire().getEmail().equals(email)) if (photo.getProprietaire().getEmail().equals(email))
return true; return true;
Partage partage = partageRepository Partage partage = partageRepository.findByPhoto_IdAndUtilisateur_Email(photoId, email);
.findByPhoto_IdAndUtilisateur_Email(photoId, email)
.orElse(null);
if (partage == null) return false; if (partage == null) return false;
return partage.getPermission() == Partage.Permission.COMMENT return partage.getPermission() == Partage.Permission.COMMENT
|| partage.getPermission() == Partage.Permission.ADMIN; || partage.getPermission() == Partage.Permission.ADMIN;
@@ -110,9 +107,7 @@ public class PartageServiceImpl implements PartageService {
// propriétaire = admin total // propriétaire = admin total
if (photo.getProprietaire().getEmail().equals(email)) if (photo.getProprietaire().getEmail().equals(email))
return true; return true;
Partage partage = partageRepository Partage partage = partageRepository.findByPhoto_IdAndUtilisateur_Email(photoId, email);
.findByPhoto_IdAndUtilisateur_Email(photoId, email)
.orElse(null);
if (partage == null) return false; if (partage == null) return false;
return partage.getPermission() == Partage.Permission.ADMIN; return partage.getPermission() == Partage.Permission.ADMIN;
} }
@@ -121,28 +116,154 @@ public class PartageServiceImpl implements PartageService {
public void updatePermission(Long photoId, String targetEmail, String newPermission, String requesterEmail) { public void updatePermission(Long photoId, String targetEmail, String newPermission, String requesterEmail) {
Photo photo = photoRepository.findById(photoId) Photo photo = photoRepository.findById(photoId)
.orElseThrow(() -> new RuntimeException("Photo introuvable")); .orElseThrow(() -> new RuntimeException("Photo introuvable"));
//Vérifier si requester = propriétaire // Si ce n'est pas le propriétaire → vérifier si ADMIN
if (!photo.getProprietaire().getEmail().equals(requesterEmail)) { if (!photo.getProprietaire().getEmail().equals(requesterEmail)) {
// Sinon, vérifier s'il a ADMIN Partage requesterPartage =
Partage requesterPartage = partageRepository partageRepository.findByPhoto_IdAndUtilisateur_Email(photoId, requesterEmail);
.findByPhoto_IdAndUtilisateur_Email(photoId, requesterEmail) if (requesterPartage == null ||
.orElse(null); requesterPartage.getPermission() != Partage.Permission.ADMIN) {
if (requesterPartage == null || requesterPartage.getPermission() != Partage.Permission.ADMIN) {
throw new RuntimeException("Vous navez pas les droits ADMIN pour modifier les permissions."); throw new RuntimeException("Vous navez pas les droits ADMIN pour modifier les permissions.");
} }
} }
// OK → modification des droits Partage partage =
Partage partage = partageRepository partageRepository.findByPhoto_IdAndUtilisateur_Email(photoId, targetEmail);
.findByPhoto_IdAndUtilisateur_Email(photoId, targetEmail) if (partage == null)
.orElseThrow(() -> new RuntimeException("Partage introuvable")); throw new RuntimeException("Partage introuvable");
Partage.Permission permission = Partage.Permission.valueOf(newPermission); partage.setPermission(Partage.Permission.valueOf(newPermission));
partage.setPermission(permission);
partageRepository.save(partage); partageRepository.save(partage);
} }
@Override @Override
public int countShares(Long photoId) { public int countShares(Long photoId) {
return partageRepository.countByPhoto_Id(photoId); return partageRepository.countByPhoto_Id(photoId);
} }
@Override
public void shareAlbum(Long albumId, String targetEmail, String permissionStr, String ownerEmail) {
Album album = albumRepository.findById(albumId)
.orElseThrow(() -> new RuntimeException("Album introuvable"));
if (!album.getProprietaire().getEmail().equals(ownerEmail))
throw new RuntimeException("Vous nêtes pas propriétaire de cet album");
Utilisateur target = utilisateurRepository.findByEmail(targetEmail)
.orElseThrow(() -> new RuntimeException("Utilisateur introuvable"));
if (partageRepository.existsByAlbum_IdAndUtilisateur_Email(albumId, targetEmail))
throw new RuntimeException("Cet utilisateur a déjà accès à cet album");
//Créer le partage ALBUM
Partage.Permission permission = Partage.Permission.valueOf(permissionStr);
Partage partage = new Partage();
partage.setAlbum(album);
partage.setUtilisateur(target);
partage.setPermission(permission);
partageRepository.save(partage);
//Créer le partage PHOTO pour toutes les photos existantes
for (Photo photo : album.getPhotos()) {
boolean exists = partageRepository.existsByPhoto_IdAndUtilisateur_Email(photo.getId(), targetEmail);
if (!exists) {
Partage p = new Partage();
p.setPhoto(photo);
p.setUtilisateur(target);
p.setPermission(permission);
partageRepository.save(p);
}
}
}
@Override
public void updateAlbumPermission(Long albumId, String targetEmail, String newPermission, String ownerEmail) {
Album album = albumRepository.findById(albumId)
.orElseThrow(() -> new RuntimeException("Album introuvable"));
if (!album.getProprietaire().getEmail().equals(ownerEmail))
throw new RuntimeException("Vous nêtes pas propriétaire");
Partage partageAlbum = partageRepository.findByAlbum_IdAndUtilisateur_Email(albumId, targetEmail);
if (partageAlbum == null)
throw new RuntimeException("Partage introuvable");
Partage.Permission perm = Partage.Permission.valueOf(newPermission);
//Mettre à jour permission sur ALBUM
partageAlbum.setPermission(perm);
partageRepository.save(partageAlbum);
//Mettre à jour permissions sur toutes les PHOTOS
for (Photo photo : album.getPhotos()) {
Partage partagePhoto = partageRepository.findByPhoto_IdAndUtilisateur_Email(photo.getId(), targetEmail);
if (partagePhoto != null) {
partagePhoto.setPermission(perm);
partageRepository.save(partagePhoto);
}
}
}
@Override
public boolean canViewAlbum(Long albumId, String email) {
Album album = albumRepository.findById(albumId).orElse(null);
if (album == null) return false;
if (album.getVisibilite() == Album.Visibilite.PUBLIC)
return true;
if (album.getProprietaire().getEmail().equals(email))
return true;
return partageRepository.existsByAlbum_IdAndUtilisateur_Email(albumId, email);
}
@Override
public List<PartageDTO> getAlbumPartages(Long albumId) {
return partageRepository.findByAlbum_Id(albumId)
.stream()
.map(PartageMapper::toDTO)
.toList();
}
@Override
public boolean canCommentAlbum(Long albumId, String email) {
Album album = albumRepository.findById(albumId).orElse(null);
if (album == null) return false;
//Le propriétaire : autorisé à tout
if (album.getProprietaire().getEmail().equals(email))
return true;
//Chercher un partage
Partage partage = partageRepository.findByAlbum_IdAndUtilisateur_Email(albumId, email);
if (partage == null) return false;
//COMMENT autorisé pour COMMENT et ADMIN
return partage.getPermission() == Partage.Permission.COMMENT
|| partage.getPermission() == Partage.Permission.ADMIN;
}
@Override
public boolean canAdminAlbum(Long albumId, String email) {
Album album = albumRepository.findById(albumId).orElse(null);
if (album == null) return false;
//Le propriétaire est toujours ADMIN
if (album.getProprietaire().getEmail().equals(email))
return true;
//hercher un partage
Partage partage = partageRepository.findByAlbum_IdAndUtilisateur_Email(albumId, email);
if (partage == null) return false;
//ADMIN uniquement si permission ADMIN
return partage.getPermission() == Partage.Permission.ADMIN;
}
@Override
public void removeAlbumShare(Long albumId, String targetEmail, String ownerEmail) {
Album album = albumRepository.findById(albumId)
.orElseThrow(() -> new RuntimeException("Album introuvable"));
// Vérification propriétaire
if (!album.getProprietaire().getEmail().equals(ownerEmail))
throw new RuntimeException("Vous nêtes pas propriétaire");
//Supprimer le partage ALBUM
Partage partageAlbum = partageRepository.findByAlbum_IdAndUtilisateur_Email(albumId, targetEmail);
if (partageAlbum != null) {
partageRepository.delete(partageAlbum);
}
//Supprimer les partages sur toutes les photos
for (Photo photo : album.getPhotos()) {
Partage partagePhoto = partageRepository.findByPhoto_IdAndUtilisateur_Email(photo.getId(), targetEmail);
if (partagePhoto != null) {
partageRepository.delete(partagePhoto);
}
}
}
} }

View File

@@ -134,11 +134,12 @@ public class PhotoServiceImpl implements PhotoService {
//============= PHOTOS SHARED WITH USER ================== //============= PHOTOS SHARED WITH USER ==================
@Override @Override
public Page<PhotoDTO> listSharedWith(String email, int page, int size) { public Page<PhotoDTO> listSharedWith(String email, int page, int size) {
List<PhotoDTO> photos = List<PhotoDTO> photos = partageRepository.findByUtilisateur_Email(email)
partageRepository.findByUtilisateur_Email(email)
.stream() .stream()
.map(Partage::getPhoto) .map(Partage::getPhoto)
.filter(photo -> photo != null)
.map(PhotoMapper::toDTO) .map(PhotoMapper::toDTO)
.filter(dto -> dto != null)
.toList(); .toList();
int start = page * size; int start = page * size;
int end = Math.min(start + size, photos.size()); int end = Math.min(start + size, photos.size());
@@ -147,6 +148,7 @@ public class PhotoServiceImpl implements PhotoService {
} }
//============= PHOTOS SET AS SHARED BY USER ============= //============= PHOTOS SET AS SHARED BY USER =============
@Override @Override
public Page<PhotoDTO> listSharedPhotos(String email, int page, int size) { public Page<PhotoDTO> listSharedPhotos(String email, int page, int size) {
@@ -155,4 +157,15 @@ public class PhotoServiceImpl implements PhotoService {
.findByProprietaire_EmailAndVisibilite(email, Photo.Visibilite.SHARED, pageable) .findByProprietaire_EmailAndVisibilite(email, Photo.Visibilite.SHARED, pageable)
.map(PhotoMapper::toDTO); .map(PhotoMapper::toDTO);
} }
//============= ALL PHOTOS OF USER =======================
@Override
public List<PhotoDTO> listAllPhotosOfUser(String email) {
return photoRepository.findByProprietaire_Email(email)
.stream()
.map(PhotoMapper::toDTO)
.toList();
}
} }

View File

@@ -0,0 +1,101 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<title>Détail album</title>
</head>
<body>
<a th:href="@{/mes-albums}">⬅ Retour aux albums</a>
<h1 th:text="${album.nom}"></h1>
<p><b>Description :</b> <span th:text="${album.description}"></span></p>
<p><b>Propriétaire :</b> <span th:text="${album.proprietaire.email}"></span></p>
<p><b>Visibilité :</b> <span th:text="${album.visibilite}"></span></p>
<hr/>
<!-- LISTE DES PHOTOS -->
<h2>Photos dans lalbum</h2>
<div th:each="p : ${album.photos}" style="display:inline-block; margin:10px;">
<a th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
</a>
<br/>
<a th:if="${canAdmin}" th:href="@{'/album/' + ${album.id} + '/remove/' + ${p.id}}"
style="color:red;">Retirer</a>
</div>
<!-- Bloc visible uniquement par le propriétaire ou ADMIN album -->
<div th:if="${canAdmin}">
<hr/>
<!-- AJOUT PHOTO -->
<h2>Ajouter une photo</h2>
<div th:if="${param.added}" style="color:green; font-weight:bold;">
Photo ajoutée !
</div>
<form th:action="@{'/album/' + ${album.id} + '/add'}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<select name="photoId">
<option th:each="p : ${myPhotos}"
th:value="${p.id}"
th:text="${p.nomFichierOriginal}">
</option>
</select>
<button type="submit">Ajouter</button>
</form>
<hr/>
<h3>Gestion des partages de lalbum</h3>
<div th:if="${param.error}" style="color:red; font-weight:bold;">
<span th:text="${param.error}"></span>
</div>
<!-- FORMULAIRE AJOUT PARTAGE -->
<form th:action="@{'/album/' + ${album.id} + '/share'}" method="post">
<label>Email du destinataire :</label>
<input type="email" name="email" required>
<label>Permission :</label>
<select name="permission">
<option value="READ">Lecture</option>
<option value="COMMENT">Commentaire</option>
<option value="ADMIN">Admin</option>
</select>
<button type="submit">Partager</button>
</form>
<h4>Utilisateurs ayant accès :</h4>
<table>
<tr>
<th>Utilisateur</th>
<th>Permission</th>
<th>Actions</th>
</tr>
<tr th:each="p : ${partages}">
<td th:text="${p.utilisateur.email}"></td>
<!-- FORMULAIRE UPDATE PERMISSION -->
<td>
<form th:action="@{'/album/' + ${album.id} + '/share/update'}" method="post">
<input type="hidden" name="email" th:value="${p.utilisateur.email}"/>
<select name="permission">
<option value="READ" th:selected="${p.permission == 'READ'}">Lecture</option>
<option value="COMMENT" th:selected="${p.permission == 'COMMENT'}">Commentaire</option>
<option value="ADMIN" th:selected="${p.permission == 'ADMIN'}">Admin</option>
</select>
<button type="submit">Modifier</button>
</form>
</td>
<td>
<a th:href="@{'/album/' + ${album.id} + '/unshare/' + ${p.utilisateur.email}}">Retirer</a>
</td>
</tr>
</table>
<hr>
<!-- SUPPRESSION ALBUM -->
<a th:href="@{'/album/' + ${album.id} + '/delete'}" style="color:red;">
Supprimer lalbum
</a>
</div>
</body>
</html>

View File

@@ -7,6 +7,7 @@
<h1>Bienvenue sur FotoSharing<span th:if="${prenom}" th:text="' : ' + ${prenom}"></span></h1> <h1>Bienvenue sur FotoSharing<span th:if="${prenom}" th:text="' : ' + ${prenom}"></span></h1>
<p><a th:href="@{/upload}">Uploader une photo</a></p> <p><a th:href="@{/upload}">Uploader une photo</a></p>
<p><a th:href="@{/mes-photos}">Voir mes photos</a></p> <p><a th:href="@{/mes-photos}">Voir mes photos</a></p>
<p><a th:href="@{/mes-albums}">Voir mes albums</a></p>
<p><a th:href="@{/galerie}">Voir la galerie publique</a></p> <p><a th:href="@{/galerie}">Voir la galerie publique</a></p>
<p> <p>
<form th:action="@{/logout}" method="post" style="display: inline;"> <form th:action="@{/logout}" method="post" style="display: inline;">

View File

@@ -0,0 +1,70 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<title>Mes albums</title>
</head>
<body>
<h1>Mes albums</h1>
<!-- FORMULAIRE AJOUT ALBUM -->
<h3>Créer un album</h3>
<form th:action="@{/albums/create}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<label>Nom :</label>
<input type="text" name="nom" required><br/>
<label>Description :</label>
<input type="text" name="description"><br/>
<label>Visibilité :</label>
<select name="visibilite">
<option value="PRIVATE">Privé</option>
<option value="SHARED">Partagé</option>
<option value="PUBLIC">Public</option>
</select>
<button type="submit">Créer</button>
</form>
<hr/>
<!-- LISTE ALBUMS -->
<h2>Liste de mes albums</h2>
<div th:each="album : ${albums.content}" style="margin:10px 0;">
<a th:href="@{'/album/' + ${album.id}}">
<b th:text="${album.nom}"></b>
</a>
<span th:text="'(' + ${album.visibilite} + ')'"></span>
<p th:text="${album.description}"></p>
</div>
<!-- PAGINATION -->
<div>
<a th:if="${albums.hasPrevious()}"
th:href="@{/mes-albums(page=${currentPage - 1})}"></a>
<span th:text="${currentPage + 1}"></span>
<a th:if="${albums.hasNext()}"
th:href="@{/mes-albums(page=${currentPage + 1})}"></a>
</div>
<hr/>
<h2>Albums partagés avec moi</h2>
<div th:if="${sharedAlbums.isEmpty()}">
<p>Aucun album ne vous a été partagé.</p>
</div>
<div th:unless="${sharedAlbums.isEmpty()}">
<div class="album-list">
<div th:each="album : ${sharedAlbums}" class="album-item">
<a th:href="@{'/album/' + ${album.id}}">
<strong th:text="${album.nom}"></strong>
</a>
<p>
Partagé par :
<span th:text="${album.proprietaire.email}"></span>
</p>
</div>
</div>
</div>
</body>
</html>

View File

@@ -10,7 +10,7 @@
<p><a th:href="@{/}">Retour accueil</a></p> <p><a th:href="@{/}">Retour accueil</a></p>
<h2>Mes photos privées</h2> <h2>Mes photos privées</h2>
<div> <div>
<a th:each="p : ${photosPrivees.content}" <a th:each="p : ${photosPrivees.content}" th:if="${p != null}"
th:href="@{'/photo/' + ${p.id}}"> th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/> <img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
</a> </a>
@@ -26,7 +26,7 @@
<h2>Mes photos publiques</h2> <h2>Mes photos publiques</h2>
<div> <div>
<a th:each="p : ${photosPubliques.content}" <a th:each="p : ${photosPubliques.content}" th:if="${p != null}"
th:href="@{'/photo/' + ${p.id}}"> th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/> <img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
</a> </a>
@@ -42,7 +42,7 @@
<h2>Mes photos partagées</h2> <h2>Mes photos partagées</h2>
<div style="display:flex; gap:20px; flex-wrap:wrap;"> <div style="display:flex; gap:20px; flex-wrap:wrap;">
<div th:each="p : ${mesPhotosPartagees.content}"> <div th:each="p : ${mesPhotosPartagees.content}" th:if="${p != null}">
<a th:href="@{'/photo/' + ${p.id}}"> <a th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120" <img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"
style="display:block; border:1px solid #ccc;"/> style="display:block; border:1px solid #ccc;"/>
@@ -76,6 +76,7 @@
<h2>Photos partagées avec moi</h2> <h2>Photos partagées avec moi</h2>
<div> <div>
<a th:each="p : ${photosPartagees.content}" <a th:each="p : ${photosPartagees.content}"
th:if="${p != null}"
th:href="@{'/photo/' + ${p.id}}"> th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/> <img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
</a> </a>

View File

@@ -0,0 +1,101 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<title>Détail album</title>
</head>
<body>
<a th:href="@{/mes-albums}">⬅ Retour aux albums</a>
<h1 th:text="${album.nom}"></h1>
<p><b>Description :</b> <span th:text="${album.description}"></span></p>
<p><b>Propriétaire :</b> <span th:text="${album.proprietaire.email}"></span></p>
<p><b>Visibilité :</b> <span th:text="${album.visibilite}"></span></p>
<hr/>
<!-- LISTE DES PHOTOS -->
<h2>Photos dans lalbum</h2>
<div th:each="p : ${album.photos}" style="display:inline-block; margin:10px;">
<a th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
</a>
<br/>
<a th:if="${canAdmin}" th:href="@{'/album/' + ${album.id} + '/remove/' + ${p.id}}"
style="color:red;">Retirer</a>
</div>
<!-- Bloc visible uniquement par le propriétaire ou ADMIN album -->
<div th:if="${canAdmin}">
<hr/>
<!-- AJOUT PHOTO -->
<h2>Ajouter une photo</h2>
<div th:if="${param.added}" style="color:green; font-weight:bold;">
Photo ajoutée !
</div>
<form th:action="@{'/album/' + ${album.id} + '/add'}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<select name="photoId">
<option th:each="p : ${myPhotos}"
th:value="${p.id}"
th:text="${p.nomFichierOriginal}">
</option>
</select>
<button type="submit">Ajouter</button>
</form>
<hr/>
<h3>Gestion des partages de lalbum</h3>
<div th:if="${param.error}" style="color:red; font-weight:bold;">
<span th:text="${param.error}"></span>
</div>
<!-- FORMULAIRE AJOUT PARTAGE -->
<form th:action="@{'/album/' + ${album.id} + '/share'}" method="post">
<label>Email du destinataire :</label>
<input type="email" name="email" required>
<label>Permission :</label>
<select name="permission">
<option value="READ">Lecture</option>
<option value="COMMENT">Commentaire</option>
<option value="ADMIN">Admin</option>
</select>
<button type="submit">Partager</button>
</form>
<h4>Utilisateurs ayant accès :</h4>
<table>
<tr>
<th>Utilisateur</th>
<th>Permission</th>
<th>Actions</th>
</tr>
<tr th:each="p : ${partages}">
<td th:text="${p.utilisateur.email}"></td>
<!-- FORMULAIRE UPDATE PERMISSION -->
<td>
<form th:action="@{'/album/' + ${album.id} + '/share/update'}" method="post">
<input type="hidden" name="email" th:value="${p.utilisateur.email}"/>
<select name="permission">
<option value="READ" th:selected="${p.permission == 'READ'}">Lecture</option>
<option value="COMMENT" th:selected="${p.permission == 'COMMENT'}">Commentaire</option>
<option value="ADMIN" th:selected="${p.permission == 'ADMIN'}">Admin</option>
</select>
<button type="submit">Modifier</button>
</form>
</td>
<td>
<a th:href="@{'/album/' + ${album.id} + '/unshare/' + ${p.utilisateur.email}}">Retirer</a>
</td>
</tr>
</table>
<hr>
<!-- SUPPRESSION ALBUM -->
<a th:href="@{'/album/' + ${album.id} + '/delete'}" style="color:red;">
Supprimer lalbum
</a>
</div>
</body>
</html>

View File

@@ -7,6 +7,7 @@
<h1>Bienvenue sur FotoSharing<span th:if="${prenom}" th:text="' : ' + ${prenom}"></span></h1> <h1>Bienvenue sur FotoSharing<span th:if="${prenom}" th:text="' : ' + ${prenom}"></span></h1>
<p><a th:href="@{/upload}">Uploader une photo</a></p> <p><a th:href="@{/upload}">Uploader une photo</a></p>
<p><a th:href="@{/mes-photos}">Voir mes photos</a></p> <p><a th:href="@{/mes-photos}">Voir mes photos</a></p>
<p><a th:href="@{/mes-albums}">Voir mes albums</a></p>
<p><a th:href="@{/galerie}">Voir la galerie publique</a></p> <p><a th:href="@{/galerie}">Voir la galerie publique</a></p>
<p> <p>
<form th:action="@{/logout}" method="post" style="display: inline;"> <form th:action="@{/logout}" method="post" style="display: inline;">

View File

@@ -0,0 +1,70 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<title>Mes albums</title>
</head>
<body>
<h1>Mes albums</h1>
<!-- FORMULAIRE AJOUT ALBUM -->
<h3>Créer un album</h3>
<form th:action="@{/albums/create}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<label>Nom :</label>
<input type="text" name="nom" required><br/>
<label>Description :</label>
<input type="text" name="description"><br/>
<label>Visibilité :</label>
<select name="visibilite">
<option value="PRIVATE">Privé</option>
<option value="SHARED">Partagé</option>
<option value="PUBLIC">Public</option>
</select>
<button type="submit">Créer</button>
</form>
<hr/>
<!-- LISTE ALBUMS -->
<h2>Liste de mes albums</h2>
<div th:each="album : ${albums.content}" style="margin:10px 0;">
<a th:href="@{'/album/' + ${album.id}}">
<b th:text="${album.nom}"></b>
</a>
<span th:text="'(' + ${album.visibilite} + ')'"></span>
<p th:text="${album.description}"></p>
</div>
<!-- PAGINATION -->
<div>
<a th:if="${albums.hasPrevious()}"
th:href="@{/mes-albums(page=${currentPage - 1})}"></a>
<span th:text="${currentPage + 1}"></span>
<a th:if="${albums.hasNext()}"
th:href="@{/mes-albums(page=${currentPage + 1})}"></a>
</div>
<hr/>
<h2>Albums partagés avec moi</h2>
<div th:if="${sharedAlbums.isEmpty()}">
<p>Aucun album ne vous a été partagé.</p>
</div>
<div th:unless="${sharedAlbums.isEmpty()}">
<div class="album-list">
<div th:each="album : ${sharedAlbums}" class="album-item">
<a th:href="@{'/album/' + ${album.id}}">
<strong th:text="${album.nom}"></strong>
</a>
<p>
Partagé par :
<span th:text="${album.proprietaire.email}"></span>
</p>
</div>
</div>
</div>
</body>
</html>

View File

@@ -10,7 +10,7 @@
<p><a th:href="@{/}">Retour accueil</a></p> <p><a th:href="@{/}">Retour accueil</a></p>
<h2>Mes photos privées</h2> <h2>Mes photos privées</h2>
<div> <div>
<a th:each="p : ${photosPrivees.content}" <a th:each="p : ${photosPrivees.content}" th:if="${p != null}"
th:href="@{'/photo/' + ${p.id}}"> th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/> <img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
</a> </a>
@@ -26,7 +26,7 @@
<h2>Mes photos publiques</h2> <h2>Mes photos publiques</h2>
<div> <div>
<a th:each="p : ${photosPubliques.content}" <a th:each="p : ${photosPubliques.content}" th:if="${p != null}"
th:href="@{'/photo/' + ${p.id}}"> th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/> <img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
</a> </a>
@@ -42,7 +42,7 @@
<h2>Mes photos partagées</h2> <h2>Mes photos partagées</h2>
<div style="display:flex; gap:20px; flex-wrap:wrap;"> <div style="display:flex; gap:20px; flex-wrap:wrap;">
<div th:each="p : ${mesPhotosPartagees.content}"> <div th:each="p : ${mesPhotosPartagees.content}" th:if="${p != null}">
<a th:href="@{'/photo/' + ${p.id}}"> <a th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120" <img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"
style="display:block; border:1px solid #ccc;"/> style="display:block; border:1px solid #ccc;"/>
@@ -76,6 +76,7 @@
<h2>Photos partagées avec moi</h2> <h2>Photos partagées avec moi</h2>
<div> <div>
<a th:each="p : ${photosPartagees.content}" <a th:each="p : ${photosPartagees.content}"
th:if="${p != null}"
th:href="@{'/photo/' + ${p.id}}"> th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/> <img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
</a> </a>