Compare commits

..

No commits in common. "4856326b29449c35861eaf66aa8c88d6c9385d02" and "dc5ab9c3c4bccdc2293640a0c3a13b14c111e25a" have entirely different histories.

8 changed files with 144 additions and 173 deletions

View File

@ -355,25 +355,19 @@ public class AffiliationService {
.map(c -> club)); .map(c -> club));
}) })
.call(club -> reactiveMailer.send( .call(club -> reactiveMailer.send(
Mail.withHtml(form.getM1_email(), "FFSAF - Creation de votre compte sur l'intranet", Mail.withText(form.getM1_email(),
String.format(Utils.HTML_HEADER, "FFSAF - Creation de votre compte sur l'intranet") + "FFSAF - Acceptation de votre demande d'affiliation",
String.format(""" String.format(
<p>Votre demande d'affiliation pour le club <span class="highlight">%s</span> a été acceptée.</p> """
<p>LLe numéro d'affiliation de votre club est le <span class="highlight">%d</span>.</p> Bonjour,
""",
club.getName(), club.getNo_affiliation()) + Votre demande d'affiliation pour le club %s a été acceptée.
Utils.HTML_FOOTER Le numéro d'affiliation de votre club est le %d.
)
.setText(String.format(""" Cordialement,
Bonjour, L'équipe de la FFSAF
""", club.getName(), club.getNo_affiliation())
Votre demande d'affiliation pour le club %s a été acceptée. ).setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("contact@ffsaf.fr")
Le numéro d'affiliation de votre club est le %d.
Cordialement,
L'équipe de la FFSAF
""", club.getName(), club.getNo_affiliation()))
.setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("contact@ffsaf.fr")
.addTo(form.getM2_email(), form.getM3_email()) .addTo(form.getM2_email(), form.getM3_email())
)); ));
} }
@ -464,27 +458,21 @@ public class AffiliationService {
public Uni<?> deleteReqAffiliation(long id, String reason, boolean federationAdmin) { public Uni<?> deleteReqAffiliation(long id, String reason, boolean federationAdmin) {
return repositoryRequest.findById(id) return repositoryRequest.findById(id)
.call(aff -> federationAdmin ? reactiveMailer.send( .call(aff -> federationAdmin ? reactiveMailer.send(
Mail.withHtml(aff.getM1_email(), "FFSAF - Votre demande d'affiliation a été rejetée.", Mail.withText(aff.getM1_email(),
String.format(Utils.HTML_HEADER, "FFSAF - Votre demande d'affiliation a été rejetée.") + "FFSAF - Votre demande d'affiliation a été rejetée.",
String.format(""" String.format(
<p>Votre demande d'affiliation pour le club %s a été rejetée pour la/les raison(s) suivante(s):<br/>%s</p> """
<p>Si vous rencontrez un problème ou si vous avez des questions, n'hésitez pas à nous contacter à l'adresse <a href="mailto:contact@ffsaf.fr">contact@ffsaf.fr</a>.</p> Bonjour,
""",
aff.getName(), reason) + Votre demande d'affiliation pour le club %s a été rejetée pour la/les raison(s) suivante(s):
Utils.HTML_FOOTER %s
)
.setText(String.format(""" Si vous rencontrez un problème ou si vous avez des questions, n'hésitez pas à nous contacter à l'adresse contact@ffsaf.fr.
Bonjour,
Cordialement,
Votre demande d'affiliation pour le club %s a été rejetée pour la/les raison(s) suivante(s): L'équipe de la FFSAF
%s """, aff.getName(), reason)
).setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("contact@ffsaf.fr")
Si vous rencontrez un problème ou si vous avez des questions, n'hésitez pas à nous contacter à l'adresse contact@ffsaf.fr.
Cordialement,
L'équipe de la FFSAF
""", aff.getName(), reason))
.setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("contact@ffsaf.fr")
.addTo(aff.getM2_email(), aff.getM3_email()) .addTo(aff.getM2_email(), aff.getM3_email())
) : Uni.createFrom().nullItem()) ) : Uni.createFrom().nullItem())
.chain(aff -> Panache.withTransaction(() -> repositoryRequest.delete(aff))) .chain(aff -> Panache.withTransaction(() -> repositoryRequest.delete(aff)))

View File

@ -156,32 +156,24 @@ public class KeycloakService {
return oldEmail; return oldEmail;
}).call(oldEmail -> oldEmail == null || !enabled_email ? Uni.createFrom().item("") : }).call(oldEmail -> oldEmail == null || !enabled_email ? Uni.createFrom().item("") :
reactiveMailer.send( reactiveMailer.send(
Mail.withHtml(oldEmail, "FFSAF - Changement de votre adresse email", Mail.withText(oldEmail,
String.format(Utils.HTML_HEADER, "FFSAF - Changement de votre adresse email") + "FFSAF - Changement de votre adresse email",
String.format(""" String.format(
<p>Suite à la modification de votre adresse email fournie lors de votre ()inscription à la FFSAF,<br/> """
vous allez recevoir dans les prochaines minutes un email de vérification de votre nouvelle adresse sur celle-ci.</p> Bonjour,
<p>Ancienne adresse email : <span class="highlight">%s</span><br/>Nouvelle adresse email : <span class="highlight">%s</span></p>
<p>Si vous n'avez pas demandé cette modification, veuillez contacter le support à l'adresse <a href="mailto:support@ffsaf.fr">support@ffsaf.fr</a>.</p> Suite à la modification de votre adresse email fournie lors de votre ()inscription à la FFSAF,
""", vous allez recevoir dans les prochaines minutes un email de vérification de votre nouvelle adresse sur celle-ci.
oldEmail, email) +
Utils.HTML_FOOTER Ancienne adresse email : %s
) Nouvelle adresse email : %s
.setText(String.format("""
Bonjour, Si vous n'avez pas demandé cette modification, veuillez contacter le support à l'adresse support@ffsaf.fr.
Suite à la modification de votre adresse email fournie lors de votre ()inscription à la FFSAF, Cordialement,
vous allez recevoir dans les prochaines minutes un email de vérification de votre nouvelle adresse sur celle-ci. L'équipe de la FFSAF
""", oldEmail, email)
Ancienne adresse email : %s ).setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("support@ffsaf.fr")
Nouvelle adresse email : %s
Si vous n'avez pas demandé cette modification, veuillez contacter le support à l'adresse support@ffsaf.fr.
Cordialement,
L'équipe de la FFSAF
""", oldEmail, email))
.setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("support@ffsaf.fr")
).onFailure().invoke(e -> LOGGER.error("Fail to send email", e))); ).onFailure().invoke(e -> LOGGER.error("Fail to send email", e)));
} }
@ -278,27 +270,93 @@ public class KeycloakService {
.invoke(user -> membreModel.setUserId(user.getId())) .invoke(user -> membreModel.setUserId(user.getId()))
.call(user -> updateRole(user.getId(), List.of("safca_user"), List.of())) .call(user -> updateRole(user.getId(), List.of("safca_user"), List.of()))
.call(user -> enabled_email ? reactiveMailer.send( .call(user -> enabled_email ? reactiveMailer.send(
Mail.withHtml(user.getEmail(), "FFSAF - Creation de votre compte sur l'intranet", Mail.withHtml(user.getEmail(), "FFSAF - Creation de votre compte sur l'intranet", String.format(
String.format(Utils.HTML_HEADER, "Création de votre compte intranet FFSAF") + """
String.format( <!DOCTYPE html>
""" <html data-lt-installed="true">
<p>Suite &agrave; votre premi&egrave;re inscription <span class="highlight">%s</span> &agrave; la <strong>F&eacute;d&eacute;ration France Soft Armored Fighting (FFSAF)</strong>, votre compte intranet a &eacute;t&eacute; cr&eacute;&eacute;.</p>
<p>Ce compte vous permettra de :</p> <head>
<ul> <meta charset="UTF-8">
<li>Consulter vos informations personnelles,</li> <title>Création de votre compte intranet FFSAF</title>
<li>Vous inscrire aux comp&eacute;titions (bient&ocirc;t disponible),</li> <style>
<li>Consulter les r&eacute;sultats des comp&eacute;titions.</li> body {
</ul> font-family: Arial, sans-serif;
<p>L&rsquo;intranet est accessible &agrave; l&rsquo;adresse suivante :</p> line-height: 1.6;
<p style="text-align: center;"><a href="https://intra.ffsaf.fr" class="button">Acc&eacute;der &agrave; l&apos;intranet</a></p> color: #333;
<p>Votre nom d&rsquo;utilisateur est : <span class="highlight">%s</span></p> max-width: 600px;
<p>Pour d&eacute;finir votre mot de passe, rendez-vous sur l&rsquo;intranet : <strong>Connexion &gt; Mot de passe oubli&eacute; ?</strong></p> margin: 0 auto;
<p>Si vous n&rsquo;avez pas demand&eacute; cette inscription, veuillez contacter le support &agrave; l&rsquo;adresse : <a href="mailto:support@ffsaf.fr">support@ffsaf.fr</a>.</p> padding: 20px;
<p class="footer">(Pas de panique, nous ne vous enverrons pas de message autre que ceux concernant votre compte.)</p> }
""",
membreModel.getRole() == RoleAsso.MEMBRE ? "par votre club (" + membreModel.getClub() .header {
.getName() + ") " : "", user.getUsername()) + background-color: #003366;
Utils.HTML_FOOTER color: white;
padding: 20px;
text-align: center;
border-radius: 5px 5px 0 0;
}
.content {
padding: 20px;
background-color: #f9f9f9;
border-radius: 0 0 5px 5px;
border: 1px solid #ddd;
border-top: none;
}
.button {
display: inline-block;
padding: 10px 20px;
background-color: #003366;
color: white !important;
text-decoration: none;
border-radius: 5px;
margin: 15px 0;
}
.footer {
margin-top: 20px;
font-size: 0.9em;
color: #666;
text-align: center;
}
.highlight {
font-weight: bold;
color: #003366;
}
</style>
</head>
<body data-gramm="false" data-lt-tmp-id="lt-957854">
<div class="header">
<div><img src="https://intra.ffsaf.fr/Logo-FFSAF-2023.png" alt="ffsaf" height="128">
<h1>F&eacute;d&eacute;ration France Soft Armored Fighting</h1>
</div>
</div>
<div class="content">
<p>Bonjour,</p>
<p>Suite &agrave; votre premi&egrave;re inscription <span class="highlight">%s</span> &agrave; la <strong>F&eacute;d&eacute;ration France Soft Armored Fighting (FFSAF)</strong>, votre compte intranet a &eacute;t&eacute; cr&eacute;&eacute;.</p>
<p>Ce compte vous permettra de :</p>
<ul>
<li>Consulter vos informations personnelles,</li>
<li>Vous inscrire aux comp&eacute;titions (bient&ocirc;t disponible),</li>
<li>Consulter les r&eacute;sultats des comp&eacute;titions.</li>
</ul>
<p>L&rsquo;intranet est accessible &agrave; l&rsquo;adresse suivante :</p>
<p style="text-align: center;"><a href="https://intra.ffsaf.fr" class="button">Acc&eacute;der &agrave; l&apos;intranet</a></p>
<p>Votre nom d&rsquo;utilisateur est : <span class="highlight">%s</span></p>
<p>Pour d&eacute;finir votre mot de passe, rendez-vous sur l&rsquo;intranet : <strong>Connexion &gt; Mot de passe oubli&eacute; ?</strong></p>
<p>Si vous n&rsquo;avez pas demand&eacute; cette inscription, veuillez contacter le support &agrave; l&rsquo;adresse : <a href="mailto:support@ffsaf.fr">support@ffsaf.fr</a>.</p>
<p class="footer">(Pas de panique, nous ne vous enverrons pas de message autre que ceux concernant votre compte.)</p>
<p>Cordialement,<br>L&rsquo;&eacute;quipe de la FFSAF</p>
</div>
</body>
</html>
""",
membreModel.getRole() == RoleAsso.MEMBRE ? "par votre club (" + membreModel.getClub()
.getName() + ") " : "", user.getUsername())
) )
.setText(String.format( .setText(String.format(
""" """

View File

@ -21,79 +21,6 @@ import java.util.concurrent.Future;
public class Utils { public class Utils {
private static final Logger LOGGER = Logger.getLogger(Utils.class); private static final Logger LOGGER = Logger.getLogger(Utils.class);
public static String HTML_HEADER = """
<!DOCTYPE html>
<html data-lt-installed="true">
<head>
<meta charset="UTF-8">
<title>%s</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
background-color: #003366;
color: white;
padding: 20px;
text-align: center;
border-radius: 5px 5px 0 0;
}
.content {
padding: 20px;
background-color: #f9f9f9;
border-radius: 0 0 5px 5px;
border: 1px solid #ddd;
border-top: none;
}
.button {
display: inline-block;
padding: 10px 20px;
background-color: #003366;
color: white !important;
text-decoration: none;
border-radius: 5px;
margin: 15px 0;
}
.footer {
margin-top: 20px;
font-size: 0.9em;
color: #666;
text-align: center;
}
.highlight {
font-weight: bold;
color: #003366;
}
</style>
</head>
<body data-gramm="false" data-lt-tmp-id="lt-957854">
<div class="header">
<div><img src="https://intra.ffsaf.fr/Logo-FFSAF-2023.png" alt="ffsaf" height="128">
<h1>F&eacute;d&eacute;ration France Soft Armored Fighting</h1>
</div>
</div>
<div class="content">
<p>Bonjour,</p>
""";
public static String HTML_FOOTER = """
<p>Cordialement,<br>L&rsquo;&eacute;quipe de la FFSAF</p>
</div>
</body>
</html>
""";
public static int getSaison() { public static int getSaison() {
return getSaison(new Date()); return getSaison(new Date());
} }

View File

@ -292,10 +292,9 @@ public class RMatch {
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid"))) if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException("Permission denied"); throw new DForbiddenException("Permission denied");
})) }))
.call(cm -> Panache.withTransaction( .call(cm -> matchRepository.delete("id IN ?1 AND category = ?2", data.matchesToRemove, cm)
() -> matchRepository.delete("id IN ?1 AND category = ?2", data.matchesToRemove, cm))
.call(__ -> SSMatch.sendDeleteMatch(connection, data.matchesToRemove))) .call(__ -> SSMatch.sendDeleteMatch(connection, data.matchesToRemove)))
.call(cm -> Panache.withSession(() -> matchRepository.list("id IN ?1 AND category = ?2", .call(cm -> matchRepository.list("id IN ?1 AND category = ?2",
Stream.concat(data.matchOrderToUpdate.keySet().stream(), Stream.concat(data.matchOrderToUpdate.keySet().stream(),
data.matchPouleToUpdate.keySet().stream()) data.matchPouleToUpdate.keySet().stream())
.distinct().toList(), cm) .distinct().toList(), cm)
@ -306,14 +305,13 @@ public class RMatch {
model.setCategory_ord(data.matchOrderToUpdate.get(model.getId())); model.setCategory_ord(data.matchOrderToUpdate.get(model.getId()));
})) }))
.call(mm -> Panache.withTransaction(() -> matchRepository.persist(mm))) .call(mm -> Panache.withTransaction(() -> matchRepository.persist(mm)))
.invoke(mm -> matches.addAll(mm.stream().map(MatchEntity::fromModel).toList()))) .invoke(mm -> matches.addAll(mm.stream().map(MatchEntity::fromModel).toList()))
) )
.chain(categoryModel -> { .chain(categoryModel -> {
Uni<List<MatchModel>> uni = Uni.createFrom().item(new ArrayList<>()); Uni<List<MatchModel>> uni = Uni.createFrom().item(new ArrayList<>());
for (AddMatch match : data.newMatch) for (AddMatch match : data.newMatch)
uni = uni.call(l -> creatMatch(categoryModel, match).invoke(l::add)); uni = uni.call(l -> creatMatch(categoryModel, match).invoke(l::add));
Uni<List<MatchModel>> finalUni = uni; return uni;
return Panache.withSession(() -> finalUni);
} }
) )
.chain(mm -> Panache.withTransaction(() -> matchRepository.create(mm)) .chain(mm -> Panache.withTransaction(() -> matchRepository.create(mm))

View File

@ -299,7 +299,7 @@ function AssoInfo({initData, needFile}) {
<div className="mb-3"> <div className="mb-3">
<div className="input-group"> <div className="input-group">
<label className="input-group-text" htmlFor="status">Statuts{needFile && "*"}</label> <label className="input-group-text" htmlFor="status">Statue{needFile && "*"}</label>
<input type="file" className="form-control" id="status" name="status" accept=".pdf,.txt" required={needFile}/> <input type="file" className="form-control" id="status" name="status" accept=".pdf,.txt" required={needFile}/>
</div> </div>
{!needFile && <div className="form-text" id="status">Laissez vide pour ne rien changer. (Si un statu a déjà été envoyé lors de cette {!needFile && <div className="form-text" id="status">Laissez vide pour ne rien changer. (Si un statu a déjà été envoyé lors de cette
@ -313,7 +313,7 @@ function MembreInfo({role, initData}) {
return <> return <>
<div className="input-group mb-3"> <div className="input-group mb-3">
<label className="input-group-text" htmlFor="inputGroupSelect01">Rôle</label> <label className="input-group-text" htmlFor="inputGroupSelect01">Rôles</label>
<select className="form-select" id="inputGroupSelect01" defaultValue={initData.role ? initData.role : (role === "m1" ? "PRESIDENT" : 0)} <select className="form-select" id="inputGroupSelect01" defaultValue={initData.role ? initData.role : (role === "m1" ? "PRESIDENT" : 0)}
disabled={initData.role ? initData.role === "PRESIDENT" : role === "m1"} name={role + "_role"} required> disabled={initData.role ? initData.role === "PRESIDENT" : role === "m1"} name={role + "_role"} required>
<option value="0">Sélectionner...</option> <option value="0">Sélectionner...</option>

View File

@ -155,8 +155,8 @@ function ModalContent2({clubData, data}) {
aria-label="Close"></button> aria-label="Close"></button>
</div> </div>
<div className="modal-body"> <div className="modal-body">
<p>Veuillez sélectionner 0 à 3 membres du bureau pour remplir la pré-demande. (Si un membre non-bureau va le devenir l'an prochain, <p>Veuillez sélectionner 3 membres du bureau pour remplir la pré-demande. (Si un membre non-bureau va le devenir l'an prochain,
vous pourrez les renseigner à la prochaine étape)</p> vous pourrez toujours remplacer un des membres sélectionné à la prochaine étape)</p>
{data && data.map((d, index) => { {data && data.map((d, index) => {
return <div key={index} className="input-group mb-1"> return <div key={index} className="input-group mb-1">
<div className="input-group-text"> <div className="input-group-text">

View File

@ -116,7 +116,7 @@ export function CategoryContent({cat, catId, setCat}) {
const activeMatches = matches2.filter(m => m.poule !== '-') const activeMatches = matches2.filter(m => m.poule !== '-')
const combsIDs = matches2.flatMap(d => [d.c1, d.c2]).filter((v, i, a) => v != null && a.indexOf(v) === i) const combsIDs = activeMatches.flatMap(d => [d.c1, d.c2]).filter((v, i, a) => v != null && a.indexOf(v) === i)
.map(d => { .map(d => {
let poule = activeMatches.find(m => (m.c1 === d || m.c2 === d) && m.categorie_ord !== -42)?.poule let poule = activeMatches.find(m => (m.c1 === d || m.c2 === d) && m.categorie_ord !== -42)?.poule
if (!poule) if (!poule)

View File

@ -111,11 +111,11 @@ function Home2({perm}) {
<h4 className="col-auto" style={{margin: "auto 0"}}>Sélectionne les modes d'affichage</h4> <h4 className="col-auto" style={{margin: "auto 0"}}>Sélectionne les modes d'affichage</h4>
<div className="col"> <div className="col">
{perm === "ADMIN" && <> {perm === "ADMIN" && <>
<button className="btn btn-primary" onClick={() => nav("table")}>Secrétariats de lice</button> <button className="btn btn-primary" onClick={() => nav("table")}>Table de marque</button>
<button className="btn btn-primary ms-3" onClick={() => nav("admin")}>Administration</button> <button className="btn btn-primary ms-3" onClick={() => nav("admin")}>Administration</button>
</>} </>}
{perm === "TABLE" && <> {perm === "TABLE" && <>
<button className="btn btn-primary" onClick={() => nav("table")}>Secrétariats de lice</button> <button className="btn btn-primary" onClick={() => nav("table")}>Table de marque</button>
</>} </>}
</div> </div>
</div> </div>