dev #94
@ -67,8 +67,15 @@ public class CompetitionModel {
|
||||
@Column(name = "table_")
|
||||
List<String> table = new ArrayList<>();
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
String data1;
|
||||
@Column(columnDefinition = "TEXT")
|
||||
String data2;
|
||||
@Column(columnDefinition = "TEXT")
|
||||
String data3;
|
||||
@Column(columnDefinition = "TEXT")
|
||||
String data4;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
String config;
|
||||
}
|
||||
|
||||
@ -98,7 +98,13 @@ public class CompetitionService {
|
||||
Cache cacheNoneAccess;
|
||||
|
||||
public Uni<CompetitionData> getById(SecurityCtx securityCtx, Long id) {
|
||||
return permService.hasViewPerm(securityCtx, id).map(CompetitionData::fromModelLight);
|
||||
return permService.hasViewPerm(securityCtx, id).map(cm -> {
|
||||
CompetitionData out = CompetitionData.fromModelLight(cm);
|
||||
if (cm.getAdmin() != null) {
|
||||
out.setCanEditRegisters(cm.getAdmin().stream().anyMatch(u -> u.equals(securityCtx.getSubject())));
|
||||
}
|
||||
return out;
|
||||
});
|
||||
}
|
||||
|
||||
public Uni<CompetitionData> getByIdAdmin(SecurityCtx securityCtx, Long id) {
|
||||
@ -106,7 +112,8 @@ public class CompetitionService {
|
||||
return Uni.createFrom()
|
||||
.item(new CompetitionData(null, "", "", "", "", new Date(), new Date(),
|
||||
CompetitionSystem.INTERNAL, RegisterMode.FREE, new Date(), new Date(), true,
|
||||
null, "", "", null, true, "", "", "", ""));
|
||||
null, "", "", null, true, true,
|
||||
"", "", "", "", "{}"));
|
||||
}
|
||||
return permService.hasAdminViewPerm(securityCtx, id)
|
||||
.chain(competitionModel -> Mutiny.fetch(competitionModel.getInsc())
|
||||
@ -129,6 +136,14 @@ public class CompetitionService {
|
||||
out.addAll(cm.stream().map(CompetitionData::fromModelLight).toList());
|
||||
out.forEach(competition -> competition.setCanEdit(true));
|
||||
}))
|
||||
.chain(ids -> repository.list("id NOT IN ?1 AND ?2 IN admin", ids, securityCtx.getSubject())
|
||||
.map(cm -> {
|
||||
out.addAll(cm.stream().map(CompetitionData::fromModelLight).toList());
|
||||
out.forEach(competition -> competition.setCanEditRegisters(true));
|
||||
List<Long> ids2 = new ArrayList<>(ids);
|
||||
ids2.addAll(cm.stream().map(CompetitionModel::getId).toList());
|
||||
return ids2;
|
||||
}))
|
||||
.call(ids ->
|
||||
repository.list("id NOT IN ?1 AND (publicVisible = TRUE OR registerMode IN ?2)", ids,
|
||||
securityCtx.isClubAdmin() ? List.of(RegisterMode.FREE, RegisterMode.HELLOASSO,
|
||||
@ -165,8 +180,9 @@ public class CompetitionService {
|
||||
public Uni<List<CompetitionData>> getAllSystemTable(SecurityCtx securityCtx,
|
||||
CompetitionSystem system) {
|
||||
return repository.list("system = ?1", system)
|
||||
.chain(l -> Uni.join().all(l.stream().map(cm -> permService.hasTablePerm(securityCtx, cm)).toList())
|
||||
.andCollectFailures())
|
||||
.chain(l -> Uni.join().all(l.stream().map(cm ->
|
||||
permService.hasTablePerm(securityCtx, cm).onFailure().recoverWithNull()
|
||||
).toList()).andCollectFailures())
|
||||
.map(l -> l.stream().filter(Objects::nonNull).map(CompetitionData::fromModel).toList());
|
||||
}
|
||||
|
||||
@ -513,6 +529,64 @@ public class CompetitionService {
|
||||
.call(__ -> cache.invalidate(id));
|
||||
}
|
||||
|
||||
public Uni<SimpleCompetData> getInternalData(SecurityCtx securityCtx, Long id) {
|
||||
return permService.hasEditPerm(securityCtx, id)
|
||||
.invoke(Unchecked.consumer(cm -> {
|
||||
if (cm.getSystem() != CompetitionSystem.INTERNAL)
|
||||
throw new DBadRequestException("Competition is not INTERNAL");
|
||||
}))
|
||||
.chain(competitionModel -> {
|
||||
SimpleCompetData data = SimpleCompetData.fromModel(competitionModel);
|
||||
return vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
if (competitionModel.getAdmin() != null)
|
||||
data.setAdmin(
|
||||
competitionModel.getAdmin().stream().map(uuid -> keycloakService.getUserById(uuid))
|
||||
.filter(Optional::isPresent)
|
||||
.map(user -> user.get().getUsername())
|
||||
.toList());
|
||||
if (competitionModel.getTable() != null)
|
||||
data.setTable(
|
||||
competitionModel.getTable().stream().map(uuid -> keycloakService.getUserById(uuid))
|
||||
.filter(Optional::isPresent)
|
||||
.map(user -> user.get().getUsername())
|
||||
.toList());
|
||||
return data;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public Uni<Void> setInternalData(SecurityCtx securityCtx, SimpleCompetData data) {
|
||||
return permService.hasEditPerm(securityCtx, data.getId())
|
||||
.invoke(Unchecked.consumer(cm -> {
|
||||
if (cm.getSystem() != CompetitionSystem.INTERNAL)
|
||||
throw new DBadRequestException("Competition is not INTERNAL");
|
||||
}))
|
||||
.chain(cm -> vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
ArrayList<String> admin = new ArrayList<>();
|
||||
ArrayList<String> table = new ArrayList<>();
|
||||
for (String username : data.getAdmin()) {
|
||||
Optional<UserRepresentation> opt = keycloakService.getUser(username);
|
||||
if (opt.isEmpty())
|
||||
throw new DBadRequestException("User " + username + " not found");
|
||||
admin.add(opt.get().getId());
|
||||
}
|
||||
for (String username : data.getTable()) {
|
||||
Optional<UserRepresentation> opt = keycloakService.getUser(username);
|
||||
if (opt.isEmpty())
|
||||
throw new DBadRequestException("User " + username + " not found");
|
||||
table.add(opt.get().getId());
|
||||
}
|
||||
|
||||
cm.setAdmin(admin);
|
||||
cm.setTable(table);
|
||||
cm.setConfig(data.getConfigForInternal());
|
||||
|
||||
return cm;
|
||||
}))
|
||||
.chain(cm -> Panache.withTransaction(() -> repository.persist(cm)))
|
||||
.replaceWithVoid();
|
||||
}
|
||||
|
||||
public Uni<SimpleCompetData> getSafcaData(SecurityCtx securityCtx, Long id) {
|
||||
return permService.getSafcaConfig(id)
|
||||
.call(Unchecked.function(o -> {
|
||||
|
||||
@ -377,7 +377,11 @@ public class KeycloakService {
|
||||
|
||||
|
||||
public Optional<UserRepresentation> getUser(UUID userId) {
|
||||
UserResource user = keycloak.realm(realm).users().get(userId.toString());
|
||||
return getUserById(userId.toString());
|
||||
}
|
||||
|
||||
public Optional<UserRepresentation> getUserById(String userId) {
|
||||
UserResource user = keycloak.realm(realm).users().get(userId);
|
||||
if (user == null)
|
||||
return Optional.empty();
|
||||
else
|
||||
|
||||
@ -71,6 +71,14 @@ public class CompetitionEndpoints {
|
||||
return service.getSafcaData(securityCtx, id);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}/internalData")
|
||||
@Authenticated
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<SimpleCompetData> getInternalData(@PathParam("id") Long id) {
|
||||
return service.getInternalData(securityCtx, id);
|
||||
}
|
||||
|
||||
|
||||
@GET
|
||||
@Path("all")
|
||||
@ -95,6 +103,14 @@ public class CompetitionEndpoints {
|
||||
return service.setSafcaData(securityCtx, data);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/internalData")
|
||||
@Authenticated
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<?> setInternalData(SimpleCompetData data) {
|
||||
return service.setInternalData(securityCtx, data);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{id}")
|
||||
@Authenticated
|
||||
|
||||
@ -35,10 +35,12 @@ public class CompetitionData {
|
||||
private String owner;
|
||||
private List<SimpleRegister> registers;
|
||||
private boolean canEdit;
|
||||
private boolean canEditRegisters;
|
||||
private String data1;
|
||||
private String data2;
|
||||
private String data3;
|
||||
private String data4;
|
||||
private String config;
|
||||
|
||||
public static CompetitionData fromModel(CompetitionModel model) {
|
||||
if (model == null)
|
||||
@ -47,8 +49,8 @@ public class CompetitionData {
|
||||
return new CompetitionData(model.getId(), model.getName(), model.getDescription(), model.getAdresse(),
|
||||
model.getUuid(), model.getDate(), model.getTodate(), model.getSystem(),
|
||||
model.getRegisterMode(), model.getStartRegister(), model.getEndRegister(), model.isPublicVisible(),
|
||||
model.getClub().getId(), model.getClub().getName(), model.getOwner(), null, false,
|
||||
model.getData1(), model.getData2(), model.getData3(), model.getData4());
|
||||
model.getClub().getId(), model.getClub().getName(), model.getOwner(), null, false, false,
|
||||
model.getData1(), model.getData2(), model.getData3(), model.getData4(), model.getConfig());
|
||||
}
|
||||
|
||||
public static CompetitionData fromModelLight(CompetitionModel model) {
|
||||
@ -58,14 +60,15 @@ public class CompetitionData {
|
||||
CompetitionData out = new CompetitionData(model.getId(), model.getName(), model.getDescription(),
|
||||
model.getAdresse(), "", model.getDate(), model.getTodate(), null,
|
||||
model.getRegisterMode(), model.getStartRegister(), model.getEndRegister(), model.isPublicVisible(),
|
||||
null, model.getClub().getName(), "", null, false,
|
||||
"", "", "", "");
|
||||
null, model.getClub().getName(), "", null, false, false,
|
||||
"", "", "", "", "{}");
|
||||
|
||||
if (model.getRegisterMode() == RegisterMode.HELLOASSO) {
|
||||
out.setData1(model.getData1());
|
||||
out.setData2(model.getData2());
|
||||
}
|
||||
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import fr.titionfire.ffsaf.data.model.CompetitionModel;
|
||||
import fr.titionfire.ffsaf.net2.data.SimpleCompet;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
@ -25,4 +29,41 @@ public class SimpleCompetData {
|
||||
return new SimpleCompetData(compet.id(), compet.show_blason(), compet.show_flag(),
|
||||
new ArrayList<>(), new ArrayList<>());
|
||||
}
|
||||
|
||||
public static SimpleCompetData fromModel(CompetitionModel competitionModel) {
|
||||
if (competitionModel == null)
|
||||
return null;
|
||||
|
||||
boolean show_blason = true;
|
||||
boolean show_flag = false;
|
||||
|
||||
if (competitionModel.getConfig() != null) {
|
||||
try {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
JsonNode rootNode = objectMapper.readTree(competitionModel.getConfig());
|
||||
|
||||
if (rootNode.has("show_blason"))
|
||||
show_blason = rootNode.get("show_blason").asBoolean();
|
||||
if (rootNode.has("show_flag"))
|
||||
show_flag = rootNode.get("show_flag").asBoolean();
|
||||
|
||||
} catch (JsonProcessingException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return new SimpleCompetData(competitionModel.getId(), show_blason, show_flag,
|
||||
new ArrayList<>(), new ArrayList<>());
|
||||
}
|
||||
|
||||
public String getConfigForInternal(){
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
JsonNode rootNode = objectMapper.createObjectNode()
|
||||
.put("show_blason", this.show_blason)
|
||||
.put("show_flag", this.show_flag);
|
||||
try {
|
||||
return objectMapper.writeValueAsString(rootNode);
|
||||
} catch (JsonProcessingException e) {
|
||||
return "{}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,8 +108,8 @@ public class CompetitionWS {
|
||||
.call(cm -> competPermService.hasEditPerm(securityCtx, cm).map(__ -> PermLevel.ADMIN)
|
||||
.onFailure()
|
||||
.recoverWithUni(competPermService.hasTablePerm(securityCtx, cm).map(__ -> PermLevel.TABLE))
|
||||
.onFailure()
|
||||
.recoverWithUni(competPermService.hasViewPerm(securityCtx, cm).map(__ -> PermLevel.VIEW))
|
||||
//.onFailure()
|
||||
//.recoverWithUni(competPermService.hasViewPerm(securityCtx, cm).map(__ -> PermLevel.VIEW))
|
||||
.invoke(prem -> connection.userData().put(UserData.TypedKey.forString("prem"), prem.toString()))
|
||||
.invoke(prem -> LOGGER.infof("Connection permission: %s", prem))
|
||||
.onFailure().transform(t -> new ForbiddenException()))
|
||||
|
||||
@ -51,9 +51,11 @@ export function CompetitionEdit() {
|
||||
<Content data={data} refresh={refresh}/>
|
||||
|
||||
{data.id !== null && <button style={{marginBottom: "1.5em", width: "100%"}} className="btn btn-primary"
|
||||
onClick={_ => navigate(`/competition/${data.id}/register?type=${data.registerMode}`)}>Voir/Modifier les participants</button>}
|
||||
onClick={_ => navigate(`/competition/${data.id}/register?type=${data.registerMode}`)}>Voir/Modifier
|
||||
les participants</button>}
|
||||
|
||||
{data.id !== null && data.system === "SAFCA" && <ContentSAFCA data2={data}/>}
|
||||
{data.id !== null && (data.system === "SAFCA" || data.system === "INTERNAL") &&
|
||||
<ContentSAFCAAndInternal data2={data} type={data.system}/>}
|
||||
|
||||
{data.id !== null && <>
|
||||
<div className="col" style={{textAlign: 'right', marginTop: '1em'}}>
|
||||
@ -72,9 +74,12 @@ export function CompetitionEdit() {
|
||||
</>
|
||||
}
|
||||
|
||||
function ContentSAFCA({data2}) {
|
||||
function ContentSAFCAAndInternal({data2, type = "SAFCA"}) {
|
||||
const getDataPath = type === "SAFCA" ? `/competition/${data2.id}/safcaData` : `/competition/${data2.id}/internalData`
|
||||
const setDataPath = type === "SAFCA" ? "/competition/safcaData" : "/competition/internalData"
|
||||
|
||||
const setLoading = useLoadingSwitcher()
|
||||
const {data, error} = useFetch(`/competition/${data2.id}/safcaData`, setLoading, 1)
|
||||
const {data, error} = useFetch(getDataPath, setLoading, 1)
|
||||
|
||||
const [state, dispatch] = useReducer(SimpleReducer, [])
|
||||
const [state2, dispatch2] = useReducer(SimpleReducer, [])
|
||||
@ -109,7 +114,7 @@ function ContentSAFCA({data2}) {
|
||||
out['table'] = state2.map(d => d.data)
|
||||
|
||||
toast.promise(
|
||||
apiAxios.post(`/competition/safcaData`, out),
|
||||
apiAxios.post(setDataPath, out),
|
||||
{
|
||||
pending: "Enregistrement des paramètres en cours",
|
||||
success: "Paramètres enregistrée avec succès 🎉",
|
||||
@ -402,7 +407,8 @@ function Content({data}) {
|
||||
<li><strong>Configurer l'url de notification : </strong>afin que nous puissions recevoir une notification à
|
||||
chaque inscription, il est nécessaire de configurer l'url de notification de votre compte HelloAsso pour
|
||||
qu'il redirige vers "https://intra.ffsaf.fr/api/webhook/ha". Pour ce faire, depuis la page d'accueil de
|
||||
votre association sur HelloAsso, allez dans <strong>Mon compte</strong> > <strong>Paramètres</strong> >
|
||||
votre association sur HelloAsso, allez dans <strong>Mon compte</strong> >
|
||||
<strong>Paramètres</strong> >
|
||||
<strong> Intégrations et API</strong> section Notification et copier-coller <strong>https://intra.ffsaf.fr/api/webhook/ha </strong>
|
||||
dans le champ <strong>Mon URL de callback</strong> et enregister.
|
||||
<img src="/img/HA-help-4.png" alt="" className="img-fluid" style={{objectFit: "contain"}}/></li>
|
||||
|
||||
@ -79,6 +79,13 @@ function MakeContent({data}) {
|
||||
href={`https://www.helloasso.com/associations/${data.data1}/evenements/${data.data2}`} target="_blank"
|
||||
rel="noopener noreferrer">{`https://www.helloasso.com/associations/${data.data1}/evenements/${data.data2}`}</a></p>
|
||||
}
|
||||
{data.canEditRegisters &&
|
||||
<div style={{marginTop: "0.5em"}}>
|
||||
<button type="button" className="btn btn-primary"
|
||||
onClick={_ => navigate("/competition/" + data.id + "/register")}>Inscription - mode administrateur
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -342,7 +342,7 @@ function CategoryHeader({cat, setCatId}) {
|
||||
}, [cats]);
|
||||
|
||||
useEffect(() => {
|
||||
if (cats && cats.length > 0 && !cat || (cats && !cats.find(c => c.id === cat.id))) {
|
||||
if (cats && cats.length > 0 && (!cat || (cats && !cats.find(c => c.id === cat.id)))) {
|
||||
setCatId(cats.sort((a, b) => a.name.localeCompare(b.name))[0].id);
|
||||
} else if (cats && cats.length === 0) {
|
||||
setModal({});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user