FEAT : Ajout pagination galerie publique

This commit is contained in:
2025-12-02 09:12:07 +01:00
parent 109cc8065e
commit 6cb141293c
15 changed files with 181 additions and 78 deletions

View File

@@ -11,6 +11,7 @@ import local.epul4a.fotosharing.service.CommentaireService;
import local.epul4a.fotosharing.service.PhotoService; import local.epul4a.fotosharing.service.PhotoService;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.PathResource; import org.springframework.core.io.PathResource;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -91,24 +92,23 @@ public class PhotoController {
} }
@GetMapping("/mes-photos") @GetMapping("/mes-photos")
public String mesPhotos(Model model, Authentication authentication) { public String mesPhotos(Model model, Authentication auth) {
String email = authentication.getName(); String email = auth.getName();
// photos que je possède model.addAttribute("photosPrivees", photoService.listPrivatePhotos(email));
List<Photo> mesPhotos = photoService.listByOwner(email); model.addAttribute("photosPubliques", photoService.listPublicPhotos(email));
// photos partagées avec moi model.addAttribute("photosPartagees", photoService.listSharedWith(email));
List<Photo> photosPartagees = photoService.listSharedWith(email);
model.addAttribute("mesPhotos", mesPhotos);
model.addAttribute("photosPartagees", photosPartagees);
return "mes-photos"; return "mes-photos";
} }
@GetMapping("/galerie") @GetMapping("/galerie")
public String galerie(Model model) { public String galerie(@RequestParam(defaultValue = "0") int page, Model model) {
model.addAttribute("photos", photoService.listPublicPhotos()); Page<Photo> photosPage = photoService.listPublic(page, 12);
model.addAttribute("photosPage", photosPage);
model.addAttribute("currentPage", page);
return "galerie"; return "galerie";
} }
@GetMapping("/photo/{id}") @GetMapping("/photo/{id}")
@PreAuthorize("@securityService.canAccessPhoto(authentication, #id)") @PreAuthorize("@securityService.canAccessPhoto(authentication, #id)")
public String viewPhoto(@PathVariable Long id, public String viewPhoto(@PathVariable Long id,
@@ -127,6 +127,10 @@ public class PhotoController {
String currentUser = (auth != null ? auth.getName() : null); String currentUser = (auth != null ? auth.getName() : null);
model.addAttribute("currentUser", currentUser); model.addAttribute("currentUser", currentUser);
// Liste des partages
List<Partage> partages = partageRepository.findByPhoto_Id(id);
model.addAttribute("partages", partages);
return "photo-detail"; return "photo-detail";
} }
@@ -166,6 +170,12 @@ public class PhotoController {
return "redirect:/photo/" + id + "?shared=ok"; return "redirect:/photo/" + id + "?shared=ok";
} }
@GetMapping("/photo/{id}/unshare/{email}")
public String unshare(@PathVariable Long id, @PathVariable String email) {
photoService.unshare(id, email);
return "redirect:/photo/" + id;
}
} }

View File

@@ -1,10 +1,15 @@
package local.epul4a.fotosharing.repository; package local.epul4a.fotosharing.repository;
import local.epul4a.fotosharing.model.Photo; import local.epul4a.fotosharing.model.Photo;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.domain.Pageable;
import java.util.List; import java.util.List;
public interface PhotoRepository extends JpaRepository<Photo, Long> { public interface PhotoRepository extends JpaRepository<Photo, Long> {
List<Photo> findByProprietaire_Email(String email); List<Photo> findByProprietaire_Email(String email);
List<Photo> findByVisibilite(Photo.Visibilite visibilite); List<Photo> findByVisibilite(Photo.Visibilite visibilite);
Page<Photo> findByVisibilite(Photo.Visibilite visibilite, Pageable pageable);
} }

View File

@@ -1,6 +1,7 @@
package local.epul4a.fotosharing.service; package local.epul4a.fotosharing.service;
import local.epul4a.fotosharing.model.Photo; import local.epul4a.fotosharing.model.Photo;
import org.springframework.data.domain.Page;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
@@ -12,6 +13,11 @@ public interface PhotoService {
List<Photo> listByOwner(String email); List<Photo> listByOwner(String email);
List<Photo> listPublicPhotos(); List<Photo> listPublicPhotos();
List<Photo> listSharedWith(String email); 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);
} }

View File

@@ -8,6 +8,9 @@ import local.epul4a.fotosharing.repository.PhotoRepository;
import local.epul4a.fotosharing.repository.UtilisateurRepository; import local.epul4a.fotosharing.repository.UtilisateurRepository;
import local.epul4a.fotosharing.service.PhotoService; import local.epul4a.fotosharing.service.PhotoService;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@@ -83,4 +86,34 @@ public class PhotoServiceImpl implements PhotoService {
.toList(); .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()
.orElse(null);
if (p != null) partageRepository.delete(p);
}
@Override
public Page<Photo> listPublic(int page, int size) {
Pageable pageable = PageRequest.of(page, size);
return photoRepository.findByVisibilite(Photo.Visibilite.PUBLIC, pageable);
}
} }

View File

@@ -1,25 +0,0 @@
spring.application.name=FotoSharing
# ===============================
# DATABASE
# ===============================
spring.datasource.url=jdbc:mariadb://192.168.112.10:3306/fotoshareDB
spring.datasource.username=ufoto
spring.datasource.password=4AinfoRep-25
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.jpa.properties.hibernate.jdbc.time_zone=UTC
# ===============================
# EMPLACEMENT DE STICKAGE
# ===============================
file.upload-dir=/opt/photo-app/uploads
spring.servlet.multipart.max-file-size=20MB
spring.servlet.multipart.max-request-size=20MB

View File

@@ -1,17 +1,40 @@
<!doctype html> <!doctype html>
<html xmlns:th="http://www.thymeleaf.org"> <html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="utf-8"><title>Galerie</title></head> <head>
<meta charset="utf-8"/>
<title>Galerie publique</title>
</head>
<body> <body>
<h1>Galerie publique</h1> <h1>Galerie publique</h1>
<p><a th:href="@{/}">Accueil</a></p> <!-- Affichage des photos -->
<div>
<a th:each="p : ${photosPage.content}"
th:href="@{'/photo/' + ${p.id}}"
style="display:inline-block; margin:10px;">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}"
width="150"
style="border:1px solid #ccc;"/>
</a>
</div>
<!-- Pagination -->
<div style="margin-top:20px;">
<a th:if="${currentPage > 0}"
th:href="@{/galerie(page=${currentPage - 1})}">
⬅ Précédent
</a>
<span th:text="'Page ' + (${currentPage} + 1)"></span>
<a th:if="${photosPage.hasNext()}"
th:href="@{/galerie(page=${currentPage + 1})}">
Suivant ➡
</a>
</div>
<ul>
<li th:each="p : ${photos}">
<span th:text="${p.nomFichierOriginal}">nom</span>
<a th:href="@{'/photo/' + ${p.id}}">Voir</a>
</li>
</ul>
</body> </body>
</html> </html>

View File

@@ -11,24 +11,30 @@
<div th:if="${#lists.isEmpty(mesPhotos)}"> <div th:if="${#lists.isEmpty(mesPhotos)}">
<p>Vous n'avez pas encore de photos.</p> <p>Vous n'avez pas encore de photos.</p>
</div> </div>
<h2>Mes photos privées</h2>
<ul> <ul>
<li th:each="p : ${mesPhotos}"> <li th:each="p : ${photosPrivees}">
<span th:text="${p.nomFichierOriginal}"></span> <a th:href="@{'/photo/' + ${p.id}}"
th:text="${p.nomFichierOriginal}"></a>
<a th:href="@{'/photo/' + ${p.id}}">Voir</a> <span style="color:red;">[PRIVÉE]</span>
</li>
</ul>
<h2>Mes photos publiques</h2>
<ul>
<li th:each="p : ${photosPubliques}">
<a th:href="@{'/photo/' + ${p.id}}"
th:text="${p.nomFichierOriginal}"></a>
<span style="color:blue;">[PUBLIQUE]</span>
</li> </li>
</ul> </ul>
<h2>Photos partagées avec moi</h2> <h2>Photos partagées avec moi</h2>
<div th:if="${#lists.isEmpty(photosPartagees)}"> <ul>
<p>Aucune photo partagée.</p>
</div>
<ul th:if="${!#lists.isEmpty(photosPartagees)}">
<li th:each="p : ${photosPartagees}"> <li th:each="p : ${photosPartagees}">
<span th:text="${p.nomFichierOriginal}"></span> <a th:href="@{'/photo/' + ${p.id}}"
<span style="color: green; font-weight: bold;">[SHARED]</span> th:text="${p.nomFichierOriginal}"></a>
<span style="color:green;">[SHARED]</span>
<a th:href="@{'/photo/' + ${p.id}}">Voir</a>
</li> </li>
</ul> </ul>

View File

@@ -30,6 +30,14 @@
</ul> </ul>
<!-- Partage de la photo --> <!-- Partage de la photo -->
<h2>Partagée avec :</h2>
<ul>
<li th:each="p : ${partages}">
<span th:text="${p.utilisateur.email}"></span>
<a th:href="@{'/photo/' + ${photo.id} + '/unshare/' + ${p.utilisateur.email}}"
style="color:red;">Retirer</a>
</li>
</ul>
<h2>Partager la photo</h2> <h2>Partager la photo</h2>
<div th:if="${currentUser == photo.proprietaire.email}"> <div th:if="${currentUser == photo.proprietaire.email}">
<form th:action="@{'/photo/' + ${photo.id} + '/share'}" method="post"> <form th:action="@{'/photo/' + ${photo.id} + '/share'}" method="post">

View File

@@ -1,17 +1,40 @@
<!doctype html> <!doctype html>
<html xmlns:th="http://www.thymeleaf.org"> <html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="utf-8"><title>Galerie</title></head> <head>
<meta charset="utf-8"/>
<title>Galerie publique</title>
</head>
<body> <body>
<h1>Galerie publique</h1> <h1>Galerie publique</h1>
<p><a th:href="@{/}">Accueil</a></p> <!-- Affichage des photos -->
<div>
<a th:each="p : ${photosPage.content}"
th:href="@{'/photo/' + ${p.id}}"
style="display:inline-block; margin:10px;">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}"
width="150"
style="border:1px solid #ccc;"/>
</a>
</div>
<!-- Pagination -->
<div style="margin-top:20px;">
<a th:if="${currentPage > 0}"
th:href="@{/galerie(page=${currentPage - 1})}">
⬅ Précédent
</a>
<span th:text="'Page ' + (${currentPage} + 1)"></span>
<a th:if="${photosPage.hasNext()}"
th:href="@{/galerie(page=${currentPage + 1})}">
Suivant ➡
</a>
</div>
<ul>
<li th:each="p : ${photos}">
<span th:text="${p.nomFichierOriginal}">nom</span>
<a th:href="@{'/photo/' + ${p.id}}">Voir</a>
</li>
</ul>
</body> </body>
</html> </html>

View File

@@ -11,24 +11,30 @@
<div th:if="${#lists.isEmpty(mesPhotos)}"> <div th:if="${#lists.isEmpty(mesPhotos)}">
<p>Vous n'avez pas encore de photos.</p> <p>Vous n'avez pas encore de photos.</p>
</div> </div>
<h2>Mes photos privées</h2>
<ul> <ul>
<li th:each="p : ${mesPhotos}"> <li th:each="p : ${photosPrivees}">
<span th:text="${p.nomFichierOriginal}"></span> <a th:href="@{'/photo/' + ${p.id}}"
th:text="${p.nomFichierOriginal}"></a>
<a th:href="@{'/photo/' + ${p.id}}">Voir</a> <span style="color:red;">[PRIVÉE]</span>
</li>
</ul>
<h2>Mes photos publiques</h2>
<ul>
<li th:each="p : ${photosPubliques}">
<a th:href="@{'/photo/' + ${p.id}}"
th:text="${p.nomFichierOriginal}"></a>
<span style="color:blue;">[PUBLIQUE]</span>
</li> </li>
</ul> </ul>
<h2>Photos partagées avec moi</h2> <h2>Photos partagées avec moi</h2>
<div th:if="${#lists.isEmpty(photosPartagees)}"> <ul>
<p>Aucune photo partagée.</p>
</div>
<ul th:if="${!#lists.isEmpty(photosPartagees)}">
<li th:each="p : ${photosPartagees}"> <li th:each="p : ${photosPartagees}">
<span th:text="${p.nomFichierOriginal}"></span> <a th:href="@{'/photo/' + ${p.id}}"
<span style="color: green; font-weight: bold;">[SHARED]</span> th:text="${p.nomFichierOriginal}"></a>
<span style="color:green;">[SHARED]</span>
<a th:href="@{'/photo/' + ${p.id}}">Voir</a>
</li> </li>
</ul> </ul>

View File

@@ -30,6 +30,14 @@
</ul> </ul>
<!-- Partage de la photo --> <!-- Partage de la photo -->
<h2>Partagée avec :</h2>
<ul>
<li th:each="p : ${partages}">
<span th:text="${p.utilisateur.email}"></span>
<a th:href="@{'/photo/' + ${photo.id} + '/unshare/' + ${p.utilisateur.email}}"
style="color:red;">Retirer</a>
</li>
</ul>
<h2>Partager la photo</h2> <h2>Partager la photo</h2>
<div th:if="${currentUser == photo.proprietaire.email}"> <div th:if="${currentUser == photo.proprietaire.email}">
<form th:action="@{'/photo/' + ${photo.id} + '/share'}" method="post"> <form th:action="@{'/photo/' + ${photo.id} + '/share'}" method="post">