From d03ec054d2ff83136442658cdecd70eddad56aba Mon Sep 17 00:00:00 2001 From: Thibaut Valentin Date: Mon, 15 Jul 2024 23:39:01 +0200 Subject: [PATCH] wip: affiliation --- .../ffsaf/data/model/ClubModel.java | 2 + .../ffsaf/data/model/MembreModel.java | 2 +- .../ffsaf/data/model/SequenceModel.java | 24 ++ .../data/repository/SequenceRepository.java | 21 ++ .../domain/service/AffiliationService.java | 191 +++++++++++++++- .../ffsaf/domain/service/KeycloakService.java | 6 +- .../ffsaf/domain/service/MembreService.java | 13 ++ .../ffsaf/rest/AffiliationEndpoints.java | 42 ++++ .../rest/from/AffiliationRequestSaveForm.java | 115 ++++++++++ .../titionfire/ffsaf/utils/SequenceType.java | 5 + .../java/fr/titionfire/ffsaf/utils/Utils.java | 50 ++++- src/main/webapp/src/pages/DemandeAff.jsx | 2 +- .../admin/affiliation/AffiliationReqPage.jsx | 207 ++++++++++++++++-- .../webapp/src/pages/admin/club/ClubPage.jsx | 3 +- 14 files changed, 643 insertions(+), 40 deletions(-) create mode 100644 src/main/java/fr/titionfire/ffsaf/data/model/SequenceModel.java create mode 100644 src/main/java/fr/titionfire/ffsaf/data/repository/SequenceRepository.java create mode 100644 src/main/java/fr/titionfire/ffsaf/rest/from/AffiliationRequestSaveForm.java create mode 100644 src/main/java/fr/titionfire/ffsaf/utils/SequenceType.java diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java index 6b38d76..d65b176 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java @@ -45,6 +45,8 @@ public class ClubModel { String contact_intern; + String address; + String RNA; Long SIRET; diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/MembreModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/MembreModel.java index 334c3fc..a64b1a9 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/MembreModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/MembreModel.java @@ -53,6 +53,6 @@ public class MembreModel { String url_photo; - @OneToMany(mappedBy = "membre", fetch = FetchType.LAZY) + @OneToMany(mappedBy = "membre", fetch = FetchType.LAZY, cascade = CascadeType.ALL) List licences; } diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/SequenceModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/SequenceModel.java new file mode 100644 index 0000000..f8fdad3 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/data/model/SequenceModel.java @@ -0,0 +1,24 @@ +package fr.titionfire.ffsaf.data.model; + +import fr.titionfire.ffsaf.utils.SequenceType; +import io.quarkus.runtime.annotations.RegisterForReflection; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@RegisterForReflection + +@Entity +@Table(name = "sequence") +public class SequenceModel { + @Id + SequenceType type; + + long value; +} diff --git a/src/main/java/fr/titionfire/ffsaf/data/repository/SequenceRepository.java b/src/main/java/fr/titionfire/ffsaf/data/repository/SequenceRepository.java new file mode 100644 index 0000000..9cd235f --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/data/repository/SequenceRepository.java @@ -0,0 +1,21 @@ +package fr.titionfire.ffsaf.data.repository; + +import fr.titionfire.ffsaf.data.model.SequenceModel; +import fr.titionfire.ffsaf.utils.SequenceType; +import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; +import io.smallrye.mutiny.Uni; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class SequenceRepository implements PanacheRepositoryBase { + + public Uni getNextValueInTransaction(SequenceType type) { + return this.findById(type).onItem().ifNull() + .switchTo(() -> this.persist(new SequenceModel(type, 1L))) + .chain(v -> { + v.setValue(v.getValue() + 1); + return this.persistAndFlush(v); + }) + .map(SequenceModel::getValue); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java index 2b21572..7f06786 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java @@ -1,20 +1,19 @@ package fr.titionfire.ffsaf.domain.service; -import fr.titionfire.ffsaf.data.model.AffiliationModel; -import fr.titionfire.ffsaf.data.model.AffiliationRequestModel; -import fr.titionfire.ffsaf.data.repository.AffiliationRepository; -import fr.titionfire.ffsaf.data.repository.AffiliationRequestRepository; -import fr.titionfire.ffsaf.data.repository.ClubRepository; -import fr.titionfire.ffsaf.data.repository.CombRepository; +import fr.titionfire.ffsaf.data.model.*; +import fr.titionfire.ffsaf.data.repository.*; import fr.titionfire.ffsaf.rest.data.SimpleAffiliation; import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliation; import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm; +import fr.titionfire.ffsaf.rest.from.AffiliationRequestSaveForm; +import fr.titionfire.ffsaf.utils.SequenceType; import fr.titionfire.ffsaf.utils.Utils; import io.quarkus.hibernate.reactive.panache.Panache; import io.quarkus.hibernate.reactive.panache.common.WithSession; import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import jakarta.ws.rs.NotFoundException; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.hibernate.reactive.mutiny.Mutiny; @@ -37,6 +36,15 @@ public class AffiliationService { @Inject AffiliationRepository repository; + @Inject + KeycloakService keycloakService; + + @Inject + SequenceRepository sequenceRepository; + + @Inject + LicenceRepository licenceRepository; + @ConfigProperty(name = "upload_dir") String media; @@ -46,21 +54,21 @@ public class AffiliationService { // noinspection ResultOfMethodCallIgnored return Uni.createFrom().item(affModel) - .call(model -> ((model.getM1_lincence() != 0) ? combRepository.find("licence", + .call(model -> ((model.getM1_lincence() != -1) ? combRepository.find("licence", model.getM1_lincence()).count().invoke(count -> { if (count == 0) { throw new IllegalArgumentException("Licence membre n°1 inconnue"); } }) : Uni.createFrom().nullItem()) ) - .call(model -> ((model.getM2_lincence() != 0) ? combRepository.find("licence", + .call(model -> ((model.getM2_lincence() != -1) ? combRepository.find("licence", model.getM2_lincence()).count().invoke(count -> { if (count == 0) { throw new IllegalArgumentException("Licence membre n°2 inconnue"); } }) : Uni.createFrom().nullItem()) ) - .call(model -> ((model.getM3_lincence() != 0) ? combRepository.find("licence", + .call(model -> ((model.getM3_lincence() != -1) ? combRepository.find("licence", model.getM3_lincence()).count().invoke(count -> { if (count == 0) { throw new IllegalArgumentException("Licence membre n°3 inconnue"); @@ -76,10 +84,165 @@ public class AffiliationService { .map(__ -> "Ok"); } + public Uni saveAdmin(AffiliationRequestSaveForm form) { + return repositoryRequest.findById(form.getId()) + .onItem().ifNull().failWith(new NotFoundException("Affiliation request not found")) + .map(model -> { + model.setName(form.getName()); + model.setSiret(form.getSiret()); + model.setRNA(form.getRna()); + model.setAddress(form.getAddress()); + + if (form.getM1_mode() == 2) { + model.setM1_lname(form.getM1_lname()); + model.setM1_fname(form.getM1_fname()); + } else { + model.setM1_lincence( + form.getM1_lincence() == null ? 0 : Integer.parseInt(form.getM1_lincence())); + } + model.setM1_role(form.getM1_role()); + if (form.getM1_email_mode() == 0) + model.setM1_email(form.getM1_email()); + + if (form.getM2_mode() == 2) { + model.setM2_lname(form.getM2_lname()); + model.setM2_fname(form.getM2_fname()); + } else { + model.setM2_lincence( + form.getM2_lincence() == null ? 0 : Integer.parseInt(form.getM2_lincence())); + } + model.setM2_role(form.getM2_role()); + if (form.getM2_email_mode() == 0) + model.setM2_email(form.getM2_email()); + + if (form.getM3_mode() == 2) { + model.setM3_lname(form.getM3_lname()); + model.setM3_fname(form.getM3_fname()); + } else { + model.setM3_lincence( + form.getM3_lincence() == null ? 0 : Integer.parseInt(form.getM3_lincence())); + } + model.setM3_role(form.getM3_role()); + if (form.getM3_email_mode() == 0) + model.setM3_email(form.getM3_email()); + + return model; + }) + .chain(model -> Panache.withTransaction(() -> repositoryRequest.persist(model))) + .onItem() + .invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getLogo(), media, + "aff_request/logo"))) + .onItem() + .invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getStatus(), media, + "aff_request/status"))) + .map(__ -> "Ok"); + } + + private Uni setMembre(AffiliationRequestSaveForm.Member member, ClubModel club, int saison) { + return Uni.createFrom().nullItem().chain(__ -> { + if (member.getMode() == 2) { + MembreModel membreModel = new MembreModel(); + membreModel.setFname(member.getFname()); + membreModel.setLname(member.getLname()); + membreModel.setClub(club); + membreModel.setRole(member.getRole()); + membreModel.setEmail(member.getEmail()); + return Panache.withTransaction(() -> + combRepository.persist(membreModel) + .chain(m -> sequenceRepository.getNextValueInTransaction(SequenceType.Licence) + .invoke(l -> m.setLicence(Math.toIntExact(l))) + .chain(() -> combRepository.persist(m)))); + } else { + return combRepository.find("licence", Integer.parseInt(member.getLicence())).firstResult() + .onItem().ifNull().switchTo(() -> { + MembreModel membreModel = new MembreModel(); + membreModel.setFname(member.getFname()); + membreModel.setLname(member.getLname()); + return Panache.withTransaction( + () -> sequenceRepository.getNextValueInTransaction(SequenceType.Licence) + .invoke(l -> membreModel.setLicence(Math.toIntExact(l))) + .chain(() -> combRepository.persist(membreModel))); + }) + .map(m -> { + m.setClub(club); + m.setRole(member.getRole()); + m.setEmail(member.getEmail()); + return m; + }).call(m -> Panache.withTransaction(() -> combRepository.persist(m))); + } + }) + .call(m -> (m.getUserId() == null) ? keycloakService.initCompte(m.getId()) : + keycloakService.setClubGroupMembre(m, club)) + .call(m -> Panache.withTransaction(() -> licenceRepository.persist( + new LicenceModel(null, m, saison, false, true)))); + } + + public Uni accept(AffiliationRequestSaveForm form) { + return repositoryRequest.findById(form.getId()) + .onItem().ifNull().failWith(new NotFoundException("Affiliation request not found")) + .chain(req -> + clubRepository.find("SIRET = ?1", form.getSiret()).firstResult() + .chain(model -> (model == null) ? acceptNew(form, req) : acceptOld(form, req, model)) + .call(club -> setMembre(form.new Member(1), club, req.getSaison()) + .call(__ -> setMembre(form.new Member(2), club, req.getSaison()) + .call(___ -> setMembre(form.new Member(3), club, req.getSaison())))) + .onItem() + .invoke(model -> Uni.createFrom() + .future(Utils.replacePhoto(form.getId(), form.getLogo(), media, + "aff_request/logo"))) + .onItem() + .invoke(model -> Uni.createFrom() + .future(Utils.replacePhoto(form.getId(), form.getStatus(), media, + "aff_request/status"))) + .call(model -> Utils.moveMedia(form.getId(), model.getId(), media, "aff_request/logo", + "ppClub")) + .call(model -> Utils.moveMedia(form.getId(), model.getId(), media, "aff_request/status", + "clubStatus")) + ) + .map(__ -> "Ok"); + } + + private Uni acceptNew(AffiliationRequestSaveForm form, AffiliationRequestModel model) { + return Uni.createFrom().nullItem() + .chain(() -> { + ClubModel club = new ClubModel(); + club.setName(form.getName()); + club.setCountry("fr"); + club.setSIRET(form.getSiret()); + club.setRNA(form.getRna()); + club.setAddress(form.getAddress()); + club.setAffiliations(List.of(new AffiliationModel(null, club, model.getSaison()))); + return Panache.withTransaction(() -> clubRepository.persist(club) + .chain(c -> sequenceRepository.getNextValueInTransaction(SequenceType.Affiliation) + .invoke(c::setNo_affiliation) + .chain(() -> clubRepository.persist(c)) + .chain(() -> repositoryRequest.delete(model)) + ) + .chain(() -> repository.persist(new AffiliationModel(null, club, model.getSaison()))) + .map(c -> club)); + }); + } + + private Uni acceptOld(AffiliationRequestSaveForm form, AffiliationRequestModel model, ClubModel club) { + return Uni.createFrom().nullItem() + .chain(() -> { + club.setName(form.getName()); + club.setCountry("fr"); + club.setSIRET(form.getSiret()); + club.setRNA(form.getRna()); + club.setAddress(form.getAddress()); + return Panache.withTransaction(() -> clubRepository.persist(club) + .chain(() -> repository.persist(new AffiliationModel(null, club, model.getSaison()))) + .chain(() -> repositoryRequest.delete(model))); + }) + .map(__ -> club); + } + public Uni getRequest(long id) { return repositoryRequest.findById(id).map(SimpleReqAffiliation::fromModel) + .onItem().ifNull().failWith(new NotFoundException("Affiliation request not found")) .call(out -> clubRepository.find("SIRET = ?1", out.getSiret()).firstResult().invoke(c -> { - if (c != null){ + if (c != null) { out.setClub(c.getId()); out.setClub_name(c.getName()); out.setClub_no_aff(c.getNo_affiliation()); @@ -102,7 +265,9 @@ public class AffiliationService { } public Uni> getAffiliation(long id) { - return clubRepository.findById(id).call(model -> Mutiny.fetch(model.getAffiliations())) + return clubRepository.findById(id) + .onItem().ifNull().failWith(new NotFoundException("Affiliation request not found")) + .call(model -> Mutiny.fetch(model.getAffiliations())) .chain(model -> repositoryRequest.list("siret = ?1", model.getSIRET()) .map(reqs -> reqs.stream().map(req -> new SimpleAffiliation(req.getId() * -1, model.getId(), req.getSaison(), false))) @@ -112,7 +277,9 @@ public class AffiliationService { } public Uni setAffiliation(long id, int saison) { - return clubRepository.findById(id).chain(club -> + return clubRepository.findById(id) + .onItem().ifNull().failWith(new NotFoundException("Affiliation request not found")) + .chain(club -> Panache.withTransaction(() -> repository.persist(new AffiliationModel(null, club, saison)))) .map(SimpleAffiliation::fromModel); } diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/KeycloakService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/KeycloakService.java index f59c861..7a3c49d 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/KeycloakService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/KeycloakService.java @@ -192,9 +192,9 @@ public class KeycloakService { String finalLogin = login; return getUser(login).orElseThrow(() -> new KeycloakException("Fail to fetch user %s".formatted(finalLogin))); }) - .invoke(user -> keycloak.realm(realm).users().get(user.getId()) - .executeActionsEmail(List.of(RequiredAction.VERIFY_EMAIL.name(), - RequiredAction.UPDATE_PASSWORD.name()))) + //.invoke(user -> keycloak.realm(realm).users().get(user.getId()) // TODO enable for production + // .executeActionsEmail(List.of(RequiredAction.VERIFY_EMAIL.name(), + // RequiredAction.UPDATE_PASSWORD.name()))) .invoke(user -> membreModel.setUserId(user.getId())) .call(user -> membreService.setUserId(membreModel.getId(), user.getId())) .call(user -> setClubGroupMembre(membreModel, membreModel.getClub())); diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java index c545e09..138c737 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java @@ -27,6 +27,8 @@ import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.ForbiddenException; import org.eclipse.microprofile.jwt.JsonWebToken; +import java.util.List; + @WithSession @ApplicationScoped @@ -102,6 +104,10 @@ public class MembreService { return repository.findById(id); } + public Uni getByLicence(long licence) { + return repository.find("licence = ?1", licence).firstResult(); + } + public Uni update(long id, FullMemberForm membre) { return repository.findById(id) .chain(membreModel -> clubRepository.findById(membre.getClub()).map(club -> new Pair<>(membreModel, club))) @@ -235,4 +241,11 @@ public class MembreService { model.setGrade_arbitrage(input.getGrade_arbitrage()); return model; } + + public Uni> getSimilar(String fname, String lname) { + return repository.listAll().map(membreModels -> membreModels.stream() + .filter(m -> StringSimilarity.similarity(m.getFname(), fname) <= 3 && + StringSimilarity.similarity(m.getLname(), lname) <= 3) + .map(SimpleMembre::fromModel).toList()); + } } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java index 2ac5007..185c189 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java @@ -4,7 +4,9 @@ import fr.titionfire.ffsaf.domain.service.AffiliationService; import fr.titionfire.ffsaf.rest.data.SimpleAffiliation; import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliation; import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm; +import fr.titionfire.ffsaf.rest.from.AffiliationRequestSaveForm; import fr.titionfire.ffsaf.utils.GroupeUtils; +import fr.titionfire.ffsaf.utils.Utils; import io.quarkus.oidc.IdToken; import io.quarkus.security.identity.SecurityIdentity; import io.smallrye.mutiny.Uni; @@ -13,8 +15,11 @@ import jakarta.annotation.security.RolesAllowed; import jakarta.inject.Inject; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.jwt.JsonWebToken; +import java.net.URISyntaxException; import java.util.List; import java.util.function.Consumer; @@ -31,6 +36,9 @@ public class AffiliationEndpoints { @Inject SecurityIdentity securityIdentity; + @ConfigProperty(name = "upload_dir") + String media; + Consumer checkPerm = Unchecked.consumer(id -> { if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(id, idToken)) throw new ForbiddenException(); @@ -44,6 +52,24 @@ public class AffiliationEndpoints { return service.getRequest(id); } + @PUT + @Path("/request/save") + @RolesAllowed({"federation_admin"}) + @Produces(MediaType.TEXT_PLAIN) + @Consumes(MediaType.MULTIPART_FORM_DATA) + public Uni saveAdminAffRequest(AffiliationRequestSaveForm form) { + return service.saveAdmin(form); + } + + @PUT + @Path("/request/apply") + @RolesAllowed({"federation_admin"}) + @Produces(MediaType.TEXT_PLAIN) + @Consumes(MediaType.MULTIPART_FORM_DATA) + public Uni acceptAffRequest(AffiliationRequestSaveForm form) { + return service.accept(form); + } + @POST @Path("/request") @Produces(MediaType.TEXT_PLAIN) @@ -52,6 +78,7 @@ public class AffiliationEndpoints { return service.save(form); } + @GET @Path("/current") @RolesAllowed({"federation_admin"}) @@ -83,4 +110,19 @@ public class AffiliationEndpoints { public Uni deleteLicence(@PathParam("id") long id) { return service.deleteAffiliation(id); } + + @GET + @Path("/request/{id}/logo") + @RolesAllowed({"federation_admin"}) + public Uni getLogo(@PathParam("id") long id) throws URISyntaxException { + return Utils.getMediaFile(id, media, "aff_request/logo", Uni.createFrom().nullItem()); + } + + @GET + @Path("/request/{id}/status") + @RolesAllowed({"federation_admin"}) + public Uni getStatus(@PathParam("id") long id) throws URISyntaxException { + return Utils.getMediaFile(id, media, "aff_request/status", "affiliation_request_" + id + ".pdf", + Uni.createFrom().nullItem()); + } } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/from/AffiliationRequestSaveForm.java b/src/main/java/fr/titionfire/ffsaf/rest/from/AffiliationRequestSaveForm.java new file mode 100644 index 0000000..495b1d9 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/from/AffiliationRequestSaveForm.java @@ -0,0 +1,115 @@ +package fr.titionfire.ffsaf.rest.from; + +import fr.titionfire.ffsaf.utils.RoleAsso; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.core.MediaType; +import lombok.Getter; +import lombok.ToString; +import org.jboss.resteasy.reactive.PartType; + +@Getter +@ToString +public class AffiliationRequestSaveForm { + @FormParam("id") + private Long id = null; + @FormParam("name") + private String name = null; + @FormParam("siret") + private Long siret = null; + @FormParam("rna") + private String rna = null; + @FormParam("address") + private String address = null; + + @FormParam("status") + @PartType(MediaType.APPLICATION_OCTET_STREAM) + private byte[] status = new byte[0]; + @FormParam("logo") + @PartType(MediaType.APPLICATION_OCTET_STREAM) + private byte[] logo = new byte[0]; + + @FormParam("m1_mode") + private Integer m1_mode = null; + @FormParam("m1_role") + private RoleAsso m1_role = null; + @FormParam("m1_licence") + private String m1_lincence = null; + @FormParam("m1_lname") + private String m1_lname = null; + @FormParam("m1_fname") + private String m1_fname = null; + @FormParam("m1_email") + private String m1_email = null; + @FormParam("m1_email_mode") + private Integer m1_email_mode = null; + + @FormParam("m2_mode") + private Integer m2_mode = null; + @FormParam("m2_role") + private RoleAsso m2_role = null; + @FormParam("m2_licence") + private String m2_lincence = null; + @FormParam("m2_lname") + private String m2_lname = null; + @FormParam("m2_fname") + private String m2_fname = null; + @FormParam("m2_email") + private String m2_email = null; + @FormParam("m2_email_mode") + private Integer m2_email_mode = null; + + @FormParam("m3_mode") + private Integer m3_mode = null; + @FormParam("m3_role") + private RoleAsso m3_role = null; + @FormParam("m3_licence") + private String m3_lincence = null; + @FormParam("m3_lname") + private String m3_lname = null; + @FormParam("m3_fname") + private String m3_fname = null; + @FormParam("m3_email") + private String m3_email = null; + @FormParam("m3_email_mode") + private Integer m3_email_mode = null; + + + @Getter + public class Member { + private Integer mode; + private RoleAsso role; + private String licence; + private String lname; + private String fname; + private String email; + private Integer email_mode; + + public Member(int n){ + if (n == 1) { + mode = m1_mode; + role = m1_role; + licence = m1_lincence; + lname = m1_lname; + fname = m1_fname; + email = m1_email; + email_mode = m1_email_mode; + } else if (n == 2) { + mode = m2_mode; + role = m2_role; + licence = m2_lincence; + lname = m2_lname; + fname = m2_fname; + email = m2_email; + email_mode = m2_email_mode; + } else if (n == 3) { + mode = m3_mode; + role = m3_role; + licence = m3_lincence; + lname = m3_lname; + fname = m3_fname; + email = m3_email; + email_mode = m3_email_mode; + } + } + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/utils/SequenceType.java b/src/main/java/fr/titionfire/ffsaf/utils/SequenceType.java new file mode 100644 index 0000000..a38f496 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/utils/SequenceType.java @@ -0,0 +1,5 @@ +package fr.titionfire.ffsaf.utils; + +public enum SequenceType { + Licence, Affiliation +} diff --git a/src/main/java/fr/titionfire/ffsaf/utils/Utils.java b/src/main/java/fr/titionfire/ffsaf/utils/Utils.java index 7dda235..4910615 100644 --- a/src/main/java/fr/titionfire/ffsaf/utils/Utils.java +++ b/src/main/java/fr/titionfire/ffsaf/utils/Utils.java @@ -39,8 +39,47 @@ public class Utils { } } + public static Uni moveMedia(long idSrc, long idDest, String media, String dirSrc, String dirDst) { + System.out.println("moveMedia: " + idSrc + " -> " + idDest + " " + media + " " + dirSrc + " " + dirDst); + return Uni.createFrom().nullItem().map(__ -> { + File dirFile = new File(media, dirSrc); + if (!dirFile.exists()) + return "Not found"; + + File dirDestFile = new File(media, dirDst); + if (!dirDestFile.exists()) + if (!dirDestFile.mkdirs()) + return "Fail to create directory " + dirDestFile; + + FilenameFilter filter = (directory, filename) -> filename.startsWith(String.valueOf(idSrc)); + File[] files = dirFile.listFiles(filter); + if (files == null || files.length == 0) + return "Not found"; + + FilenameFilter filter2 = (directory, filename) -> filename.startsWith(String.valueOf(idDest)); + File[] files2 = dirDestFile.listFiles(filter2); + if (files2 != null) { + for (File file : files2) { + //noinspection ResultOfMethodCallIgnored + file.delete(); + } + } + + for (File file : files) { + //noinspection ResultOfMethodCallIgnored + file.renameTo(new File(dirDestFile, + file.getName().replaceFirst(String.valueOf(idSrc), String.valueOf(idDest)))); + } + + return "Ok"; + }); + } + public static Future replacePhoto(long id, byte[] input, String media, String dir) { return CompletableFuture.supplyAsync(() -> { + if (input == null || input.length == 0) + return "OK"; + try (InputStream is = new BufferedInputStream(new ByteArrayInputStream(input))) { String mimeType; try { @@ -76,7 +115,12 @@ public class Utils { } public static Uni getMediaFile(long id, String media, String dirname, - Uni uniBase) throws URISyntaxException { + Uni uniBase) throws URISyntaxException { + return getMediaFile(id, media, dirname, null, uniBase); + } + + public static Uni getMediaFile(long id, String media, String dirname, String out_filename, + Uni uniBase) throws URISyntaxException { Future> future = CompletableFuture.supplyAsync(() -> { FilenameFilter filter = (directory, filename) -> filename.startsWith(String.valueOf(id)); File[] files = new File(media, dirname).listFiles(filter); @@ -104,8 +148,10 @@ public class Utils { resp.type(MediaType.APPLICATION_OCTET_STREAM); resp.header(HttpHeaders.CONTENT_LENGTH, filePair.getValue().length); resp.header(HttpHeaders.CONTENT_TYPE, mimeType); - resp.header(HttpHeaders.CONTENT_DISPOSITION, "inline; "); + resp.header(HttpHeaders.CONTENT_DISPOSITION, + "inline; " + ((out_filename == null) ? "" : "filename=\"" + out_filename + "\"")); + System.out.println("getMediaFile: " + mimeType); return resp.build(); })); } diff --git a/src/main/webapp/src/pages/DemandeAff.jsx b/src/main/webapp/src/pages/DemandeAff.jsx index e50c621..b981aca 100644 --- a/src/main/webapp/src/pages/DemandeAff.jsx +++ b/src/main/webapp/src/pages/DemandeAff.jsx @@ -137,7 +137,7 @@ function AssoInfo() {
N° SIRET* - setSiret(e.target.value)} defaultValue={500213731}/>
- + + avatar +
+
+ + +
+ +
+
+
+ + + + + +
+
Laissez vide pour ne rien changer.
+
+ {data.members.map((member, index) => { return
@@ -75,6 +180,12 @@ function Content({data}) { })}
+
+
+ + +
+
@@ -83,17 +194,30 @@ function Content({data}) { function MemberPart({index, member}) { const [mode, setMode] = useState(member.licence >= 0 ? 0 : 2) const [current, setCurrent] = useState(-1) + const [email, setEmail] = useState("") + const [emailChoice, setEmailChoice] = useState("0") useEffect(() => { if (mode !== 1) setCurrent(-1) + if (mode === 2) { + setEmail("") + setEmailChoice("0") + } }, [mode]); + useEffect(() => { + if (mode !== 2 && email !== "") { + setEmailChoice("1") + } + }, [email]); + return
Membre n°{index + 1}
+
- +
@@ -106,7 +230,7 @@ function MemberPart({index, member}) {
- +
@@ -120,8 +244,10 @@ function MemberPart({index, member}) {
+ - +
@@ -135,19 +261,39 @@ function MemberPart({index, member}) {
- - - + +
+ +
+
+ setEmailChoice(e.target.value)}/> +
+ Nouvel email + +
+
+
+ setEmailChoice(e.target.value)}/> +
+ Conserver l'ancien email + +
} -function MemberLicence({member, mode, index}) { +function MemberLicence({member, mode, index, setEmail}) { const setLoading = useLoadingSwitcher() const [licence, setLicence] = useState(member.licence) const {data, refresh} = useFetch(null, setLoading, 1) @@ -160,12 +306,24 @@ function MemberLicence({member, mode, index}) { ref.current = licence }, [licence]); - const name = "m" + (index + 1) + "licence"; + + useEffect(() => { + if (data && mode === 0) { + if (data) + setEmail(data.email) + else + setEmail("") + } else if (mode === 0) + setEmail("") + }, [data, mode]); + + const name = "licence" + index; return <> +
Licence - = 0 ? String(licence) : ""} disabled={mode !== 0} required={mode === 0} onChange={event => setLicence(event.target.value)}/>
@@ -174,11 +332,22 @@ function MemberLicence({member, mode, index}) { } -function MemberSimilar({member, current, setCurrent, mode}) { +function MemberSimilar({member, current, setCurrent, mode, index, setEmail}) { const setLoading = useLoadingSwitcher() const {data} = useFetch(`/member/find/similar?fname=${encodeURI(member.fname)}&lname=${encodeURI(member.lname)}`, setLoading, 1) + useEffect(() => { + if (data && current >= 0 && mode === 1) { + if (data[current]) + setEmail(data[current].email) + else + setEmail("") + } else if (mode === 1) + setEmail("") + }, [current, data, mode]); + return
+ = 0) ? data[current].licence : ""} readOnly hidden/> {data && data.map((m, index) => { return