225 Commits

Author SHA1 Message Date
2772e44456 feat: auto fill certificate fields on add licence
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 11m30s
2026-08-06 16:16:47 +02:00
8f0e3c0738 feat: show old member by default 2026-08-06 15:14:30 +02:00
81f6169d41 feat: add licence detail to add log 2026-08-06 15:03:03 +02:00
931c5076b7 feat: update libs 2026-08-06 15:02:44 +02:00
a328e71035 feat: add licence state to membre find result 2026-08-05 16:45:18 +02:00
d3805ea0e7 fix: cache refresh v2
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m13s
2026-03-23 22:11:42 +01:00
2d883ca1da fix: cache refresh
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m10s
2026-03-23 16:29:11 +01:00
2501b998fa fix: add log in getAllClubArray
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m8s
2026-03-23 16:05:09 +01:00
a7c89dbd73 hot fix: remove save score button 2
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m55s
2026-03-21 12:08:45 +01:00
b2ee06d3b9 hot fix: remove save score button
Some checks failed
Deploy Production Server / if_merged (pull_request) Failing after 44s
2026-03-21 11:59:37 +01:00
3ed44bc044 fix: change obs lice name location
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m57s
2026-03-20 16:40:16 +01:00
d091503f19 fix: clear pub aff when no cat select
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m3s
2026-03-20 16:10:59 +01:00
ea17989a82 fix: change methode for move upload file
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m59s
2026-03-18 13:07:39 +01:00
e8555f7992 fix: add more log for file copy
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m0s
2026-03-18 11:06:40 +01:00
fdd42c6a63 fix: add log for file copy
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m1s
2026-03-18 10:48:09 +01:00
ddf530ce4c fix: obs support wss
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m8s
2026-03-17 23:16:06 +01:00
286911cf89 fix: language detection
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m2s
2026-03-16 12:19:30 +01:00
4bfa6e845d fix: club guest mixing in result
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m3s
2026-03-10 13:59:35 +01:00
806ead5b98 fix: eslint version
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m29s
2026-03-09 13:19:12 +01:00
ee0a7d87e9 feat: cm Add audio encodeur generator
Some checks failed
Deploy Production Server / if_merged (pull_request) Failing after 34s
2026-03-09 13:07:51 +01:00
812d873d5d fix: Match reorder refresh 2026-03-08 21:58:57 +01:00
d6d9f86254 feat: PDF for classement club 2026-02-27 11:10:31 +01:00
a22225ee7a feat: classement club 2026-02-23 12:01:36 +01:00
2f390b03e2 feat: add podium PDF
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m28s
2026-02-19 15:55:46 +01:00
ed5d73c25f feat: improve generation PDF for categories 2026-02-17 21:35:29 +01:00
0a368454c4 fix: duplicate id liceInput1 2026-02-16 22:49:49 +01:00
752f03cba5 feat: PDF for categories 2026-02-16 22:43:14 +01:00
d857fce71f feat: support decimal on weight 2026-02-13 21:42:48 +01:00
2fd09af0ea feat: auto categories full competition 2026-02-13 21:33:19 +01:00
d43cdc1a4e feat: auto categories 2026-02-11 14:08:13 +01:00
8663aa61cf feat: add silver cup png to cm 2026-02-06 18:02:11 +01:00
758e02dc5b feat: remove ^/api$ path log 2026-02-06 17:26:33 +01:00
31315c951a feat: auto poule
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m28s
2026-02-06 17:17:44 +01:00
ab4d57985c fix: add classement array in competition.js 2026-02-06 13:23:09 +01:00
2e846bf801 feat: use preset in cmt 2026-02-06 13:15:18 +01:00
9954dd002c feat: add config for preset in category + filter on select comb 2026-02-05 23:53:49 +01:00
7dab5b8880 fix: tree detection on cat edit 2026-02-05 22:32:31 +01:00
b37902466c feat: add team card when comb is register 2026-02-05 22:07:14 +01:00
89d9e04a6f feat: add classement match system 2026-02-05 21:29:27 +01:00
e2197d0712 fix: score panel fonction update 2026-02-01 15:25:49 +01:00
172dcdaa67 fix: ghost selection in PayAndValidateList
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m59s
2026-01-30 15:00:32 +01:00
cdd7221e86 fix: add weight config for all register source 2026-01-30 14:54:44 +01:00
952300d063 feat: card in result view 2026-01-30 14:50:21 +01:00
541d3824f3 feat: update CardModel 2026-01-30 11:58:31 +01:00
4c260b86b9 feat: remove old card system 2026-01-29 22:43:59 +01:00
9018ecef12 feat: card for team 2026-01-29 22:35:54 +01:00
d749dea6f4 feat: add return of table state 2026-01-27 22:11:49 +01:00
189eb135bb wip: competition card system 2026-01-26 22:36:26 +01:00
197ee0d5b1 feat: register import
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m49s
2026-01-23 16:48:09 +01:00
3d8597869d feat: update register export 2026-01-22 19:04:08 +01:00
4a07eb4ed9 feat: comp register optional weight config 2026-01-22 15:13:48 +01:00
d2a7e6cbac feat: rework CatPreset for add more detail + add preset 2026-01-21 22:05:58 +01:00
78d22b466d feat: add lock picto and hide team on register admin + show registration information on user page 2026-01-20 22:53:47 +01:00
b419e2f58d feat: add category registration system to club and user 2026-01-20 21:49:10 +01:00
a5d3973394 feat: add category registration system to admin 2026-01-20 20:21:34 +01:00
4dff0940c1 feat: add docker healthcheck for api 2026-01-18 21:10:20 +01:00
eed7a10cfb feat: select comb double clic support and add background to already select
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m43s
2026-01-18 15:26:16 +01:00
1cbbde6506 feat: add team support to result 2026-01-18 15:10:38 +01:00
e8d5d0fa0c feat: add team support to cm 2026-01-17 22:37:48 +01:00
a5bbc41dfd feat: update js npm lib
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m49s
2026-01-16 16:20:51 +01:00
01436ac220 feat: add match eq support
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m42s
2026-01-16 15:37:23 +01:00
0150c4fac2 fix: translate backend asset includes in native
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m36s
2026-01-16 14:21:46 +01:00
c456fdae8b fix: translate backend add log
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m48s
2026-01-16 13:59:12 +01:00
4bf07cc8f8 Merge pull request 'i18n' (#99) from i18n into master
Reviewed-on: #99
2026-01-16 12:18:53 +00:00
a8356c8663 feat: translate backend
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m23s
2026-01-15 16:32:43 +01:00
c7a2215103 feat: translation competition manager part 2026-01-15 14:08:57 +01:00
46f543abde feat: translation competition part 2026-01-14 18:24:26 +01:00
d8b6f1ad25 feat: translation result 2026-01-14 15:51:15 +01:00
9170157dbb feat: init translation 2026-01-13 21:31:05 +01:00
babbc5d585 Merge pull request 'feat: keep showLicenceState in sessionStorage' (#98) from dev into master
Reviewed-on: #98
2026-01-07 21:10:12 +00:00
51c774a21e feat: keep showLicenceState in sessionStorage
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m52s
2026-01-07 22:09:41 +01:00
9d85b9ae45 Merge pull request 'feat: support ha partial config' (#97) from dev into master
Reviewed-on: #97
2026-01-07 15:35:10 +00:00
0ab0731ebd feat: support ha partial config
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m52s
2026-01-07 16:34:57 +01:00
ec48f73d9f Merge pull request 'feat: support ha partial config' (#96) from dev into master
Reviewed-on: #96
2026-01-07 15:24:44 +00:00
f98f1bd1ac feat: support ha partial config
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m59s
2026-01-07 16:24:19 +01:00
b9d752ac55 Merge pull request 'dev' (#95) from dev into master
Reviewed-on: #95
2026-01-05 16:57:01 +00:00
13650b27c1 feat: add selection
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m50s
2026-01-05 17:56:37 +01:00
0757ae7198 feat: allow admin and club admin to get comp result 2026-01-05 14:20:22 +01:00
a871b52006 feat: use blason setting 2026-01-05 11:09:22 +01:00
78959fc485 feat: cma select comb sort by name 2026-01-04 21:29:42 +01:00
0a91e72c29 feat: autorise null mail for admin add 2026-01-04 21:05:20 +01:00
e3a1d1c50b Merge pull request 'dev' (#94) from dev into master
Reviewed-on: #94
2026-01-03 20:50:18 +00:00
3933954e09 feat: add permission setting to admin page
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m44s
2026-01-03 21:49:36 +01:00
959e356fb9 fix: quick add auto complet 2026-01-03 11:22:04 +01:00
3b5955ff31 fix: rm match with cardboard 2026-01-02 23:44:53 +01:00
e8ee5811a0 feat: avoid ws reconnect when user logged out 2026-01-02 23:12:22 +01:00
12326fe122 Merge pull request 'fix: ws timeout' (#93) from dev into master
Reviewed-on: #93
2026-01-02 21:33:40 +00:00
9624ff93f0 fix: ws timeout
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m42s
2026-01-02 22:33:11 +01:00
5444235512 Merge pull request 'dev' (#92) from dev into master
Reviewed-on: #92
2026-01-02 15:00:08 +00:00
b54eaa2549 fix: remove case sensitive sort result
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m44s
2026-01-02 15:58:59 +01:00
6a7af8aea9 fix: tree order on result api 2026-01-02 15:33:56 +01:00
bc4dbb532f fix: remove extra log on recalculateMatch 2026-01-02 15:13:41 +01:00
0e269e0bf7 Merge pull request 'dev' (#91) from dev into master
Reviewed-on: #91
2026-01-02 14:06:09 +00:00
6d6324e1b4 fix: result on empty category
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m45s
2026-01-02 15:05:30 +01:00
309f2a2268 feat: recalculateMatch add tmp log 2026-01-02 15:03:09 +01:00
13bae3e817 Merge pull request 'dev' (#90) from dev into master
Reviewed-on: #90
2026-01-02 13:42:36 +00:00
c6751b0428 fix: recalculateMatch 2026-01-02 14:42:02 +01:00
237952f95a fix: tree order on cmt 2026-01-02 14:29:11 +01:00
075f01a205 feat: add privacy setting in me page
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m53s
2026-01-02 14:09:55 +01:00
82231744af feat: improve result page design 2026-01-02 13:48:36 +01:00
71a4d98434 fix: club remove 2026-01-02 13:47:54 +01:00
29a1184994 feat: add Result Privacy membre setting 2026-01-01 19:43:25 +01:00
3d07a5bc8e Merge pull request 'fix: rm licence' (#89) from dev into master
Reviewed-on: #89
2025-12-31 15:47:55 +00:00
a730384d37 fix: rm licence
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m42s
2025-12-31 16:47:37 +01:00
412e661319 Merge pull request 'fix: cat graphe color and text alignement' (#88) from dev into master
Reviewed-on: #88
2025-12-31 13:50:57 +00:00
bc72b7912c fix: cat graphe color and text alignement
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m46s
2025-12-31 14:50:15 +01:00
6359e7e027 Merge pull request 'fix: ci cache' (#87) from dev into master
Reviewed-on: #87
2025-12-31 13:15:42 +00:00
c7a9cc221f fix: ci cache
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 9m31s
2025-12-31 14:15:25 +01:00
17c61be171 Merge pull request 'fix: ci cache' (#86) from dev into master
Reviewed-on: #86
2025-12-31 13:12:17 +00:00
76f69c2154 fix: ci cache
Some checks failed
Deploy Production Server / if_merged (pull_request) Failing after 1m30s
2025-12-31 14:11:43 +01:00
639f07ccdc Merge pull request 'fix: ci cache' (#85) from dev into master
Reviewed-on: #85
2025-12-31 13:07:57 +00:00
c02e37862c fix: ci cache
Some checks failed
Deploy Production Server / if_merged (pull_request) Failing after 2s
2025-12-31 14:07:37 +01:00
2b6dc48880 Merge pull request 'dev' (#84) from dev into master
Reviewed-on: #84
2025-12-31 12:59:50 +00:00
6879d9195d fix: remove useless log
Some checks failed
Deploy Production Server / if_merged (pull_request) Failing after 7s
2025-12-31 13:59:33 +01:00
1595fb57c3 update: config + add action cache 2025-12-31 13:50:28 +01:00
8067d496f8 Merge pull request 'Re-update to 3.30.5' (#83) from dev into master
Reviewed-on: #83
2025-12-31 12:20:08 +00:00
aba8d8a202 Re-update to 3.30.5
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 8m10s
2025-12-31 13:19:31 +01:00
38663d97c2 Merge pull request 'Restore pom.xml' (#82) from dev into master
Reviewed-on: #82
2025-12-29 22:47:27 +00:00
377ce70125 Restore pom.xml
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m36s
2025-12-29 23:47:06 +01:00
61cee85335 Merge pull request 'Restore Quarkus 3.16.4' (#81) from dev into master
Reviewed-on: #81
2025-12-29 22:36:34 +00:00
19949d1e35 Restore Quarkus 3.16.4
Some checks failed
Deploy Production Server / if_merged (pull_request) Failing after 2m35s
2025-12-29 23:36:02 +01:00
03cdcf78c8 Merge pull request 'Move to Quarkus 3.20.4' (#80) from dev into master
Reviewed-on: #80
2025-12-29 22:30:24 +00:00
0b4f843084 Move to Quarkus 3.20.4
Some checks failed
Deploy Production Server / if_merged (pull_request) Failing after 2m20s
2025-12-29 23:30:01 +01:00
fa00273c87 Merge pull request 'Move to Quarkus 3.27.1' (#79) from dev into master
Reviewed-on: #79
2025-12-29 22:24:03 +00:00
b2438e52fc Move to Quarkus 3.27.1
Some checks failed
Deploy Production Server / if_merged (pull_request) Failing after 5m4s
2025-12-29 23:23:42 +01:00
31bbe5719f Merge pull request 'dev' (#78) from dev into master
Reviewed-on: #78
2025-12-29 20:54:30 +00:00
bf377c9c5e feat: external website result connector
Some checks failed
Deploy Production Server / if_merged (pull_request) Failing after 2m31s
2025-12-29 21:03:05 +01:00
145d5e7ca8 feat: update quarkus 2025-12-29 15:41:13 +01:00
448e49b9fd feat: add obs connector 2025-12-28 21:22:28 +01:00
b7a1e58436 fix: cma comb dupli on poule list 2025-12-27 17:34:58 +01:00
a73a3562c3 feat: add scrolle to match list in cmt 2025-12-27 17:29:28 +01:00
0dace76fc3 feat: cat remove 2025-12-27 17:06:27 +01:00
f76ca43ed6 feat: add import fail log detail 2025-12-27 16:55:23 +01:00
c6b24a1b20 feat: add fname formatter 2025-12-27 16:43:14 +01:00
c47c04c29e fix: cat update/add refresh 2025-12-27 15:00:50 +01:00
e06df24002 ty-fix: ws timeout 2025-12-27 14:20:10 +01:00
352c34064f fix: score save update 2025-12-27 14:18:38 +01:00
2ccd2a9178 fix: disable match filter on 1 zone cat + rename lice to zone 2025-12-27 13:02:45 +01:00
0ff3ffd66b Merge pull request 'feat: add club_tresorier to club admin' (#77) from dev into master
Reviewed-on: #77
2025-12-26 21:19:10 +00:00
847674d62c feat: add club_tresorier to club admin
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m46s
2025-12-26 22:18:03 +01:00
8896e2a28a Merge pull request 'fix: delect club without user' (#76) from dev into master
Reviewed-on: #76
2025-12-26 13:37:05 +00:00
68b4f8e476 fix: delect club without user
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m40s
2025-12-26 14:35:19 +01:00
d3fb884ab4 Merge pull request 'fix: Me page' (#75) from dev into master
Reviewed-on: #75
2025-12-26 13:16:56 +00:00
81faafe2de fix: Me page
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m46s
2025-12-26 14:16:30 +01:00
ee730be570 Merge pull request 'dev' (#74) from dev into master
Reviewed-on: #74
2025-12-25 19:03:17 +00:00
4856326b29 feat: html mail for all public
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 11m4s
2025-12-25 19:59:47 +01:00
b94f4b43ce fix: reword message + fix missing comb un cat list (cma) 2025-12-25 19:27:15 +01:00
795d044c0b fix:typo 2025-12-25 19:25:56 +01:00
dc5ab9c3c4 feat: add inactif member filter 2025-12-20 18:22:24 +01:00
870891a77c Merge pull request 'dev' (#73) from dev into master
Reviewed-on: #73
2025-12-19 15:14:04 +00:00
7e1356b621 fix: cm auto ask new category on empty competition
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m17s
2025-12-19 16:13:37 +01:00
b689521e56 fix: comb register with null license 2025-12-19 16:02:28 +01:00
e81f475402 Merge pull request 'dev-comp' (#72) from dev into master
Reviewed-on: #72
2025-12-19 13:47:40 +00:00
095a5fcede Merge branch 'dev-comp' into dev
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m20s
2025-12-19 14:43:46 +01:00
73f026210c feat: cm home page 2025-12-19 14:37:24 +01:00
4b969e6d69 feat: add CardPanel 2025-12-19 00:30:35 +01:00
4706af27f8 feat: add tooltip for menu 2025-12-18 19:19:25 +01:00
3e8c19534b feat: add score panel 2025-12-18 19:00:39 +01:00
3decc6d0e0 Merge pull request 'dev' (#71) from dev into master
Reviewed-on: #71
2025-12-18 15:37:19 +00:00
254fd7d73f feat: html email
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m5s
2025-12-18 16:36:30 +01:00
12a75d003c feat: mask inactive club on map 2025-12-18 15:33:45 +01:00
7f957b98f2 fix: deleteLicence Panache missing transaction context 2025-12-18 15:24:32 +01:00
8b2c9613fd Merge pull request 'dev' (#70) from dev into master
Reviewed-on: #70
2025-12-18 14:03:51 +00:00
50b49c40e3 fix: error 500 when license is null
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m6s
2025-12-18 15:03:22 +01:00
47d3921e05 fix: email typo 2025-12-18 14:57:26 +01:00
ecc9753237 Merge pull request 'feat: add cat filter on mass license validation' (#69) from dev into master
Reviewed-on: #69
2025-12-18 13:47:32 +00:00
7c4addd525 feat: add cat filter on mass license validation
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m27s
2025-12-18 14:46:52 +01:00
a1b5ca2694 fix: pubAdd chrono init 2025-12-17 21:00:43 +01:00
c5f7b81ac3 feat: move to MatchPanel 2025-12-17 20:54:04 +01:00
7f999733dc feat: chrono 2025-12-17 20:43:49 +01:00
0ac92fcda3 feat: add public screen 2025-12-15 23:08:46 +01:00
8e01fb46f8 feat: SmartLogoBackground 2025-12-15 21:00:19 +01:00
a8565534e6 update README.md 2025-12-05 15:28:03 +01:00
43fcbb5ef6 Merge pull request 'feat: re-add siret/rna api' (#68) from dev into master
Reviewed-on: #68
2025-11-19 14:31:46 +00:00
fefc5d651b feat: re-add siret/rna api
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m4s
2025-11-19 15:26:36 +01:00
163d9b23e9 Merge pull request 'fix: error on loading logo on maps' (#67) from dev into master
Reviewed-on: #67
2025-11-18 21:02:58 +00:00
53509820c6 fix: error on loading logo on maps
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 9m58s
2025-11-18 22:02:13 +01:00
a8ad3a7558 Merge pull request 'fix: remove siret/rna api' (#66) from dev into master
Reviewed-on: #66
2025-11-18 20:46:34 +00:00
f46e268d39 fix: remove siret/rna api
Some checks failed
Deploy Production Server / if_merged (pull_request) Failing after 1m33s
2025-11-18 21:46:05 +01:00
abad3194a4 Merge pull request 'dev' (#65) from dev into master
Reviewed-on: #65
2025-11-18 19:32:14 +00:00
6e7eec0587 feat: membre list filter history
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m16s
2025-11-18 15:45:18 +01:00
8c83668364 feat: add categorie filter membre search 2025-11-18 14:02:02 +01:00
771c06ccd8 feat: make user-friendly cat name 2025-11-18 13:27:49 +01:00
b479b992cf feat: add ordering for member page 2025-11-17 21:48:24 +01:00
f018e52afa Merge pull request 'dev' (#64) from dev into master
Reviewed-on: #64
2025-11-14 15:55:53 +00:00
be2f01c070 feat: upgrade club groupe name in kc when club is rename
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m13s
2025-11-14 16:38:44 +01:00
9ab50238b9 feat: upgrade aff find to user all id (Rna, siret, siren) 2025-11-14 16:17:40 +01:00
cf5d93630f fix: certifDate NaN all membre export 2025-11-14 14:46:58 +01:00
e5e17d3862 Merge pull request 'fix: null licence on import' (#63) from dev into master
Reviewed-on: #63
2025-11-14 13:32:09 +00:00
7410569ced fix: null licence on import
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m3s
2025-11-14 14:31:37 +01:00
b107f443aa Merge pull request 'fix: add more log to import' (#62) from dev into master
Reviewed-on: #62
2025-11-14 13:15:27 +00:00
8e2d68ebd5 fix: add more log to import
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m10s
2025-11-14 14:14:22 +01:00
f050127fd7 Merge pull request 'dev' (#61) from dev into master
Reviewed-on: #61
2025-11-14 12:45:53 +00:00
d02fd63834 fix: empty mail on import
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m30s
2025-11-14 13:43:51 +01:00
b143cc759f feat: remove kc new account mail 2025-11-12 16:38:35 +01:00
cc5534ef00 feat: add cache to getAssoInfo 2025-11-12 16:15:40 +01:00
26f56006f6 feat: merge rna and siret fields 2025-11-12 14:56:28 +01:00
f8dacee3e7 Merge pull request 'fix: club delete on RegisterModel' (#60) from dev into master
Reviewed-on: #60
2025-11-08 18:09:04 +00:00
ed1f30f2b6 fix: club delete on RegisterModel
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m17s
2025-11-08 19:08:41 +01:00
8517e9824c Merge pull request 'feat: allow empty mail' (#59) from dev into master
Reviewed-on: #59
2025-11-08 17:45:02 +00:00
6cec8ff31d feat: allow empty mail
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m16s
2025-11-08 18:44:35 +01:00
e7deba52e9 Merge pull request 'dev' (#58) from dev into master
Reviewed-on: #58
2025-11-07 15:31:18 +00:00
d95c173fa8 fix: aff renew select length
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m51s
2025-11-07 16:19:49 +01:00
8a0e4423f2 feat: remove saison selection on aff req 2025-11-07 16:13:37 +01:00
b956236934 fix: affiliation ok login msg 2025-11-07 15:57:36 +01:00
7767c98304 feat: add email tooltip 2025-11-07 15:55:10 +01:00
7e380ccb69 feat: add re-login message 2025-11-07 15:31:25 +01:00
94d1148eb1 fix: null email on import 2025-11-07 15:30:56 +01:00
8a14f58ce5 Merge pull request 'fix: log detail' (#57) from dev into master
Reviewed-on: #57
2025-11-05 21:06:54 +00:00
79dbbdaaec fix: log detail
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m27s
2025-11-05 22:06:36 +01:00
bf5704db54 Merge pull request 'fix: log detail' (#56) from dev into master
Reviewed-on: #56
2025-11-05 20:43:26 +00:00
dec98f9508 fix: log detail
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m22s
2025-11-05 21:43:05 +01:00
d3a62e980d Merge pull request 'feat: add aff req log detail' (#55) from dev into master
Reviewed-on: #55
2025-11-05 20:19:10 +00:00
b89ed62795 feat: add aff req log detail
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m33s
fix: membre import null email filter
2025-11-05 21:18:29 +01:00
5ffc9fb495 Merge pull request 'fix: typo' (#54) from dev into master
Reviewed-on: #54
2025-09-05 18:31:45 +00:00
5e48bc4623 fix: typo
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 9m51s
2025-09-05 20:31:10 +02:00
81b953fb05 Merge pull request 'dev' (#53) from dev into master
Reviewed-on: #53
2025-09-03 19:47:13 +00:00
c6659f8d85 feat: keep log
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 10m19s
2025-09-03 21:46:26 +02:00
e86fe42b3d fix: club order 2025-09-03 21:35:36 +02:00
ef528aa524 fix: log message length 2025-09-03 19:53:19 +02:00
e6cc4cbc96 Merge pull request 'dev' (#52) from dev into master
Reviewed-on: #52
2025-08-21 09:43:54 +00:00
c58dedf80a Merge pull request 'competition rework' (#51) from dev into master
Reviewed-on: #51
2025-08-18 09:33:51 +00:00
1908de681e Merge pull request 'dev' (#50) from dev into master
Reviewed-on: #50
2025-08-15 19:49:13 +00:00
76381c75bd Merge pull request 'fix: Membre edition' (#49) from dev into master
Reviewed-on: #49
2025-08-15 18:34:43 +00:00
7d281196c1 Merge pull request 'dev' (#48) from dev into master
Reviewed-on: #48
2025-08-15 16:26:23 +00:00
208 changed files with 21446 additions and 5851 deletions

View File

@@ -17,17 +17,38 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Cache Maven packages
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Cache node modules
id: cache-npm
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
java-version: '17.0.12' java-version: '21'
distribution: 'graalvm' distribution: 'graalvm'
cache: 'maven'
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: '18' node-version: '22'
cache: 'npm' cache: 'npm'
cache-dependency-path: src/main/webapp/package-lock.json cache-dependency-path: src/main/webapp/package-lock.json

1
.gitignore vendored
View File

@@ -50,3 +50,4 @@ nb-configuration.xml
/media/ /media/
/media-ext/ /media-ext/
/sign.jpg /sign.jpg
/.gitea/workflows/test.yml

View File

@@ -1,8 +1,22 @@
# ffsaf-site # FFSAF - Intranet
This project uses Quarkus, the Supersonic Subatomic Java Framework. ## Introduction et context
If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ . Lintranet de la Fédération française de Soft Armored Fighting a pour but centralise la prise de licences des adhérents
et laffiliation des clubs. Il permet aussi de recueillir les résultats des compétitions organisées par les clubs et,
plus récemment, den faciliter lorganisation grâce à un outil intégré.
Le système de prise de licence se divise en trois parties :
* Un formulaire public pour la première demande daffiliation ;
* Un espace club, accessible après validation, pour la saisie des informations relatives aux demandes de licence des
adhérents ;
* Un espace fédération pour accepter les demandes de licences et daffiliation.
Un espace membre permet enfin à chaque adhérent de télécharger son attestation de licence.
Pour les compétitions, le système permet la création de catégories, de poules et de matchs, avec génération automatique
des matchs au sein dune poule. Il supporte plusieurs lices, assure la synchronisation en temps réel des modifications
entre toutes les instances de lapplication web et publie automatiquement les résultats sur le site de lorganisateur.
## Running the application in dev mode ## Running the application in dev mode
@@ -52,34 +66,3 @@ Or, if you don't have GraalVM installed, you can run the native executable build
You can then execute your native executable with: `./target/ffsaf-site-1.0-SNAPSHOT-runner` You can then execute your native executable with: `./target/ffsaf-site-1.0-SNAPSHOT-runner`
If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling. If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling.
## Related Guides
- Reactive MySQL client ([guide](https://quarkus.io/guides/reactive-sql-clients)): Connect to the MySQL database using the reactive pattern
- RESTEasy Reactive ([guide](https://quarkus.io/guides/resteasy-reactive)): A Jakarta REST implementation utilizing build time processing and Vert.x.
This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it.
- Hibernate ORM with Panache ([guide](https://quarkus.io/guides/hibernate-orm-panache)): Simplify your persistence code for Hibernate ORM via the
active record or the repository pattern
- Reactive PostgreSQL client ([guide](https://quarkus.io/guides/reactive-sql-clients)): Connect to the PostgreSQL database using the reactive pattern
## Provided Code
### Hibernate ORM
Create your first JPA entity
[Related guide section...](https://quarkus.io/guides/hibernate-orm)
[Related Hibernate with Panache section...](https://quarkus.io/guides/hibernate-orm-panache)
### RESTEasy Reactive
Easily start your Reactive RESTful Web Services
[Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources)
### RESTEasy Reactive Qute
Create your web page using Quarkus RESTEasy Reactive & Qute
[Related guide section...](https://quarkus.io/guides/qute#type-safe-templates)

View File

@@ -20,6 +20,11 @@ services:
- default - default
- intra - intra
- nginx - nginx
healthcheck:
test: [ "CMD", "curl", "-f", "https://intra.ffsaf.fr/api" ]
interval: 30s
timeout: 5s
retries: 3
ffsaf-db: ffsaf-db:
image: public.ecr.aws/docker/library/postgres:17.2 image: public.ecr.aws/docker/library/postgres:17.2

18
pom.xml
View File

@@ -11,7 +11,7 @@
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.16.4</quarkus.platform.version> <quarkus.platform.version>3.30.5</quarkus.platform.version>
<skipITs>true</skipITs> <skipITs>true</skipITs>
<surefire-plugin.version>3.2.3</surefire-plugin.version> <surefire-plugin.version>3.2.3</surefire-plugin.version>
</properties> </properties>
@@ -56,19 +56,15 @@
<artifactId>quarkus-rest-client-jackson</artifactId> <artifactId>quarkus-rest-client-jackson</artifactId>
</dependency> </dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-mssql-client</artifactId>
<version>4.4.1</version>
</dependency>
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId> <artifactId>quarkus-arc</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.quarkiverse.tika</groupId> <groupId>io.quarkiverse.antivirus</groupId>
<artifactId>quarkus-tika</artifactId> <artifactId>quarkus-antivirus</artifactId>
<version>2.0.4</version> <version>1.3.0</version>
</dependency> </dependency>
<dependency> <dependency>
@@ -96,7 +92,7 @@
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.18.22</version> <version>1.18.42</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
@@ -127,7 +123,7 @@
<dependency> <dependency>
<groupId>org.apache.xmlgraphics</groupId> <groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop</artifactId> <artifactId>fop</artifactId>
<version>2.6</version> <version>2.11</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>

View File

@@ -44,7 +44,9 @@ public class FrontendForwardingFilter implements ContainerResponseFilter {
final String path = info.getPath(); final String path = info.getPath();
final String address = request.remoteAddress().toString(); final String address = request.remoteAddress().toString();
LOG.infof("Request %s %s from IP %s", method, path, address); if (!path.equals("/api")) {
LOG.infof("Request %s %s from IP %s", method, path, address);
}
int status = responseContext.getStatus(); int status = responseContext.getStatus();
if (status != 404 && !(status == 405 && "GET".equals(requestContext.getMethod()))) { if (status != 404 && !(status == 405 && "GET".equals(requestContext.getMethod()))) {

View File

@@ -0,0 +1,48 @@
package fr.titionfire.ffsaf;
import fr.titionfire.ffsaf.domain.service.TradService;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.container.PreMatching;
import jakarta.ws.rs.ext.Provider;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
@Provider
@PreMatching
public class UserInfoProvider implements ContainerRequestFilter {
private static final List<Locale> SUPPORTED_LANGUAGES = Arrays.asList(
Locale.FRENCH,
Locale.ENGLISH
);
@Override
public void filter(ContainerRequestContext requestContext) {
List<Locale> acceptableLanguages = requestContext.getAcceptableLanguages();
Locale selectedLocale = findFirstSupportedLanguage(acceptableLanguages);
if (selectedLocale == null)
selectedLocale = TradService.fallbackLocale;
requestContext.setProperty("userLocale", selectedLocale);
}
private Locale findFirstSupportedLanguage(List<Locale> acceptableLanguages) {
for (Locale acceptableLanguage : acceptableLanguages) {
// Vérifie si la langue est dans la liste des langues supportées
if (SUPPORTED_LANGUAGES.contains(acceptableLanguage)) {
return acceptableLanguage;
}
// Vérifie aussi par tag de langue (ex: "fr-FR" -> "fr")
String languageTag = acceptableLanguage.getLanguage();
for (Locale supportedLanguage : SUPPORTED_LANGUAGES) {
if (supportedLanguage.getLanguage().equals(languageTag)) {
return supportedLanguage;
}
}
}
return null;
}
}

View File

@@ -0,0 +1,64 @@
package fr.titionfire.ffsaf.data.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import java.util.Date;
import java.util.Objects;
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "card")
public class CardModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
Long comb;
Long match;
Long category;
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "competition", referencedColumnName = "id")
CompetitionModel competition;
@JsonProperty("competition")
Long competitionId;
CardType type;
String reason;
@CreationTimestamp
Date date;
@Column(nullable = false, columnDefinition = "boolean default false")
boolean teamCard = false;
public boolean hasEffect(MatchModel match) {
return switch (this.type) {
case BLUE -> false;
case YELLOW -> Objects.equals(this.match, match.getId());
case RED -> Objects.equals(this.category, match.getCategory().getId())
|| Objects.equals(this.match, match.getId());
case BLACK -> true;
};
}
public enum CardType {
BLUE,
YELLOW,
RED,
BLACK
}
}

View File

@@ -1,42 +0,0 @@
package fr.titionfire.ffsaf.data.model;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "cardboard")
public class CardboardModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "comb", referencedColumnName = "id")
MembreModel comb;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "guest_comb", referencedColumnName = "id")
CompetitionGuestModel guestComb;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "match", referencedColumnName = "id")
MatchModel match;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "compet", referencedColumnName = "id")
CompetitionModel compet;
int red;
int yellow;
}

View File

@@ -0,0 +1,89 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.Categorie;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.*;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "category_preset")
public class CatPresetModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "competition", referencedColumnName = "id")
CompetitionModel competition;
String name = "";
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "category_preset_catconfig", joinColumns = @JoinColumn(name = "id_preset"))
List<CategorieEmbeddable> categories;
SwordType swordType = SwordType.NONE;
ShieldType shieldType = ShieldType.NONE;
/*
* 1 - 1 - Casque
* 2 - 2 - Gorgerin
* 3 - 4 - Coquille et Protection pelvienne
* 4 - 8 - Gant main(s) armée(s)
* 5 - 16 - Gant main bouclier
* 6 - 32 - Plastron
* 7 - 64 - Protection de bras armé(s)
* 8 - 128 - Protection de bras de bouclier
* 9 - 256 - Protection de jambes
* 10 - 512 - Protection de genoux
* 11 - 1024 - Protection de coudes
* 12 - 2048 - Protection dorsale
* 13 - 4096 - Protection de pieds
*/
int mandatoryProtection1 = 0;
int mandatoryProtection2 = 0;
@ManyToMany(mappedBy = "categoriesInscrites", fetch = FetchType.LAZY)
private List<RegisterModel> registers = new ArrayList<>();
@ManyToMany(mappedBy = "categoriesInscrites", fetch = FetchType.LAZY)
private List<CompetitionGuestModel> guest = new ArrayList<>();
public enum SwordType {
NONE,
ONE_HAND,
TWO_HAND,
SABER
}
public enum ShieldType {
NONE,
STANDARD,
ROUND,
TEARDROP,
BUCKLER
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Embeddable
public static class CategorieEmbeddable {
Categorie categorie;
long roundDuration;
long pauseDuration;
}
}

View File

@@ -44,4 +44,14 @@ public class CategoryModel {
Integer type; Integer type;
String liceName = "1"; String liceName = "1";
@Column(nullable = false, columnDefinition = "boolean default false")
boolean treeAreClassement = false;
@Column(nullable = false, columnDefinition = "boolean default false")
boolean fullClassement = false;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "id_preset", referencedColumnName = "id")
CatPresetModel preset;
} }

View File

@@ -0,0 +1,38 @@
package fr.titionfire.ffsaf.data.model;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.CreationTimestamp;
import java.util.Date;
import java.util.List;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "card_team")
public class ClubCardModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
Long competition;
String teamUuid;
String teamName;
CardModel.CardType type;
String reason;
@CreationTimestamp
Date date;
List<Long> cardIds;
}

View File

@@ -0,0 +1,11 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
public interface CombModel {
Long getCombId();
String getName();
String getName(MembreModel model, ResultPrivacy privacy);
Categorie getCategorie();
}

View File

@@ -2,6 +2,7 @@ package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.Categorie; import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.Genre; import fr.titionfire.ffsaf.utils.Genre;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@@ -9,6 +10,11 @@ import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
@Getter @Getter
@Setter @Setter
@AllArgsConstructor @AllArgsConstructor
@@ -17,7 +23,7 @@ import lombok.Setter;
@Entity @Entity
@Table(name = "competition_guest") @Table(name = "competition_guest")
public class CompetitionGuestModel { public class CompetitionGuestModel implements CombModel {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
Long id; Long id;
@@ -37,14 +43,73 @@ public class CompetitionGuestModel {
String country = "fr"; String country = "fr";
Integer weight = null; Float weight = null;
Float weightReal = null;
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
@JoinTable(
name = "groupe_membre",
joinColumns = @JoinColumn(name = "groupe_id"),
inverseJoinColumns = @JoinColumn(name = "membre_id")
)
List<MembreModel> comb = new ArrayList<>();
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
@JoinTable(
name = "groupe_guest",
joinColumns = @JoinColumn(name = "groupe_id"),
inverseJoinColumns = @JoinColumn(name = "guest_id")
)
List<CompetitionGuestModel> guest = new ArrayList<>();
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinTable(
name = "categories_insc_guest",
joinColumns = @JoinColumn(name = "guest_id"),
inverseJoinColumns = @JoinColumn(name = "category_id")
)
List<CatPresetModel> categoriesInscrites = new ArrayList<>();
public CompetitionGuestModel(String s) { public CompetitionGuestModel(String s) {
this.fname = s.substring(0, s.indexOf(" ")); this.fname = s.substring(0, s.indexOf(" "));
this.lname = s.substring(s.indexOf(" ") + 1); this.lname = s.substring(s.indexOf(" ") + 1);
} }
@Override
public Long getCombId() {
return this.id * -1;
}
@Override
public String getName() { public String getName() {
return fname + " " + lname; if (this.isTeam())
return this.fname;
return this.fname + " " + this.lname;
}
@Override
public String getName(MembreModel model, ResultPrivacy privacy) {
return getName();
}
public boolean isTeam() {
return "__team".equals(this.lname);
}
public boolean isInTeam(Object comb_) {
if (!this.isTeam())
return false;
if (comb_ instanceof Long id_) {
if (id_ >= 0)
return comb.stream().anyMatch(membre -> Objects.equals(membre.getId(), id_));
else
return guest.stream().anyMatch(guestModel -> Objects.equals(guestModel.getId(), -id_));
}
return Stream.concat(comb.stream(), guest.stream()).anyMatch(c -> Objects.equals(c, comb_));
}
public Float getWeight2() {
return (this.weightReal != null) ? this.weightReal : this.weight;
} }
} }

View File

@@ -1,5 +1,6 @@
package fr.titionfire.ffsaf.data.model; package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.CompetitionSystem; import fr.titionfire.ffsaf.utils.CompetitionSystem;
import fr.titionfire.ffsaf.utils.RegisterMode; import fr.titionfire.ffsaf.utils.RegisterMode;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
@@ -58,6 +59,10 @@ public class CompetitionModel {
@OneToMany(mappedBy = "competition", fetch = FetchType.LAZY, cascade = CascadeType.ALL) @OneToMany(mappedBy = "competition", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
List<CompetitionGuestModel> guests = new ArrayList<>(); List<CompetitionGuestModel> guests = new ArrayList<>();
@OneToMany(mappedBy = "competition", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
List<CatPresetModel> catPreset = new ArrayList<>();
List<Categorie> requiredWeight = new ArrayList<>();
List<Long> banMembre = new ArrayList<>(); List<Long> banMembre = new ArrayList<>();
@@ -67,8 +72,15 @@ public class CompetitionModel {
@Column(name = "table_") @Column(name = "table_")
List<String> table = new ArrayList<>(); List<String> table = new ArrayList<>();
@Column(columnDefinition = "TEXT")
String data1; String data1;
@Column(columnDefinition = "TEXT")
String data2; String data2;
@Column(columnDefinition = "TEXT")
String data3; String data3;
@Column(columnDefinition = "TEXT")
String data4; String data4;
@Column(columnDefinition = "TEXT")
String config;
} }

View File

@@ -41,7 +41,7 @@ public class LogModel {
} }
public enum ObjectType { public enum ObjectType {
Membre, Affiliation, Licence, Club, Competition, Register Membre, Affiliation, Licence, Club, Competition, Register, Selection
} }
} }

View File

@@ -1,6 +1,7 @@
package fr.titionfire.ffsaf.data.model; package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.CompetitionSystem; import fr.titionfire.ffsaf.utils.CompetitionSystem;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
import fr.titionfire.ffsaf.utils.ScoreEmbeddable; import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*; import jakarta.persistence.*;
@@ -9,6 +10,7 @@ import lombok.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects;
@Getter @Getter
@Setter @Setter
@@ -61,20 +63,36 @@ public class MatchModel {
char poule = 'A'; char poule = 'A';
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) public String getC1Name(MembreModel model, ResultPrivacy privacy) {
@JoinColumn(name = "match", referencedColumnName = "id") if (c1_id != null)
List<CardboardModel> cardboard = new ArrayList<>(); return c1_id.getName(model, privacy);
if (c1_guest != null)
return c1_guest.getName(model, privacy);
return "";
}
public String getC2Name(MembreModel model, ResultPrivacy privacy) {
if (c2_id != null)
return c2_id.getName(model, privacy);
if (c2_guest != null)
return c2_guest.getName(model, privacy);
return "";
}
public String getC1Name() { public String getC1Name() {
if (c1_id == null) if (c1_id != null)
return c1_guest.fname + " " + c1_guest.lname; return c1_id.getName();
return c1_id.fname + " " + c1_id.lname; if (c1_guest != null)
return c1_guest.getName();
return "";
} }
public String getC2Name() { public String getC2Name() {
if (c2_id == null) if (c2_id != null)
return c2_guest.fname + " " + c2_guest.lname; return c2_id.getName();
return c2_id.fname + " " + c2_id.lname; if (c2_guest != null)
return c2_guest.getName();
return "";
} }
public int win() { public int win() {
@@ -90,4 +108,46 @@ public class MatchModel {
} }
return sum; return sum;
} }
public CombModel getC1() {
if (this.c1_id != null)
return this.c1_id;
if (this.c1_guest != null)
return this.c1_guest;
return null;
}
public CombModel getC2() {
if (this.c2_id != null)
return this.c2_id;
if (this.c2_guest != null)
return this.c2_guest;
return null;
}
public boolean isC1(Object comb) {
if (this.c1_guest != null && this.c1_guest.isInTeam(comb))
return true;
if (comb instanceof Long id_) {
if (id_ >= 0)
return Objects.equals(this.c1_id != null ? this.c1_id.getId() : null, id_);
else
return Objects.equals(this.c1_guest != null ? this.c1_guest.getId() : null, -id_);
}
return Objects.equals(this.c1_id, comb) || Objects.equals(this.c1_guest, comb);
}
public boolean isC2(Object comb) {
if (this.c2_guest != null && this.c2_guest.isInTeam(comb))
return true;
if (comb instanceof Long id_) {
if (id_ >= 0)
return Objects.equals(this.c2_id != null ? this.c2_id.getId() : null, id_);
else
return Objects.equals(this.c2_guest != null ? this.c2_guest.getId() : null, -id_);
}
return Objects.equals(this.c2_id, comb) || Objects.equals(this.c2_guest, comb);
}
} }

View File

@@ -1,9 +1,6 @@
package fr.titionfire.ffsaf.data.model; package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.Categorie; import fr.titionfire.ffsaf.utils.*;
import fr.titionfire.ffsaf.utils.Genre;
import fr.titionfire.ffsaf.utils.GradeArbitrage;
import fr.titionfire.ffsaf.utils.RoleAsso;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.*; import lombok.*;
@@ -11,6 +8,7 @@ import org.eclipse.microprofile.openapi.annotations.media.Schema;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects;
@Getter @Getter
@Setter @Setter
@@ -21,23 +19,23 @@ import java.util.List;
@Entity @Entity
@Table(name = "membre") @Table(name = "membre")
public class MembreModel implements LoggableModel { public class MembreModel implements LoggableModel, CombModel {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
@Access(AccessType.PROPERTY) @Access(AccessType.PROPERTY)
@Schema(description = "L'identifiant du membre.", example = "1") @Schema(description = "L'identifiant du membre.", examples = "1")
Long id; Long id;
@Schema(description = "L'identifiant long du membre (userID).", example = "e81d1d35-d897-421e-8086-6c5e74d13c6e") @Schema(description = "L'identifiant long du membre (userID).", examples = "e81d1d35-d897-421e-8086-6c5e74d13c6e")
String userId; String userId;
@Schema(description = "Le nom du membre.", example = "Dupont") @Schema(description = "Le nom du membre.", examples = "Dupont")
String lname; String lname;
@Schema(description = "Le prénom du membre.", example = "Jean") @Schema(description = "Le prénom du membre.", examples = "Jean")
String fname; String fname;
@Schema(description = "La catégorie du membre.", example = "SENIOR") @Schema(description = "La catégorie du membre.", examples = "SENIOR")
Categorie categorie; Categorie categorie;
@ManyToOne(fetch = FetchType.EAGER) @ManyToOne(fetch = FetchType.EAGER)
@@ -45,34 +43,39 @@ public class MembreModel implements LoggableModel {
@Schema(description = "Le club du membre.") @Schema(description = "Le club du membre.")
ClubModel club; ClubModel club;
@Schema(description = "Le genre du membre.", example = "H") @Schema(description = "Le genre du membre.", examples = "H")
Genre genre; Genre genre;
@Schema(description = "Le numéro de licence du membre.", example = "12345") @Schema(description = "Le numéro de licence du membre.", examples = "12345")
Integer licence; Integer licence;
@Schema(description = "Le pays du membre.", example = "FR") @Schema(description = "Le pays du membre.", examples = "FR")
String country; String country;
@Schema(description = "La date de naissance du membre.") @Schema(description = "La date de naissance du membre.")
Date birth_date; Date birth_date;
@Schema(description = "L'adresse e-mail du membre.", example = "jean.dupont@example.com") @Schema(description = "L'adresse e-mail du membre.", examples = "jean.dupont@examples.com")
String email; String email;
@Schema(description = "Le rôle du membre dans l'association.", example = "MEMBRE") @Schema(description = "Le rôle du membre dans l'association.", examples = "MEMBRE")
RoleAsso role; RoleAsso role;
@Schema(description = "Le grade d'arbitrage du membre.", example = "NA") @Schema(description = "Le grade d'arbitrage du membre.", examples = "NA")
GradeArbitrage grade_arbitrage; GradeArbitrage grade_arbitrage;
@Schema(hidden = true) @Schema(description = "La confidentialité de ces résultats", examples = "PUBLIC")
String url_photo; @Column(nullable = false, columnDefinition = "smallint default 0")
ResultPrivacy resultPrivacy = ResultPrivacy.PUBLIC;
@OneToMany(mappedBy = "membre", fetch = FetchType.LAZY, cascade = CascadeType.ALL) @OneToMany(mappedBy = "membre", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@Schema(description = "Les licences du membre. (optionnel)") @Schema(description = "Les licences du membre. (optionnel)")
List<LicenceModel> licences; List<LicenceModel> licences;
@OneToMany(mappedBy = "membre", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@Schema(description = "Les séléctions du membre. (optionnel)")
List<SelectionModel> selections;
@Override @Override
public String getObjectName() { public String getObjectName() {
return fname + " " + lname; return fname + " " + lname;
@@ -100,4 +103,25 @@ public class MembreModel implements LoggableModel {
", grade_arbitrage=" + grade_arbitrage + ", grade_arbitrage=" + grade_arbitrage +
'}'; '}';
} }
@Override
public Long getCombId() {
return this.id;
}
@Override
public String getName() {
return this.fname + " " + this.lname;
}
@Override
public String getName(MembreModel model, ResultPrivacy privacy) {
if (model == null || !Objects.equals(this.getId(), model.getId())) {
if (model == null && this.getResultPrivacy() != ResultPrivacy.PUBLIC)
return "Anonyme";
if (this.getResultPrivacy().ordinal() > privacy.ordinal())
return "Anonyme";
}
return getName();
}
} }

View File

@@ -11,6 +11,9 @@ import lombok.Setter;
import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction; import org.hibernate.annotations.OnDeleteAction;
import java.util.ArrayList;
import java.util.List;
@Getter @Getter
@Setter @Setter
@AllArgsConstructor @AllArgsConstructor
@@ -34,7 +37,8 @@ public class RegisterModel {
@JoinColumn(name = "id_membre") @JoinColumn(name = "id_membre")
MembreModel membre; MembreModel membre;
Integer weight; Float weight;
Float weightReal;
int overCategory = 0; int overCategory = 0;
Categorie categorie; Categorie categorie;
@@ -46,7 +50,18 @@ public class RegisterModel {
@Column(nullable = false, columnDefinition = "boolean default false") @Column(nullable = false, columnDefinition = "boolean default false")
boolean lockEdit = false; boolean lockEdit = false;
public RegisterModel(CompetitionModel competition, MembreModel membre, Integer weight, int overCategory, @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinTable(
name = "categories_insc_comb",
joinColumns = {
@JoinColumn(name = "id_competition", referencedColumnName = "id_competition"),
@JoinColumn(name = "id_membre", referencedColumnName = "id_membre")
},
inverseJoinColumns = @JoinColumn(name = "category_id")
)
List<CatPresetModel> categoriesInscrites = new ArrayList<>();
public RegisterModel(CompetitionModel competition, MembreModel membre, Float weight, int overCategory,
Categorie categorie, ClubModel club) { Categorie categorie, ClubModel club) {
this.id = new RegisterId(competition.getId(), membre.getId()); this.id = new RegisterId(competition.getId(), membre.getId());
this.competition = competition; this.competition = competition;
@@ -66,4 +81,19 @@ public class RegisterModel {
return membre.club; return membre.club;
return club; return club;
} }
public Categorie getCategorie2() {
Categorie tmp = this.categorie;
if (tmp == null)
tmp = membre.getCategorie();
if (tmp == null)
return null;
return Categorie.values()[Math.min(tmp.ordinal() + this.overCategory, Categorie.values().length - 1)];
}
public Float getWeight2() {
if (weightReal != null)
return weightReal;
return weight;
}
} }

View File

@@ -0,0 +1,44 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.Categorie;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.*;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "selection")
public class SelectionModel implements LoggableModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(description = "L'identifiant de la séléction.")
Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "membre", referencedColumnName = "id")
@Schema(description = "Le membre de la séléction. (optionnel)")
MembreModel membre;
@Schema(description = "La saison de la séléction.", examples = "2025")
int saison;
@Schema(description = "Catégorie de la séléction.")
Categorie categorie;
@Override
public String getObjectName() {
return "selection " + id.toString();
}
@Override
public LogModel.ObjectType getObjectType() {
return LogModel.ObjectType.Selection;
}
}

View File

@@ -40,6 +40,14 @@ public class TreeModel {
@JoinColumn(referencedColumnName = "id") @JoinColumn(referencedColumnName = "id")
TreeModel right; TreeModel right;
public TreeModel(Long category, Integer level, MatchModel match) {
this.category = category;
this.level = level;
this.match = match;
this.left = null;
this.right = null;
}
public List<TreeModel> flat() { public List<TreeModel> flat() {
List<TreeModel> out = new ArrayList<>(); List<TreeModel> out = new ArrayList<>();
this.flat(out); this.flat(out);
@@ -55,4 +63,44 @@ public class TreeModel {
if (this.left != null) if (this.left != null)
this.left.flat(out); this.left.flat(out);
} }
public int death() {
int dg = 0;
int dd = 0;
if (this.right != null)
dg = this.right.death();
if (this.left != null)
dg = this.left.death();
return 1 + Math.max(dg, dd);
}
public int getMaxChildrenAtDepth(int death, int current) {
if (current == death)
return 1;
int tmp = 0;
if (this.right != null)
tmp += this.right.getMaxChildrenAtDepth(death, current + 1);
if (this.left != null)
tmp += this.left.getMaxChildrenAtDepth(death, current + 1);
return tmp;
}
public void getChildrenAtDepth (int death, int current, List<TreeModel> out) {
if (current == death) {
out.add(this);
return;
}
if (this.right != null)
this.right.getChildrenAtDepth(death, current + 1, out);
if (this.left != null)
this.left.getChildrenAtDepth(death, current + 1, out);
}
} }

View File

@@ -0,0 +1,10 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.CardModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class CardRepository implements PanacheRepositoryBase<CardModel, Long> {
}

View File

@@ -0,0 +1,9 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.CatPresetModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class CatPresetRepository implements PanacheRepositoryBase<CatPresetModel, Long> {
}

View File

@@ -2,8 +2,17 @@ package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.CheckoutModel; import fr.titionfire.ffsaf.data.model.CheckoutModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped @ApplicationScoped
public class CheckoutRepository implements PanacheRepositoryBase<CheckoutModel, Long> { public class CheckoutRepository implements PanacheRepositoryBase<CheckoutModel, Long> {
public Uni<Long> countByLicenseId(long id) {
return getSession()
.chain(s -> s
.createNativeQuery("SELECT COUNT(*) FROM ffsaf__checkout WHERE ?1 = ANY(license_ids)", Long.class)
.setParameter(1, id)
.getSingleResult());
}
} }

View File

@@ -0,0 +1,10 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.ClubCardModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class ClubCardRepository implements PanacheRepositoryBase<ClubCardModel, Long> {
}

View File

@@ -20,6 +20,9 @@ public class MatchRepository implements PanacheRepositoryBase<MatchModel, Long>
} }
public Uni<Void> create(List<MatchModel> matchModel) { public Uni<Void> create(List<MatchModel> matchModel) {
if (matchModel.isEmpty())
return Uni.createFrom().voidItem();
matchModel.forEach(model -> model.setSystem(CompetitionSystem.INTERNAL)); matchModel.forEach(model -> model.setSystem(CompetitionSystem.INTERNAL));
return Panache.withTransaction(() -> this.persist(matchModel) return Panache.withTransaction(() -> this.persist(matchModel)
.call(__ -> this.flush()) .call(__ -> this.flush())

View File

@@ -0,0 +1,9 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.SelectionModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class SelectionRepository implements PanacheRepositoryBase<SelectionModel, Long> {
}

View File

@@ -1,23 +0,0 @@
package fr.titionfire.ffsaf.domain.entity;
import fr.titionfire.ffsaf.data.model.CardboardModel;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
@RegisterForReflection
public class CardboardEntity {
long comb_id;
long match_id;
long compet_id;
int red;
int yellow;
public static CardboardEntity fromModel(CardboardModel model) {
return new CardboardEntity(model.getComb().getId(), model.getMatch().getId(), model.getCompet().getId(),
model.getRed(), model.getYellow());
}
}

View File

@@ -1,5 +1,6 @@
package fr.titionfire.ffsaf.domain.entity; package fr.titionfire.ffsaf.domain.entity;
import fr.titionfire.ffsaf.data.model.CatPresetModel;
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel; import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.data.model.RegisterModel; import fr.titionfire.ffsaf.data.model.RegisterModel;
@@ -9,6 +10,10 @@ import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@RegisterForReflection @RegisterForReflection
@@ -17,21 +22,23 @@ public class CombEntity {
private String lname; private String lname;
private String fname; private String fname;
Categorie categorie; Categorie categorie;
Long club; String club_uuid;
String club_str; String club_str;
Genre genre; Genre genre;
String country; String country;
int overCategory; int overCategory;
Integer weight; Float weight;
List<CombEntity> teamMembers;
List<Long> categoriesInscrites;
public static CombEntity fromModel(MembreModel model) { public static CombEntity fromModel(MembreModel model) {
if (model == null) if (model == null)
return null; return null;
return new CombEntity(model.getId(), model.getLname(), model.getFname(), model.getCategorie(), return new CombEntity(model.getId(), model.getLname(), model.getFname(), model.getCategorie(),
model.getClub() == null ? null : model.getClub().getId(), model.getClub() == null ? null : model.getClub().getClubId(),
model.getClub() == null ? "Sans club" : model.getClub().getName(), model.getGenre(), model.getCountry(), model.getClub() == null ? "Sans club" : model.getClub().getName(), model.getGenre(), model.getCountry(),
0, null); 0, null, new ArrayList<>(), new ArrayList<>());
} }
@@ -40,7 +47,18 @@ public class CombEntity {
return null; return null;
return new CombEntity(model.getId() * -1, model.getLname(), model.getFname(), model.getCategorie(), null, return new CombEntity(model.getId() * -1, model.getLname(), model.getFname(), model.getCategorie(), null,
model.getClub(), model.getGenre(), model.getCountry(), 0, model.getWeight()); model.getClub(), model.getGenre(), model.getCountry(), 0,
model.getWeight2() != null ? model.getWeight2() : model.getWeight(),
Stream.concat(model.getComb().stream().map(CombEntity::fromModel),
model.getGuest().stream().map(CombEntity::fromModel)).toList(),
new ArrayList<>());
}
public CombEntity addCategoriesInscrites(List<CatPresetModel> categoriesInscrites) {
if (categoriesInscrites == null)
return this;
this.categoriesInscrites = categoriesInscrites.stream().map(CatPresetModel::getId).toList();
return this;
} }
public static CombEntity fromModel(RegisterModel registerModel) { public static CombEntity fromModel(RegisterModel registerModel) {
@@ -49,8 +67,10 @@ public class CombEntity {
MembreModel model = registerModel.getMembre(); MembreModel model = registerModel.getMembre();
return new CombEntity(model.getId(), model.getLname(), model.getFname(), registerModel.getCategorie(), return new CombEntity(model.getId(), model.getLname(), model.getFname(), registerModel.getCategorie(),
registerModel.getClub2() == null ? null : registerModel.getClub2().getId(), registerModel.getClub2() == null ? null : registerModel.getClub2().getClubId(),
registerModel.getClub2() == null ? "Sans club" : registerModel.getClub2().getName(), model.getGenre(), registerModel.getClub2() == null ? "Sans club" : registerModel.getClub2().getName(), model.getGenre(),
model.getCountry(), registerModel.getOverCategory(), registerModel.getWeight()); model.getCountry(), registerModel.getOverCategory(),
registerModel.getWeight2() != null ? registerModel.getWeight2() : registerModel.getWeight(),
new ArrayList<>(), new ArrayList<>());
} }
} }

View File

@@ -6,7 +6,6 @@ import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@@ -23,7 +22,6 @@ public class MatchEntity {
private Date date; private Date date;
private List<ScoreEmbeddable> scores; private List<ScoreEmbeddable> scores;
private char poule; private char poule;
private List<CardboardEntity> cardboard;
public static MatchEntity fromModel(MatchModel model) { public static MatchEntity fromModel(MatchModel model) {
if (model == null) if (model == null)
@@ -35,22 +33,6 @@ public class MatchEntity {
model.getC2_id()), model.getC2_id()),
model.getCategory_ord(), model.isEnd(), model.getCategory().getId(), model.getDate(), model.getCategory_ord(), model.isEnd(), model.getCategory().getId(), model.getDate(),
model.getScores(), model.getScores(),
model.getPoule(), model.getPoule());
(model.getCardboard() == null) ? new ArrayList<>() : model.getCardboard().stream()
.map(CardboardEntity::fromModel).toList());
}
public int win() {
int sum = 0;
for (ScoreEmbeddable score : scores) {
if (score.getS1() == -1000 || score.getS2() == -1000)
continue;
if (score.getS1() > score.getS2())
sum++;
else if (score.getS1() < score.getS2())
sum--;
}
return sum;
} }
} }

View File

@@ -0,0 +1,184 @@
package fr.titionfire.ffsaf.domain.entity;
import fr.titionfire.ffsaf.data.model.*;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.Getter;
import java.util.*;
@RegisterForReflection
public class MatchModelExtend {
final MatchModel match;
@Getter
boolean isEnd = false;
@Getter
List<ScoreEmbeddable> scoresToPrint = new ArrayList<>();
@Getter
List<ScoreEmbeddable> scoresToCompute = new ArrayList<>();
@Getter
int win = 0;
public MatchModelExtend(MatchModel match, List<CardModel> cards) {
this.match = match;
List<Long> combIds = extractCombIds(match);
List<CardModel> cards2 = cards.stream().filter(c -> combIds.contains(c.getComb()) && c.hasEffect(match))
.sorted(Comparator.comparing(CardModel::getType).reversed()).toList();
for (ScoreEmbeddable score : match.getScores()) {
if (score.getS1() == -1000 || score.getS2() == -1000)
continue;
this.scoresToCompute.add(virtualScore(score, cards2, false));
}
calc_win_end(cards2);
for (ScoreEmbeddable score : match.getScores()) {
if (score.getS1() == -1000 || score.getS2() == -1000)
continue;
this.scoresToPrint.add(virtualScore(score, cards2, true));
}
if (this.isEnd && this.scoresToPrint.isEmpty()) {
this.scoresToPrint.add(virtualScore(new ScoreEmbeddable(0, 0, 0), cards2, true));
}
}
private ScoreEmbeddable virtualScore(ScoreEmbeddable score, List<CardModel> cards2, boolean toPrint) {
if (cards2.size() > 1) {
if (!Objects.equals(cards2.get(0).getComb(), cards2.get(1).getComb()))
return new ScoreEmbeddable(score.getN_round(), toPrint ? -997 : 0, toPrint ? -997 : 0);
}
if (!cards2.isEmpty()) {
if (isC1(cards2.get(0).getComb()))
return new ScoreEmbeddable(score.getN_round(), toPrint ? -997 : 0, 10);
else
return new ScoreEmbeddable(score.getN_round(), 10, toPrint ? -997 : 0);
}
if (score.getS1() < -900 && score.getS2() < -900)
return new ScoreEmbeddable(score.getN_round(), toPrint ? score.getS1() : 0, toPrint ? score.getS2() : 0);
else if (score.getS1() < -900)
return new ScoreEmbeddable(score.getN_round(), toPrint ? score.getS1() : 0, 10);
else if (score.getS2() < -900)
return new ScoreEmbeddable(score.getN_round(), 10, toPrint ? score.getS2() : 0);
return new ScoreEmbeddable(score.getN_round(), score.getS1(), score.getS2());
}
private void calc_win_end(List<CardModel> cards2) {
if (cards2.size() > 1) {
if (!Objects.equals(cards2.get(0).getComb(), cards2.get(1).getComb())) {
this.win = 0;
this.isEnd = true;
return;
}
}
if (!cards2.isEmpty()) {
if (match.isC1(cards2.get(0).getComb())) {
this.win = -1;
} else if (match.isC2(cards2.get(0).getComb())) {
this.win = 1;
}
this.isEnd = true;
return;
}
for (ScoreEmbeddable score : this.scoresToCompute) {
if (score.getS1() > score.getS2())
win++;
else if (score.getS1() < score.getS2())
win--;
}
this.isEnd = match.isEnd();
}
private List<Long> extractCombIds(MatchModel match) {
List<Long> ids = new ArrayList<>();
if (match.getC1_id() != null)
ids.add(match.getC1_id().getId());
if (match.getC2_id() != null)
ids.add(match.getC2_id().getId());
if (match.getC1_guest() != null)
ids.add(match.getC1_guest().getId() * -1);
if (match.getC2_guest() != null)
ids.add(match.getC2_guest().getId() * -1);
return ids;
}
//--------------- Delegation methods to MatchModel ---------------
public Long getId() {
return match.getId();
}
public MembreModel getC1_id() {
return match.getC1_id();
}
public CompetitionGuestModel getC1_guest() {
return match.getC1_guest();
}
public MembreModel getC2_id() {
return match.getC2_id();
}
public CompetitionGuestModel getC2_guest() {
return match.getC2_guest();
}
public CategoryModel getCategory() {
return match.getCategory();
}
public long getCategory_ord() {
return match.getCategory_ord();
}
public Date getDate() {
return match.getDate();
}
public char getPoule() {
return match.getPoule();
}
public String getC1Name(MembreModel model, ResultPrivacy privacy) {
return match.getC1Name(model, privacy);
}
public String getC2Name(MembreModel model, ResultPrivacy privacy) {
return match.getC2Name(model, privacy);
}
public String getC2Name() {
return match.getC2Name();
}
public String getC1Name() {
return match.getC1Name();
}
public boolean isC1(Object comb) {
return match.isC1(comb);
}
public boolean isC2(Object comb) {
return match.isC2(comb);
}
public CombModel getC1() {
return match.getC1();
}
public CombModel getC2() {
return match.getC2();
}
}

View File

@@ -1,64 +0,0 @@
package fr.titionfire.ffsaf.domain.entity;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.Genre;
import fr.titionfire.ffsaf.utils.GradeArbitrage;
import fr.titionfire.ffsaf.utils.RoleAsso;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.util.Date;
@Data
@Builder
@AllArgsConstructor
@RegisterForReflection
public class MembreEntity {
private long id;
private String lname = "";
private String fname = "";
private Categorie categorie;
private ClubEntity club;
private Genre genre;
private Integer licence;
private String country;
private Date birth_date;
private String email;
private RoleAsso role;
private GradeArbitrage grade_arbitrage;
private String url_photo;
public static MembreEntity fromModel(MembreModel model) {
if (model == null)
return null;
return new MembreEntityBuilder()
.id(model.getId())
.lname(model.getLname())
.fname(model.getFname())
.categorie(model.getCategorie())
.club(ClubEntity.fromModel(model.getClub()))
.genre(model.getGenre())
.licence(model.getLicence())
.country(model.getCountry())
.birth_date(model.getBirth_date())
.email(model.getEmail())
.role(model.getRole())
.grade_arbitrage(model.getGrade_arbitrage())
.url_photo(model.getUrl_photo())
.build();
}
public static String getFullName(MembreModel model) {
return model.getFname() + " " + model.getLname();
}
public String getFullName() {
return this.fname + " " + this.lname;
}
}

View File

@@ -64,6 +64,9 @@ public class AffiliationService {
@Inject @Inject
LoggerService ls; LoggerService ls;
@Inject
VirusScannerService scanner;
@RestClient @RestClient
StateIdService stateIdService; StateIdService stateIdService;
@@ -76,6 +79,9 @@ public class AffiliationService {
@ConfigProperty(name = "notif.affRequest.mail") @ConfigProperty(name = "notif.affRequest.mail")
List<String> mails; List<String> mails;
@Inject
TradService trad;
public Uni<List<AffiliationRequestModel>> getAllReq() { public Uni<List<AffiliationRequestModel>> getAllReq() {
return repositoryRequest.listAll(); return repositoryRequest.listAll();
} }
@@ -89,7 +95,7 @@ public class AffiliationService {
return Uni.createFrom().item(affModel) return Uni.createFrom().item(affModel)
.invoke(Unchecked.consumer(model -> { .invoke(Unchecked.consumer(model -> {
if (model.getSaison() != currentSaison && model.getSaison() != currentSaison + 1) { if (model.getSaison() != currentSaison && model.getSaison() != currentSaison + 1) {
throw new DBadRequestException("Saison non valid"); throw new DBadRequestException(trad.t("saison.non.valid"));
} }
})) }))
.chain(() -> ((affModel.getState_id().charAt(0) == 'W') ? stateIdService.get_rna( .chain(() -> ((affModel.getState_id().charAt(0) == 'W') ? stateIdService.get_rna(
@@ -107,7 +113,7 @@ public class AffiliationService {
out, affModel.getSaison())) out, affModel.getSaison()))
.onItem().invoke(Unchecked.consumer(count -> { .onItem().invoke(Unchecked.consumer(count -> {
if (count != 0 && unique) { if (count != 0 && unique) {
throw new DBadRequestException("Demande d'affiliation déjà existante"); throw new DBadRequestException(trad.t("demande.d.affiliation.deja.existante"));
} }
})) }))
) )
@@ -115,28 +121,28 @@ public class AffiliationService {
repository.count("club = ?1 and saison = ?2", club, affModel.getSaison()))) repository.count("club = ?1 and saison = ?2", club, affModel.getSaison())))
.onItem().invoke(Unchecked.consumer(count -> { .onItem().invoke(Unchecked.consumer(count -> {
if (count != 0) { if (count != 0) {
throw new DBadRequestException("Affiliation déjà existante"); throw new DBadRequestException(trad.t("affiliation.deja.existante"));
} }
})) }))
.map(o -> affModel) .map(o -> affModel)
.call(model -> ((model.getM1_lincence() != -1) ? combRepository.find("licence", .call(model -> ((model.getM1_lincence() != -1) ? combRepository.find("licence",
model.getM1_lincence()).count().invoke(Unchecked.consumer(count -> { model.getM1_lincence()).count().invoke(Unchecked.consumer(count -> {
if (count == 0) { if (count == 0) {
throw new DBadRequestException("Licence membre n°1 inconnue"); throw new DBadRequestException(trad.t("licence.membre.n.1.inconnue"));
} }
})) : Uni.createFrom().nullItem()) })) : Uni.createFrom().nullItem())
) )
.call(model -> ((model.getM2_lincence() != -1) ? combRepository.find("licence", .call(model -> ((model.getM2_lincence() != -1) ? combRepository.find("licence",
model.getM2_lincence()).count().invoke(Unchecked.consumer(count -> { model.getM2_lincence()).count().invoke(Unchecked.consumer(count -> {
if (count == 0) { if (count == 0) {
throw new DBadRequestException("Licence membre n°2 inconnue"); throw new DBadRequestException(trad.t("licence.membre.n.2.inconnue"));
} }
})) : Uni.createFrom().nullItem()) })) : Uni.createFrom().nullItem())
) )
.call(model -> ((model.getM3_lincence() != -1) ? combRepository.find("licence", .call(model -> ((model.getM3_lincence() != -1) ? combRepository.find("licence",
model.getM3_lincence()).count().invoke(Unchecked.consumer(count -> { model.getM3_lincence()).count().invoke(Unchecked.consumer(count -> {
if (count == 0) { if (count == 0) {
throw new DBadRequestException("Licence membre n°3 inconnue"); throw new DBadRequestException(trad.t("licence.membre.n.3.inconnue"));
} }
})) : Uni.createFrom().nullItem()) })) : Uni.createFrom().nullItem())
); );
@@ -145,7 +151,7 @@ public class AffiliationService {
public Uni<?> saveEdit(AffiliationRequestForm form) { public Uni<?> saveEdit(AffiliationRequestForm form) {
return pre_save(form, false) return pre_save(form, false)
.chain(model -> repositoryRequest.findById(form.getId()) .chain(model -> repositoryRequest.findById(form.getId())
.onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé")) .onItem().ifNull().failWith(new DNotFoundException(trad.t("demande.d.affiliation.non.trouve")))
.chain(origine -> { .chain(origine -> {
origine.setName(model.getName()); origine.setName(model.getName());
origine.setAddress(model.getAddress()); origine.setAddress(model.getAddress());
@@ -174,15 +180,12 @@ public class AffiliationService {
LOGGER.debug("Affiliation Request Created"); LOGGER.debug("Affiliation Request Created");
LOGGER.debug(form.toString()); LOGGER.debug(form.toString());
// noinspection ResultOfMethodCallIgnored,ReactiveStreamsUnusedPublisher // noinspection ReactiveStreamsUnusedPublisher
return pre_save(form, true) return pre_save(form, true)
.chain(model -> Panache.withTransaction(() -> repositoryRequest.persist(model))) .chain(model -> Panache.withTransaction(() -> repositoryRequest.persist(model)))
.onItem() .onItem()
.invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getLogo(), media, .invoke(m -> Utils.uploadFile(scanner, form.getLogo(), m.getId(), media, "aff_request/logo"))
"aff_request/logo"))) .invoke(m -> Utils.uploadFile(scanner, form.getStatus(), m.getId(), media, "aff_request/status"))
.onItem()
.invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getStatus(), media,
"aff_request/status")))
.call(model -> reactiveMailer.send( .call(model -> reactiveMailer.send(
Mail.withText("no-reply@ffsaf.fr", Mail.withText("no-reply@ffsaf.fr",
"[NOTIF] FFSAF - Nouvelle demande d'affiliation", "[NOTIF] FFSAF - Nouvelle demande d'affiliation",
@@ -201,7 +204,7 @@ public class AffiliationService {
LOGGER.debug(form.toString()); LOGGER.debug(form.toString());
return repositoryRequest.findById(form.getId()) return repositoryRequest.findById(form.getId())
.onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé")) .onItem().ifNull().failWith(new DNotFoundException(trad.t("demande.d.affiliation.non.trouve")))
.map(model -> { .map(model -> {
model.setName(form.getName()); model.setName(form.getName());
model.setState_id(form.getState_id()); model.setState_id(form.getState_id());
@@ -245,11 +248,8 @@ public class AffiliationService {
}) })
.chain(model -> Panache.withTransaction(() -> repositoryRequest.persist(model))) .chain(model -> Panache.withTransaction(() -> repositoryRequest.persist(model)))
.onItem() .onItem()
.invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getLogo(), media, .invoke(m -> Utils.uploadFile(scanner, form.getLogo(), m.getId(), media, "aff_request/logo"))
"aff_request/logo"))) .invoke(m -> Utils.uploadFile(scanner, form.getStatus(), m.getId(), media, "aff_request/status"))
.onItem()
.invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getStatus(), media,
"aff_request/status")))
.map(__ -> "Ok"); .map(__ -> "Ok");
} }
@@ -309,7 +309,7 @@ public class AffiliationService {
LOGGER.debug(form.toString()); LOGGER.debug(form.toString());
return repositoryRequest.findById(form.getId()) return repositoryRequest.findById(form.getId())
.onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé")) .onItem().ifNull().failWith(new DNotFoundException(trad.t("demande.d.affiliation.non.trouve")))
.chain(req -> .chain(req ->
clubRepository.find("StateId = ?1", form.getState_id()).firstResult() clubRepository.find("StateId = ?1", form.getState_id()).firstResult()
.chain(model -> (model == null) ? acceptNew(form, req) : acceptOld(form, req, model)) .chain(model -> (model == null) ? acceptNew(form, req) : acceptOld(form, req, model))
@@ -319,13 +319,11 @@ public class AffiliationService {
.recoverWithNull() .recoverWithNull()
.call(___ -> setMembre(form.new Member(3), club, req.getSaison())))) .call(___ -> setMembre(form.new Member(3), club, req.getSaison()))))
.onItem() .onItem()
.invoke(model -> Uni.createFrom() .invoke(model -> Utils.uploadFile(scanner, form.getLogo(), form.getId(), media,
.future(Utils.replacePhoto(form.getId(), form.getLogo(), media, "aff_request/logo"))
"aff_request/logo"))) .invoke(model -> Utils.uploadFile(scanner, form.getStatus(), form.getId(), media,
"aff_request/status"))
.onItem() .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", .call(model -> Utils.moveMedia(form.getId(), model.getId(), media, "aff_request/logo",
"ppClub")) "ppClub"))
.call(model -> Utils.moveMedia(form.getId(), model.getId(), media, "aff_request/status", .call(model -> Utils.moveMedia(form.getId(), model.getId(), media, "aff_request/status",
@@ -355,19 +353,25 @@ public class AffiliationService {
.map(c -> club)); .map(c -> club));
}) })
.call(club -> reactiveMailer.send( .call(club -> reactiveMailer.send(
Mail.withText(form.getM1_email(), Mail.withHtml(form.getM1_email(), "FFSAF - Creation de votre compte sur l'intranet",
"FFSAF - Acceptation de votre demande d'affiliation", String.format(Utils.HTML_HEADER, "FFSAF - Creation de votre compte sur l'intranet") +
String.format( String.format("""
""" <p>Votre demande d'affiliation pour le club <span class="highlight">%s</span> a été acceptée.</p>
Bonjour, <p>LLe numéro d'affiliation de votre club est le <span class="highlight">%d</span>.</p>
""",
club.getName(), club.getNo_affiliation()) +
Utils.HTML_FOOTER
)
.setText(String.format("""
Bonjour,
Votre demande d'affiliation pour le club %s a été acceptée. Votre demande d'affiliation pour le club %s a été acceptée.
Le numéro d'affiliation de votre club est le %d. Le numéro d'affiliation de votre club est le %d.
Cordialement, Cordialement,
L'équipe de la FFSAF L'équipe de la FFSAF
""", club.getName(), club.getNo_affiliation()) """, club.getName(), club.getNo_affiliation()))
).setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("contact@ffsaf.fr") .setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("contact@ffsaf.fr")
.addTo(form.getM2_email(), form.getM3_email()) .addTo(form.getM2_email(), form.getM3_email())
)); ));
} }
@@ -397,7 +401,7 @@ public class AffiliationService {
public Uni<SimpleReqAffiliation> getRequest(long id) { public Uni<SimpleReqAffiliation> getRequest(long id) {
return repositoryRequest.findById(id).map(SimpleReqAffiliation::fromModel) return repositoryRequest.findById(id).map(SimpleReqAffiliation::fromModel)
.onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé")) .onItem().ifNull().failWith(new DNotFoundException(trad.t("demande.d.affiliation.non.trouve")))
.call(out -> clubRepository.find("StateId = ?1", out.getStateId()).firstResult().invoke(c -> { .call(out -> clubRepository.find("StateId = ?1", out.getStateId()).firstResult().invoke(c -> {
if (c != null) { if (c != null) {
out.setClub(c.getId()); out.setClub(c.getId());
@@ -421,7 +425,7 @@ public class AffiliationService {
public Uni<List<SimpleAffiliation>> getAffiliation(long id) { public Uni<List<SimpleAffiliation>> getAffiliation(long id) {
return clubRepository.findById(id) return clubRepository.findById(id)
.onItem().ifNull().failWith(new DNotFoundException("Club non trouvé")) .onItem().ifNull().failWith(new DNotFoundException(trad.t("club.non.trouve")))
.call(model -> Mutiny.fetch(model.getAffiliations())) .call(model -> Mutiny.fetch(model.getAffiliations()))
.chain(model -> repositoryRequest.list("state_id = ?1", model.getStateId()) .chain(model -> repositoryRequest.list("state_id = ?1", model.getStateId())
.map(reqs -> reqs.stream().map(req -> .map(reqs -> reqs.stream().map(req ->
@@ -433,11 +437,11 @@ public class AffiliationService {
public Uni<SimpleAffiliation> setAffiliation(long id, int saison) { public Uni<SimpleAffiliation> setAffiliation(long id, int saison) {
return clubRepository.findById(id) return clubRepository.findById(id)
.onItem().ifNull().failWith(new DNotFoundException("Club non trouvé")) .onItem().ifNull().failWith(new DNotFoundException(trad.t("club.non.trouve")))
.call(model -> Mutiny.fetch(model.getAffiliations())) .call(model -> Mutiny.fetch(model.getAffiliations()))
.invoke(Unchecked.consumer(club -> { .invoke(Unchecked.consumer(club -> {
if (club.getAffiliations().stream().anyMatch(affiliation -> affiliation.getSaison() == saison)) { if (club.getAffiliations().stream().anyMatch(affiliation -> affiliation.getSaison() == saison)) {
throw new DBadRequestException("Affiliation déjà existante"); throw new DBadRequestException(trad.t("affiliation.deja.existante"));
} }
})) }))
.chain(club -> .chain(club ->
@@ -458,21 +462,27 @@ 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.withText(aff.getM1_email(), Mail.withHtml(aff.getM1_email(), "FFSAF - Votre demande d'affiliation a été rejetée.",
"FFSAF - Votre demande d'affiliation a été rejetée.", String.format(Utils.HTML_HEADER, "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>
Bonjour, <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>
""",
aff.getName(), reason) +
Utils.HTML_FOOTER
)
.setText(String.format("""
Bonjour,
Votre demande d'affiliation pour le club %s a été rejetée pour la/les raison(s) suivante(s): Votre demande d'affiliation pour le club %s a été rejetée pour la/les raison(s) suivante(s):
%s %s
Si vous rencontrez un problème ou si vous avez des questions, n'hésitez pas à nous contacter à l'adresse 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, Cordialement,
L'équipe de la FFSAF L'équipe de la FFSAF
""", aff.getName(), reason) """, aff.getName(), reason))
).setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("contact@ffsaf.fr") .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

@@ -0,0 +1,227 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.CardModel;
import fr.titionfire.ffsaf.data.model.ClubCardModel;
import fr.titionfire.ffsaf.data.model.CompetitionModel;
import fr.titionfire.ffsaf.data.model.MatchModel;
import fr.titionfire.ffsaf.data.repository.*;
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
import fr.titionfire.ffsaf.ws.recv.RCard;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.panache.common.Sort;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@WithSession
@ApplicationScoped
public class CardService {
@Inject
CardRepository cardRepository;
@Inject
ClubCardRepository clubCardRepository;
@Inject
RegisterRepository registerRepository;
@Inject
CompetitionGuestRepository competitionGuestRepository;
@Inject
MatchRepository matchRepository;
@Inject
TradService trad;
private static final List<CardModel.CardType> COMPETITION_LEVEL_CARDS = List.of(
CardModel.CardType.YELLOW,
CardModel.CardType.RED,
CardModel.CardType.BLACK
);
private List<Long> extractCombIds(MatchModel match) {
List<Long> ids = new ArrayList<>();
if (match.getC1_id() != null)
ids.add(match.getC1_id().getId());
if (match.getC2_id() != null)
ids.add(match.getC2_id().getId());
if (match.getC1_guest() != null)
ids.add(match.getC1_guest().getId() * -1);
if (match.getC2_guest() != null)
ids.add(match.getC2_guest().getId() * -1);
return ids;
}
public Uni<List<CardModel>> getForMatch(MatchModel match) {
return cardRepository.list(
"competition = ?1 AND (type IN ?2 OR (type = CardType.BLUE AND category = ?4)) AND comb IN ?3",
match.getCategory().getCompet(), COMPETITION_LEVEL_CARDS,
extractCombIds(match), match.getCategory().getId());
}
public Uni<List<CardModel>> getAll(CompetitionModel competition) {
return cardRepository.list("competition = ?1", competition);
}
public Uni<RCard.SendCardAdd> checkCanBeAdded(RCard.SendCardAdd card, MatchModel matchModel) {
return cardRepository.find("competition = ?1 AND comb = ?2",
Sort.descending("type"),
matchModel.getCategory().getCompet(), card.combId())
.firstResult()
.map(card_ -> {
if (card.type() == CardModel.CardType.BLUE) {
return card_ == null || (card_.getType() == CardModel.CardType.BLUE
&& !Objects.equals(card_.getCategory(), matchModel.getCategory().getId()));
}
if (card.type() == CardModel.CardType.BLACK) {
return card_ != null && card_.getType() == CardModel.CardType.RED;
}
return card_ == null || card_.getType().ordinal() < card.type().ordinal();
})
.chain(b -> {
if (b)
return Uni.createFrom().item(card);
else
return Uni.createFrom().failure(new DBadRequestException(trad.t("card.cannot.be.added")));
});
}
public Uni<List<CardModel>> addTeamCartToNewComb(Long combId, String teamUuid, String teamName,
CompetitionModel competition) {
return clubCardRepository.list("competition = ?1 AND (teamUuid = ?2 OR teamName = ?3)",
Sort.ascending("type"), competition.getId(), teamUuid, teamName)
.chain(clubCards -> {
Uni<?> queue = Uni.createFrom().voidItem();
List<CardModel> addCards = new ArrayList<>();
for (ClubCardModel clubCard : clubCards) {
CardModel model = new CardModel();
model.setCompetition(competition);
model.setCompetitionId(competition.getId());
model.setComb(combId);
model.setTeamCard(true);
model.setType(clubCard.getType());
model.setDate(clubCard.getDate());
queue = queue.call(__ -> Panache.withTransaction(() -> cardRepository.persist(model))
.invoke(addCards::add)
.call(() -> {
clubCard.getCardIds().add(model.getId());
return Panache.withTransaction(() -> clubCardRepository.persist(clubCard));
}));
}
return queue.replaceWith(addCards);
});
}
public Uni<Void> rmTeamCardFromComb(Long combId, String uuid) {
return cardRepository.delete("comb = ?1 AND competition.uuid = ?2 AND teamCard = True", combId, uuid)
.replaceWithVoid();
}
public Uni<List<CardModel>> addTeamCard(CompetitionModel competition, String teamUuid, String teamName,
CardModel.CardType type, String reason) {
return clubCardRepository.find("competition = ?1 AND (teamUuid = ?2 OR teamName = ?3)",
Sort.descending("type"), competition.getId(), teamUuid, teamName)
.firstResult()
.map(card_ -> {
if (type == CardModel.CardType.BLACK) {
return card_ != null && card_.getType() == CardModel.CardType.RED;
}
return card_ == null || card_.getType().ordinal() < type.ordinal();
})
.chain(b -> {
if (!b)
return Uni.createFrom().failure(new DBadRequestException(trad.t("card.cannot.be.added")));
if (teamUuid != null) {
return registerRepository.list("competition = ?1 AND club.clubId = ?2", competition, teamUuid)
.map(l -> l.stream().map(r -> r.getMembre().getId()).toList());
} else {
return competitionGuestRepository.list("competition = ?1 AND club = ?2", competition,
teamName)
.map(l -> l.stream().map(r -> r.getId() * -1).toList());
}
})
.chain(combIds -> cardRepository.list("competition = ?1 AND comb IN ?2", competition, combIds)
.map(cards -> {
List<CardModel> newCards = new ArrayList<>();
for (Long id : combIds) {
Optional<CardModel> optional = cards.stream()
.filter(c -> id.equals(c.getComb()) && c.getType() == type).findAny();
CardModel model = new CardModel();
model.setCompetition(competition);
model.setCompetitionId(competition.getId());
model.setComb(id);
model.setTeamCard(true);
if (optional.isEmpty()) {
model.setType(type);
} else {
model.setType(
CardModel.CardType.values()[Math.min(optional.get().getType().ordinal() + 1,
CardModel.CardType.BLACK.ordinal())]);
}
newCards.add(model);
}
return newCards;
})
)
.call(newCards -> Panache.withTransaction(() -> cardRepository.persist(newCards)))
.call(newCards -> {
ClubCardModel model = new ClubCardModel();
model.setCompetition(competition.getId());
model.setTeamUuid(teamUuid);
model.setTeamName(teamName);
model.setType(type);
model.setReason(reason);
model.setCardIds(newCards.stream().map(CardModel::getId).toList());
return Panache.withTransaction(() -> clubCardRepository.persist(model));
});
}
public Uni<List<CardModel>> recvReturnState(CompetitionModel competition, RCard.SendTeamCardReturnState state) {
return clubCardRepository.find("competition = ?1 AND (teamUuid = ?2 OR teamName = ?3) AND type = ?4",
competition.getId(), state.teamUuid(), state.teamName(), state.type())
.firstResult()
.chain(o -> cardRepository.list("id IN ?1", o.getCardIds()))
.call(cards -> matchRepository.list("category.compet = ?1 AND category.id IN ?2", competition,
state.selectedCategory())
.invoke(matches -> {
for (CardModel card : cards) {
for (MatchModel m : matches.stream()
.filter(m -> extractCombIds(m).contains(card.getComb())).toList()) {
if (state.state() == 1) {
card.setCategory(m.getCategory().getId());
} else if (state.state() == 2) {
card.setCategory(m.getCategory().getId());
if (Objects.equals(m.getId(), state.selectedMatch()))
card.setMatch(m.getId());
}
}
}
})
.chain(() -> Panache.withTransaction(() -> cardRepository.persist(cards))));
}
public Uni<List<Long>> rmTeamCard(CompetitionModel competition, String teamUuid, String teamName,
CardModel.CardType type) {
return clubCardRepository.find("competition = ?1 AND (teamUuid = ?2 OR teamName = ?3) AND type = ?4",
competition.getId(), teamUuid, teamName, type)
.firstResult()
.chain(card -> Uni.createFrom().item(card.getCardIds())
.call(() -> Panache.withTransaction(() -> cardRepository.delete("id IN ?1", card.getCardIds())))
.call(() -> Panache.withTransaction(() -> clubCardRepository.delete(card))));
}
}

View File

@@ -45,10 +45,13 @@ public class CategoryService {
@Inject @Inject
CompetitionGuestRepository competitionGuestRepository; CompetitionGuestRepository competitionGuestRepository;
@Inject
TradService trad;
public Uni<CategoryData> getByIdAdmin(SecurityCtx securityCtx, CompetitionSystem system, Long id) { public Uni<CategoryData> getByIdAdmin(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
return repository.find("systemId = ?1 AND system = ?2", id, system) return repository.find("systemId = ?1 AND system = ?2", id, system)
.firstResult() .firstResult()
.onItem().ifNull().failWith(() -> new RuntimeException("Category not found")) .onItem().ifNull().failWith(() -> new RuntimeException(trad.t("categorie.non.trouver")))
.call(data -> permService.hasAdminViewPerm(securityCtx, data.getCompet())) .call(data -> permService.hasAdminViewPerm(securityCtx, data.getCompet()))
.map(CategoryData::fromModel); .map(CategoryData::fromModel);
} }
@@ -64,7 +67,7 @@ public class CategoryService {
.chain(o -> { .chain(o -> {
if (o == null) { if (o == null) {
return competRepository.findById(data.getCompet()) return competRepository.findById(data.getCompet())
.onItem().ifNull().failWith(() -> new RuntimeException("Competition not found")) .onItem().ifNull().failWith(() -> new RuntimeException(trad.t("competition.not.found")))
.call(o2 -> permService.hasEditPerm(securityCtx, o2)) .call(o2 -> permService.hasEditPerm(securityCtx, o2))
.chain(competitionModel -> { .chain(competitionModel -> {
CategoryModel model = new CategoryModel(); CategoryModel model = new CategoryModel();
@@ -139,7 +142,7 @@ public class CategoryService {
.onItem().ifNotNull().call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet())) .onItem().ifNotNull().call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet()))
.onItem().ifNull().switchTo( .onItem().ifNull().switchTo(
() -> competRepository.findById(data.getCompet()) () -> competRepository.findById(data.getCompet())
.onItem().ifNull().failWith(() -> new RuntimeException("Compet not found")) .onItem().ifNull().failWith(() -> new RuntimeException(trad.t("competition.not.found")))
.call(o -> permService.hasEditPerm(securityCtx, o)) .call(o -> permService.hasEditPerm(securityCtx, o))
.map(o -> { .map(o -> {
CategoryModel model = new CategoryModel(); CategoryModel model = new CategoryModel();
@@ -256,7 +259,7 @@ public class CategoryService {
public Uni<?> delete(SecurityCtx securityCtx, CompetitionSystem system, Long id) { public Uni<?> delete(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult() return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
.onItem().ifNull().failWith(() -> new RuntimeException("Category not found")) .onItem().ifNull().failWith(() -> new RuntimeException(trad.t("categorie.non.trouver")))
.call(o -> permService.hasEditPerm(securityCtx, o.getCompet())) .call(o -> permService.hasEditPerm(securityCtx, o.getCompet()))
.call(o -> Mutiny.fetch(o.getTree()) .call(o -> Mutiny.fetch(o.getTree())
.call(o2 -> o2.isEmpty() ? Uni.createFrom().nullItem() : .call(o2 -> o2.isEmpty() ? Uni.createFrom().nullItem() :

View File

@@ -58,7 +58,7 @@ public class CheckoutService {
String organizationSlug; String organizationSlug;
public Uni<Boolean> canDeleteLicence(long id) { public Uni<Boolean> canDeleteLicence(long id) {
return repository.find("?1 IN licenseIds", id).count().map(count -> count == 0); return repository.countByLicenseId(id).invoke(c -> System.out.println(c)).map(count -> count == 0);
} }
public Uni<String> create(List<Long> ids, SecurityCtx securityCtx) { public Uni<String> create(List<Long> ids, SecurityCtx securityCtx) {

View File

@@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
import fr.titionfire.ffsaf.data.model.AffiliationModel; import fr.titionfire.ffsaf.data.model.AffiliationModel;
import fr.titionfire.ffsaf.data.model.ClubModel; import fr.titionfire.ffsaf.data.model.ClubModel;
import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.data.repository.AffiliationRepository;
import fr.titionfire.ffsaf.data.repository.ClubRepository; import fr.titionfire.ffsaf.data.repository.ClubRepository;
import fr.titionfire.ffsaf.data.repository.CombRepository; import fr.titionfire.ffsaf.data.repository.CombRepository;
import fr.titionfire.ffsaf.net2.ServerCustom; import fr.titionfire.ffsaf.net2.ServerCustom;
@@ -44,6 +45,9 @@ public class ClubService {
@Inject @Inject
ClubRepository repository; ClubRepository repository;
@Inject
AffiliationRepository affiliationRepository;
@Inject @Inject
ServerCustom serverCustom; ServerCustom serverCustom;
@@ -59,6 +63,9 @@ public class ClubService {
@Inject @Inject
LoggerService ls; LoggerService ls;
@Inject
TradService trad;
public SimpleClubModel findByIdOptionalClub(long id) throws Throwable { public SimpleClubModel findByIdOptionalClub(long id) throws Throwable {
return VertxContextSupport.subscribeAndAwait( return VertxContextSupport.subscribeAndAwait(
() -> Panache.withTransaction(() -> repository.findById(id).map(SimpleClubModel::fromModel))); () -> Panache.withTransaction(() -> repository.findById(id).map(SimpleClubModel::fromModel)));
@@ -126,7 +133,7 @@ public class ClubService {
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult() return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult()
.invoke(Unchecked.consumer(m -> { .invoke(Unchecked.consumer(m -> {
if (m == null || m.getClub() == null) if (m == null || m.getClub() == null)
throw new DNotFoundException("Club non trouvé"); throw new DNotFoundException(trad.t("club.non.trouve"));
})) }))
.map(MembreModel::getClub) .map(MembreModel::getClub)
.call(club -> Mutiny.fetch(club.getContact())); .call(club -> Mutiny.fetch(club.getContact()));
@@ -146,7 +153,7 @@ public class ClubService {
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult() return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult()
.invoke(Unchecked.consumer(m -> { .invoke(Unchecked.consumer(m -> {
if (m == null || m.getClub() == null) if (m == null || m.getClub() == null)
throw new DNotFoundException("Club non trouvé"); throw new DNotFoundException(trad.t("club.non.trouve"));
if (!securityCtx.isInClubGroup(m.getClub().getId())) if (!securityCtx.isInClubGroup(m.getClub().getId()))
throw new DForbiddenException(); throw new DForbiddenException();
})) }))
@@ -162,7 +169,7 @@ public class ClubService {
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult() return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult()
.invoke(Unchecked.consumer(m -> { .invoke(Unchecked.consumer(m -> {
if (m == null || m.getClub() == null) if (m == null || m.getClub() == null)
throw new DNotFoundException("Club non trouvé"); throw new DNotFoundException(trad.t("club.non.trouve"));
if (!securityCtx.isInClubGroup(m.getClub().getId())) if (!securityCtx.isInClubGroup(m.getClub().getId()))
throw new DForbiddenException(); throw new DForbiddenException();
})) }))
@@ -179,7 +186,7 @@ public class ClubService {
ls.logUpdate("Contact(s)...", club); ls.logUpdate("Contact(s)...", club);
club.setContact(MAPPER.readValue(form.getContact(), typeRef)); club.setContact(MAPPER.readValue(form.getContact(), typeRef));
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new DBadRequestException("Erreur de format des contacts"); throw new DBadRequestException(trad.t("erreur.de.format.des.contacts"));
} }
ls.logChange("Lieux d'entrainements", club.getTraining_location(), form.getTraining_location(), ls.logChange("Lieux d'entrainements", club.getTraining_location(), form.getTraining_location(),
@@ -229,12 +236,13 @@ public class ClubService {
ls.logUpdate("Contact(s)...", m); ls.logUpdate("Contact(s)...", m);
m.setContact(MAPPER.readValue(input.getContact(), typeRef)); m.setContact(MAPPER.readValue(input.getContact(), typeRef));
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new DBadRequestException("Erreur de format des contacts"); throw new DBadRequestException(trad.t("erreur.de.format.des.contacts"));
} }
} }
return Panache.withTransaction(() -> repository.persist(m)).call(() -> ls.append()); return Panache.withTransaction(() -> repository.persist(m)).call(() -> ls.append());
})) }))
.call(clubModel -> nameChange.get() ? keycloakService.updateGroupFromClub(clubModel) // update group in keycloak .call(clubModel -> nameChange.get() ? keycloakService.updateGroupFromClub(
clubModel) // update group in keycloak
: Uni.createFrom().nullItem()) : Uni.createFrom().nullItem())
.invoke(membreModel -> SReqClub.sendIfNeed(serverCustom.clients, .invoke(membreModel -> SReqClub.sendIfNeed(serverCustom.clients,
SimpleClubModel.fromModel(membreModel))) SimpleClubModel.fromModel(membreModel)))
@@ -282,11 +290,13 @@ public class ClubService {
combModel.setClub(null); combModel.setClub(null);
combModel.setRole(RoleAsso.MEMBRE); combModel.setRole(RoleAsso.MEMBRE);
}).toList()) }).toList())
.call(list -> (list.isEmpty()) ? Uni.createFrom().voidItem() : .call(list -> list.stream().noneMatch(m -> m.getUserId() != null) ?
Uni.createFrom().voidItem() :
Uni.join().all(list.stream().filter(m -> m.getUserId() != null) Uni.join().all(list.stream().filter(m -> m.getUserId() != null)
.map(m -> keycloakService.clearUser(m.getUserId())).toList()) .map(m -> keycloakService.clearUser(m.getUserId())).toList())
.andCollectFailures()) .andCollectFailures())
.chain(list -> Panache.withTransaction(() -> combRepository.persist(list))) .chain(list -> list.isEmpty() ? Uni.createFrom().voidItem() :
Panache.withTransaction(() -> combRepository.persist(list)))
.map(o -> club) .map(o -> club)
) )
.call(clubModel -> (clubModel.getClubId() == null) ? Uni.createFrom() .call(clubModel -> (clubModel.getClubId() == null) ? Uni.createFrom()
@@ -323,9 +333,11 @@ public class ClubService {
} }
public Uni<List<ClubMapData>> getMapData() { public Uni<List<ClubMapData>> getMapData() {
return repository.list("international", false).toMulti().flatMap(list -> Multi.createFrom().iterable(list)) return affiliationRepository.list("saison >= ?1 AND club.international = ?2", Utils.getSaison() - 1, false)
.call(clubModel -> Mutiny.fetch(clubModel.getContact())) .toMulti().flatMap(list -> Multi.createFrom().iterable(list))
.map(clubModel -> { .call(affiliationModel -> Mutiny.fetch(affiliationModel.getClub().getContact()))
.map(affiliationModel -> {
ClubModel clubModel = affiliationModel.getClub();
ClubMapData data = new ClubMapData(); ClubMapData data = new ClubMapData();
data.setName(clubModel.getName()); data.setName(clubModel.getName());

View File

@@ -56,7 +56,7 @@ public class CompetPermService {
CompletableFuture<SimpleCompet> f = new CompletableFuture<>(); CompletableFuture<SimpleCompet> f = new CompletableFuture<>();
SReqCompet.getConfig(serverCustom.clients, id, f); SReqCompet.getConfig(serverCustom.clients, id, f);
try { try {
return f.get(1500, TimeUnit.MILLISECONDS); return f.get(500, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) { } catch (InterruptedException | ExecutionException | TimeoutException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -71,7 +71,8 @@ public class CompetPermService {
.chain(competitionModels -> { .chain(competitionModels -> {
CompletableFuture<HashMap<String, String>> f = new CompletableFuture<>(); CompletableFuture<HashMap<String, String>> f = new CompletableFuture<>();
SReqCompet.getAllHaveAccess(serverCustom.clients, securityCtx.getSubject(), f); SReqCompet.getAllHaveAccess(serverCustom.clients, securityCtx.getSubject(), f);
return Uni.createFrom().future(f, Duration.ofMillis(1500)) return Uni.createFrom().future(f, Duration.ofMillis(500))
.onFailure().recoverWithItem(new HashMap<>())
.map(map_ -> { .map(map_ -> {
HashMap<Long, String> map = new HashMap<>(); HashMap<Long, String> map = new HashMap<>();
map_.forEach((key, value) -> map.put(Long.parseLong(key), value)); map_.forEach((key, value) -> map.put(Long.parseLong(key), value));
@@ -97,12 +98,14 @@ public class CompetPermService {
map.putIfAbsent(model.getId(), "owner"); map.putIfAbsent(model.getId(), "owner");
else if (securityCtx.roleHas("federation_admin")) else if (securityCtx.roleHas("federation_admin"))
map.putIfAbsent(model.getId(), "admin"); map.putIfAbsent(model.getId(), "admin");
else if (securityCtx.isInClubGroup(model.getClub().getId()) && (securityCtx.roleHas( else if (securityCtx.isInClubGroup(
"club_president") model.getClub().getId()) && (securityCtx.isClubAdmin()))
|| securityCtx.roleHas("club_respo_intra") || securityCtx.roleHas(
"club_secretaire")
|| securityCtx.roleHas("club_tresorier")))
map.putIfAbsent(model.getId(), "admin"); map.putIfAbsent(model.getId(), "admin");
else if (model.getAdmin().contains(securityCtx.getSubject()))
map.putIfAbsent(model.getId(), "admin");
else if (model.getTable().contains(securityCtx.getSubject()))
map.putIfAbsent(model.getId(), "table");
} }
return map; return map;
})); }));
@@ -181,12 +184,14 @@ public class CompetPermService {
if (o.getSystem() == CompetitionSystem.SAFCA) if (o.getSystem() == CompetitionSystem.SAFCA)
return hasSafcaViewPerm(securityCtx, o.getId()); return hasSafcaViewPerm(securityCtx, o.getId());
if (o.getAdmin().contains(securityCtx.getSubject()))
return Uni.createFrom().nullItem();
if (!securityCtx.isInClubGroup(o.getClub().getId())) // Only membre club pass here if (!securityCtx.isInClubGroup(o.getClub().getId())) // Only membre club pass here
throw new DForbiddenException(); throw new DForbiddenException();
if (o.getSystem() == CompetitionSystem.INTERNAL) if (o.getSystem() == CompetitionSystem.INTERNAL)
if (securityCtx.roleHas("club_president") || securityCtx.roleHas("club_respo_intra") if (securityCtx.isClubAdmin())
|| securityCtx.roleHas("club_secretaire") || securityCtx.roleHas("club_tresorier"))
return Uni.createFrom().nullItem(); return Uni.createFrom().nullItem();
throw new DForbiddenException(); throw new DForbiddenException();

View File

@@ -7,10 +7,7 @@ import fr.titionfire.ffsaf.net2.data.SimpleCompet;
import fr.titionfire.ffsaf.net2.request.SReqCompet; import fr.titionfire.ffsaf.net2.request.SReqCompet;
import fr.titionfire.ffsaf.net2.request.SReqRegister; import fr.titionfire.ffsaf.net2.request.SReqRegister;
import fr.titionfire.ffsaf.rest.client.dto.NotificationData; import fr.titionfire.ffsaf.rest.client.dto.NotificationData;
import fr.titionfire.ffsaf.rest.data.CompetitionData; import fr.titionfire.ffsaf.rest.data.*;
import fr.titionfire.ffsaf.rest.data.RegisterRequestData;
import fr.titionfire.ffsaf.rest.data.SimpleCompetData;
import fr.titionfire.ffsaf.rest.data.SimpleRegisterComb;
import fr.titionfire.ffsaf.rest.exception.DBadRequestException; import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException; import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
@@ -63,6 +60,9 @@ public class CompetitionService {
@Inject @Inject
CompetitionGuestRepository competitionGuestRepository; CompetitionGuestRepository competitionGuestRepository;
@Inject
CatPresetRepository catPresetRepository;
@Inject @Inject
ServerCustom serverCustom; ServerCustom serverCustom;
@@ -97,21 +97,29 @@ public class CompetitionService {
@CacheName("have-access") @CacheName("have-access")
Cache cacheNoneAccess; Cache cacheNoneAccess;
@Inject
TradService trad;
public Uni<CompetitionData> getById(SecurityCtx securityCtx, Long id) { 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) { public Uni<CompetitionData> getByIdAdmin(SecurityCtx securityCtx, Long id) {
if (id == 0) { if (id == 0) {
return Uni.createFrom() return Uni.createFrom().item(new CompetitionData());
.item(new CompetitionData(null, "", "", "", "", new Date(), new Date(),
CompetitionSystem.INTERNAL, RegisterMode.FREE, new Date(), new Date(), true,
null, "", "", null, true, "", "", "", ""));
} }
return permService.hasAdminViewPerm(securityCtx, id) return permService.hasAdminViewPerm(securityCtx, id)
.call(competitionModel -> Mutiny.fetch(competitionModel.getCatPreset()))
.chain(competitionModel -> Mutiny.fetch(competitionModel.getInsc()) .chain(competitionModel -> Mutiny.fetch(competitionModel.getInsc())
.chain(insc -> Mutiny.fetch(competitionModel.getGuests()) .chain(insc -> Mutiny.fetch(competitionModel.getGuests())
.map(guest -> CompetitionData.fromModel(competitionModel).addInsc(insc, guest)))) .map(guest -> CompetitionData.fromModel(competitionModel).addInsc(insc, guest)
.addPresets(competitionModel.getCatPreset()))))
.chain(data -> .chain(data ->
vertx.getOrCreateContext().executeBlocking(() -> { vertx.getOrCreateContext().executeBlocking(() -> {
keycloakService.getUser(UUID.fromString(data.getOwner())) keycloakService.getUser(UUID.fromString(data.getOwner()))
@@ -129,6 +137,14 @@ public class CompetitionService {
out.addAll(cm.stream().map(CompetitionData::fromModelLight).toList()); out.addAll(cm.stream().map(CompetitionData::fromModelLight).toList());
out.forEach(competition -> competition.setCanEdit(true)); 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 -> .call(ids ->
repository.list("id NOT IN ?1 AND (publicVisible = TRUE OR registerMode IN ?2)", ids, repository.list("id NOT IN ?1 AND (publicVisible = TRUE OR registerMode IN ?2)", ids,
securityCtx.isClubAdmin() ? List.of(RegisterMode.FREE, RegisterMode.HELLOASSO, securityCtx.isClubAdmin() ? List.of(RegisterMode.FREE, RegisterMode.HELLOASSO,
@@ -162,13 +178,22 @@ public class CompetitionService {
.map(pouleModels -> pouleModels.stream().map(CompetitionData::fromModel).toList()); .map(pouleModels -> pouleModels.stream().map(CompetitionData::fromModel).toList());
} }
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).onFailure().recoverWithNull()
).toList()).andCollectFailures())
.map(l -> l.stream().filter(Objects::nonNull).map(CompetitionData::fromModel).toList());
}
public Uni<CompetitionData> addOrUpdate(SecurityCtx securityCtx, CompetitionData data) { public Uni<CompetitionData> addOrUpdate(SecurityCtx securityCtx, CompetitionData data) {
if (data.getId() == null) { if (data.getId() == null) {
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult() return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult()
.invoke(Unchecked.consumer(combModel -> { .invoke(Unchecked.consumer(combModel -> {
if (!securityCtx.getRoles().contains("create_compet") && !securityCtx.getRoles() if (!securityCtx.getRoles().contains("create_compet") && !securityCtx.getRoles()
.contains("federation_admin")) .contains("federation_admin"))
throw new DForbiddenException("Vous ne pouvez pas créer de compétition"); throw new DForbiddenException(trad.t("vous.ne.pouvez.pas.creer.de.competition"));
})) }))
.map(MembreModel::getClub) .map(MembreModel::getClub)
.chain(clubModel -> { .chain(clubModel -> {
@@ -181,17 +206,21 @@ public class CompetitionService {
model.setGuests(new ArrayList<>()); model.setGuests(new ArrayList<>());
model.setUuid(UUID.randomUUID().toString()); model.setUuid(UUID.randomUUID().toString());
model.setOwner(securityCtx.getSubject()); model.setOwner(securityCtx.getSubject());
model.setCatPreset(new ArrayList<>());
copyData(data, model); copyData(data, model);
return Panache.withTransaction(() -> repository.persist(model)); return Panache.withTransaction(() -> repository.persist(model));
}).map(CompetitionData::fromModel) })
.call(model -> syncPreset(data, model))
.map(CompetitionData::fromModel)
.call(c -> (c.getSystem() == CompetitionSystem.SAFCA) ? cacheAccess.invalidate( .call(c -> (c.getSystem() == CompetitionSystem.SAFCA) ? cacheAccess.invalidate(
securityCtx.getSubject()) : Uni.createFrom().nullItem()) securityCtx.getSubject()) : Uni.createFrom().nullItem())
.call(c -> (c.getSystem() == CompetitionSystem.INTERNAL) ? cacheNoneAccess.invalidate( .call(c -> (c.getSystem() == CompetitionSystem.INTERNAL) ? cacheNoneAccess.invalidate(
securityCtx.getSubject()) : Uni.createFrom().nullItem()); securityCtx.getSubject()) : Uni.createFrom().nullItem());
} else { } else {
return permService.hasEditPerm(securityCtx, data.getId()) return permService.hasEditPerm(securityCtx, data.getId())
.call(model -> Mutiny.fetch(model.getCatPreset()))
.chain(model -> { .chain(model -> {
copyData(data, model); copyData(data, model);
@@ -199,7 +228,8 @@ public class CompetitionService {
keycloakService.getUser(data.getOwner()).map(UserRepresentation::getId).orElse(null)) keycloakService.getUser(data.getOwner()).map(UserRepresentation::getId).orElse(null))
.invoke(Unchecked.consumer(newOwner -> { .invoke(Unchecked.consumer(newOwner -> {
if (newOwner == null) if (newOwner == null)
throw new DBadRequestException("User " + data.getOwner() + " not found"); throw new DBadRequestException(
String.format(trad.t("user.not.found"), data.getOwner()));
if (!newOwner.equals(model.getOwner())) { if (!newOwner.equals(model.getOwner())) {
if (!securityCtx.roleHas("federation_admin") if (!securityCtx.roleHas("federation_admin")
&& !securityCtx.roleHas("safca_super_admin") && !securityCtx.roleHas("safca_super_admin")
@@ -209,7 +239,9 @@ public class CompetitionService {
} }
})) }))
.chain(__ -> Panache.withTransaction(() -> repository.persist(model))); .chain(__ -> Panache.withTransaction(() -> repository.persist(model)));
}).map(CompetitionData::fromModel) })
.call(model -> syncPreset(data, model))
.map(model -> CompetitionData.fromModel(model).addPresets(model.getCatPreset()))
.call(c -> (c.getSystem() == CompetitionSystem.SAFCA) ? cacheAccess.invalidate( .call(c -> (c.getSystem() == CompetitionSystem.SAFCA) ? cacheAccess.invalidate(
securityCtx.getSubject()) : Uni.createFrom().nullItem()) securityCtx.getSubject()) : Uni.createFrom().nullItem())
.call(c -> (c.getSystem() == CompetitionSystem.INTERNAL) ? cacheNoneAccess.invalidate( .call(c -> (c.getSystem() == CompetitionSystem.INTERNAL) ? cacheNoneAccess.invalidate(
@@ -217,6 +249,45 @@ public class CompetitionService {
} }
} }
private Uni<?> syncPreset(CompetitionData data, CompetitionModel model) {
List<Long> toRemoveId = model.getCatPreset().stream()
.map(CatPresetModel::getId)
.filter(id -> data.getPresets().stream().noneMatch(preset -> Objects.equals(preset.getId(), id)))
.toList();
for (PresetData preset : data.getPresets()) {
CatPresetModel presetModel;
if (preset.getId() != null && preset.getId() > 0) {
presetModel = model.getCatPreset().stream()
.filter(p -> p.getId().equals(preset.getId()))
.findFirst()
.orElse(new CatPresetModel());
} else {
presetModel = new CatPresetModel();
model.getCatPreset().add(presetModel);
}
presetModel.setCompetition(model);
presetModel.setName(preset.getName());
presetModel.setSwordType(preset.getSword());
presetModel.setShieldType(preset.getShield());
presetModel.setCategories(preset.getCategories());
presetModel.setMandatoryProtection1(preset.getMandatoryProtection1());
presetModel.setMandatoryProtection2(preset.getMandatoryProtection2());
}
// Remove deleted presets
model.getCatPreset().removeIf(presetModel -> toRemoveId.contains(presetModel.getId()));
return Panache.withTransaction(() -> repository.persist(model)
.call(__ -> {
if (!toRemoveId.isEmpty()) {
return catPresetRepository.delete("id IN ?1", toRemoveId);
}
return Uni.createFrom().nullItem();
}));
}
private void copyData(CompetitionData data, CompetitionModel model) { private void copyData(CompetitionData data, CompetitionModel model) {
if (model.getBanMembre() == null) if (model.getBanMembre() == null)
model.setBanMembre(new ArrayList<>()); model.setBanMembre(new ArrayList<>());
@@ -230,6 +301,7 @@ public class CompetitionService {
model.setStartRegister(data.getStartRegister()); model.setStartRegister(data.getStartRegister());
model.setEndRegister(data.getEndRegister()); model.setEndRegister(data.getEndRegister());
model.setRegisterMode(data.getRegisterMode()); model.setRegisterMode(data.getRegisterMode());
model.setRequiredWeight(data.getRequiredWeight());
model.setData1(data.getData1()); model.setData1(data.getData1());
model.setData2(data.getData2()); model.setData2(data.getData2());
model.setData3(data.getData3()); model.setData3(data.getData3());
@@ -243,11 +315,18 @@ public class CompetitionService {
Uni<List<SimpleRegisterComb>> uni = Mutiny.fetch(c.getInsc()) Uni<List<SimpleRegisterComb>> uni = Mutiny.fetch(c.getInsc())
.onItem().transformToMulti(Multi.createFrom()::iterable) .onItem().transformToMulti(Multi.createFrom()::iterable)
.onItem().call(combModel -> Mutiny.fetch(combModel.getMembre().getLicences())) .onItem().call(combModel -> Mutiny.fetch(combModel.getMembre().getLicences()))
.map(cm -> SimpleRegisterComb.fromModel(cm, cm.getMembre().getLicences())) .onItem().call(combModel -> Mutiny.fetch(combModel.getCategoriesInscrites()))
.map(cm -> SimpleRegisterComb.fromModel(cm, cm.getMembre().getLicences())
.setCategorieInscrite(cm.getCategoriesInscrites()))
.collect().asList(); .collect().asList();
return uni return uni
.call(l -> Mutiny.fetch(c.getGuests()) .call(l -> Mutiny.fetch(c.getGuests())
.map(guest -> guest.stream().map(SimpleRegisterComb::fromModel).toList()) .onItem().transformToMulti(Multi.createFrom()::iterable)
.filter(g -> !g.isTeam())
.onItem().call(guest -> Mutiny.fetch(guest.getCategoriesInscrites()))
.map(guest -> SimpleRegisterComb.fromModel(guest)
.setCategorieInscrite(guest.getCategoriesInscrites()))
.collect().asList()
.invoke(l::addAll)); .invoke(l::addAll));
}); });
@@ -262,18 +341,23 @@ public class CompetitionService {
model.getClub())) model.getClub()))
.onItem().transformToMulti(Multi.createFrom()::iterable) .onItem().transformToMulti(Multi.createFrom()::iterable)
.onItem().call(combModel -> Mutiny.fetch(combModel.getMembre().getLicences())) .onItem().call(combModel -> Mutiny.fetch(combModel.getMembre().getLicences()))
.map(combModel -> SimpleRegisterComb.fromModel(combModel, combModel.getMembre().getLicences())) .onItem().call(combModel -> Mutiny.fetch(combModel.getCategoriesInscrites()))
.map(combModel -> SimpleRegisterComb.fromModel(combModel, combModel.getMembre().getLicences())
.setCategorieInscrite(combModel.getCategoriesInscrites()))
.collect().asList(); .collect().asList();
return membreService.getByAccountId(securityCtx.getSubject()) return membreService.getByAccountId(securityCtx.getSubject())
.chain(model -> registerRepository.find("competition.id = ?1 AND membre = ?2", id, model).firstResult() .chain(model -> registerRepository.find("competition.id = ?1 AND membre = ?2", id, model).firstResult()
.map(rm -> rm == null ? List.of() : List.of(SimpleRegisterComb.fromModel(rm, List.of())))); .call(rm -> rm == null ? Uni.createFrom().voidItem() :
Mutiny.fetch(rm.getCategoriesInscrites()))
.map(rm -> rm == null ? List.of() : List.of(SimpleRegisterComb.fromModel(rm, List.of())
.setCategorieInscrite(rm.getCategoriesInscrites()))));
} }
public Uni<SimpleRegisterComb> addRegisterComb(SecurityCtx securityCtx, Long id, RegisterRequestData data, public Uni<SimpleRegisterComb> addRegisterComb(SecurityCtx securityCtx, Long id, RegisterRequestData data,
String source) { String source) {
if ("admin".equals(source)) if ("admin".equals(source))
if (data.getLicence() != -1) { // not a guest if (data.getLicence() == null || data.getLicence() != -1) { // not a guest
return permService.hasEditPerm(securityCtx, id) return permService.hasEditPerm(securityCtx, id)
.chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname()) .chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname())
.call(combModel -> Mutiny.fetch(combModel.getLicences())) .call(combModel -> Mutiny.fetch(combModel.getLicences()))
@@ -283,8 +367,12 @@ public class CompetitionService {
c.getBanMembre().remove(combModel.getId()); c.getBanMembre().remove(combModel.getId());
return Panache.withTransaction(() -> repository.persist(c)); return Panache.withTransaction(() -> repository.persist(c));
}) })
.chain(combModel -> updateRegister(data, c, combModel, true))) .chain(combModel -> updateRegister(data, c, combModel, true, false)))
.map(r -> SimpleRegisterComb.fromModel(r, r.getMembre().getLicences())); .call(r -> r.getCompetition().getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegisterNoFetch(r.getCompetition().getUuid(), r) : Uni.createFrom()
.voidItem())
.map(r -> SimpleRegisterComb.fromModel(r, r.getMembre().getLicences())
.setCategorieInscrite(r.getCategoriesInscrites()));
} else { } else {
return permService.hasEditPerm(securityCtx, id) return permService.hasEditPerm(securityCtx, id)
.chain(c -> competitionGuestRepository.findById(data.getId() * -1) .chain(c -> competitionGuestRepository.findById(data.getId() * -1)
@@ -295,21 +383,35 @@ public class CompetitionService {
model.setCompetition(c); model.setCompetition(c);
return model; return model;
})) }))
.chain(model -> { .invoke(model -> {
model.setFname(data.getFname()); model.setFname(data.getFname());
model.setLname(data.getLname()); if (data.getLname().equals("__team"))
model.setLname("_team");
else
model.setLname(data.getLname());
model.setGenre(data.getGenre()); model.setGenre(data.getGenre());
model.setClub(data.getClub()); model.setClub(data.getClub());
model.setCountry(data.getCountry()); model.setCountry(data.getCountry());
model.setWeight(data.getWeight()); model.setWeightReal(data.getWeightReal());
model.setCategorie(data.getCategorie()); model.setCategorie(data.getCategorie());
if (model.getCompetition().getRequiredWeight().contains(model.getCategorie()))
return Panache.withTransaction(() -> competitionGuestRepository.persist(model)) model.setWeight(data.getWeight());
.call(r -> model.getCompetition().getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegister(model.getCompetition().getUuid(),
r) : Uni.createFrom().voidItem());
}) })
.map(SimpleRegisterComb::fromModel); .call(g -> Mutiny.fetch(g.getCategoriesInscrites()))
.call(g -> catPresetRepository.list("competition = ?1 AND id IN ?2", g.getCompetition(),
data.getCategoriesInscrites())
.invoke(cats -> {
g.getCategoriesInscrites().clear();
g.getCategoriesInscrites().addAll(cats);
g.getCategoriesInscrites()
.removeIf(cat -> cat.getCategories().stream()
.noneMatch(e -> e.getCategorie().equals(g.getCategorie())));
}))
.chain(model -> Panache.withTransaction(() -> competitionGuestRepository.persist(model))
.call(r -> model.getCompetition().getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegisterNoFetch(model.getCompetition().getUuid(), r)
: Uni.createFrom().voidItem()))
.map(g -> SimpleRegisterComb.fromModel(g).setCategorieInscrite(g.getCategoriesInscrites()));
} }
if ("club".equals(source)) if ("club".equals(source))
return repository.findById(id) return repository.findById(id)
@@ -318,7 +420,7 @@ public class CompetitionService {
|| !securityCtx.isClubAdmin()) || !securityCtx.isClubAdmin())
throw new DForbiddenException(); throw new DForbiddenException();
if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister())) if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister()))
throw new DBadRequestException("Inscription fermée"); throw new DBadRequestException(trad.t("inscription.fermee"));
})) }))
.chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname()) .chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname())
.call(combModel -> Mutiny.fetch(combModel.getLicences())) .call(combModel -> Mutiny.fetch(combModel.getLicences()))
@@ -326,40 +428,123 @@ public class CompetitionService {
if (!securityCtx.isInClubGroup(model.getClub().getId())) if (!securityCtx.isInClubGroup(model.getClub().getId()))
throw new DForbiddenException(); throw new DForbiddenException();
if (c.getBanMembre().contains(model.getId())) if (c.getBanMembre().contains(model.getId()))
throw new DForbiddenException( throw new DForbiddenException(trad.t("insc.err1"));
"Vous n'avez pas le droit d'inscrire ce membre (par décision de l'administrateur de la compétition)");
})) }))
.chain(combModel -> updateRegister(data, c, combModel, false))) .chain(combModel -> updateRegister(data, c, combModel, false, false)))
.map(r -> SimpleRegisterComb.fromModel(r, r.getMembre().getLicences())); .call(r -> r.getCompetition().getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegisterNoFetch(r.getCompetition().getUuid(), r) : Uni.createFrom()
.voidItem())
.map(r -> SimpleRegisterComb.fromModel(r, r.getMembre().getLicences())
.setCategorieInscrite(r.getCategoriesInscrites()));
return repository.findById(id) return repository.findById(id)
.invoke(Unchecked.consumer(cm -> { .invoke(Unchecked.consumer(cm -> {
if (cm.getRegisterMode() != RegisterMode.FREE) if (cm.getRegisterMode() != RegisterMode.FREE)
throw new DForbiddenException(); throw new DForbiddenException();
if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister())) if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister()))
throw new DBadRequestException("Inscription fermée"); throw new DBadRequestException(trad.t("inscription.fermee"));
})) }))
.chain(c -> membreService.getByAccountId(securityCtx.getSubject()) .chain(c -> membreService.getByAccountId(securityCtx.getSubject())
.invoke(Unchecked.consumer(model -> { .invoke(Unchecked.consumer(model -> {
if (c.getBanMembre().contains(model.getId())) if (c.getBanMembre().contains(model.getId()))
throw new DForbiddenException( throw new DForbiddenException(trad.t("insc.err2"));
"Vous n'avez pas le droit de vous inscrire (par décision de l'administrateur de la compétition)");
})) }))
.chain(combModel -> updateRegister(data, c, combModel, false))) .chain(combModel -> updateRegister(data, c, combModel, false, false)))
.map(r -> SimpleRegisterComb.fromModel(r, List.of())); .call(r -> r.getCompetition().getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegisterNoFetch(r.getCompetition().getUuid(), r) : Uni.createFrom().voidItem())
.map(r -> SimpleRegisterComb.fromModel(r, List.of()).setCategorieInscrite(r.getCategoriesInscrites()));
}
public Uni<List<SimpleRegisterComb>> addRegistersComb(SecurityCtx securityCtx, Long id,
List<RegisterRequestData> datas,
String source) {
if (!"admin".equals(source))
return Uni.createFrom().failure(new DForbiddenException());
return permService.hasEditPerm(securityCtx, id)
.chain(cm -> Multi.createFrom().iterable(datas).onItem().transformToUni(data ->
makeImportUpdate(cm, data).onFailure().recoverWithItem(t -> {
SimpleRegisterComb errorComb = new SimpleRegisterComb();
errorComb.setLicence(-42);
errorComb.setFname("ERROR");
errorComb.setLname(t.getMessage());
return errorComb;
})).concatenate().collect().asList());
}
@WithSession
public Uni<SimpleRegisterComb> makeImportUpdate(CompetitionModel c, RegisterRequestData data) {
if (data.getLicence() == null || data.getLicence() != -1) { // not a guest
return findComb(data.getLicence(), data.getFname(), data.getLname())
.call(combModel -> Mutiny.fetch(combModel.getLicences()))
.call(combModel -> {
if (c.getBanMembre() == null)
c.setBanMembre(new ArrayList<>());
c.getBanMembre().remove(combModel.getId());
return Panache.withTransaction(() -> repository.persist(c));
})
.chain(combModel -> updateRegister(data, c, combModel, true, true))
.map(r -> SimpleRegisterComb.fromModel(r, r.getMembre().getLicences())
.setCategorieInscrite(r.getCategoriesInscrites()));
} else {
return findGuestOrInit(data.getFname(), data.getLname(), c)
.invoke(Unchecked.consumer(model -> {
if (data.getCategorie() == null)
throw new DBadRequestException(trad.t("categorie.requise"));
model.setCategorie(data.getCategorie());
if (data.getGenre() == null) {
if (model.getGenre() == null)
data.setGenre(Genre.NA);
} else
model.setGenre(data.getGenre());
if (data.getClub() == null) {
if (model.getClub() == null)
data.setClub("");
} else
model.setClub(data.getClub());
if (data.getCountry() == null) {
if (model.getCountry() == null)
data.setCountry("FR");
} else
model.setCountry(data.getCountry());
if (c.getRequiredWeight().contains(model.getCategorie())) {
if (data.getCountry() != null)
model.setWeight(data.getWeight());
}
}))
.call(g -> Mutiny.fetch(g.getCategoriesInscrites()))
.call(g -> catPresetRepository.list("competition = ?1 AND id IN ?2", c,
data.getCategoriesInscrites())
.invoke(cats -> {
g.getCategoriesInscrites().clear();
g.getCategoriesInscrites().addAll(cats);
g.getCategoriesInscrites().removeIf(cat -> cat.getCategories().stream()
.noneMatch(e -> e.getCategorie().equals(g.getCategorie())));
}))
.chain(model -> Panache.withTransaction(() -> competitionGuestRepository.persist(model))
.call(r -> c.getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegister(c.getUuid(), r) : Uni.createFrom().voidItem()))
.map(g -> SimpleRegisterComb.fromModel(g).setCategorieInscrite(g.getCategoriesInscrites()));
}
} }
private Uni<RegisterModel> updateRegister(RegisterRequestData data, CompetitionModel c, private Uni<RegisterModel> updateRegister(RegisterRequestData data, CompetitionModel c,
MembreModel combModel, boolean admin) { MembreModel combModel, boolean admin, boolean append) {
return registerRepository.find("competition = ?1 AND membre = ?2", c, combModel).firstResult() return registerRepository.find("competition = ?1 AND membre = ?2", c, combModel).firstResult()
.onFailure().recoverWithNull() .onFailure().recoverWithNull()
.map(Unchecked.function(r -> { .map(Unchecked.function(r -> {
if (r != null) { if (r != null) {
if (!admin && r.isLockEdit()) if (!admin && r.isLockEdit())
throw new DForbiddenException( throw new DForbiddenException(trad.t("insc.err3"));
"Modification bloquée par l'administrateur de la compétition"); if (data.getOverCategory() != null || !append)
r.setWeight(data.getWeight()); if (data.getOverCategory() == null)
r.setOverCategory(data.getOverCategory()); r.setOverCategory(0);
else
r.setOverCategory(data.getOverCategory());
r.setCategorie( r.setCategorie(
(combModel.getBirth_date() == null) ? combModel.getCategorie() : (combModel.getBirth_date() == null) ? combModel.getCategorie() :
Utils.getCategoryFormBirthDate(combModel.getBirth_date(), Utils.getCategoryFormBirthDate(combModel.getBirth_date(),
@@ -367,50 +552,91 @@ public class CompetitionService {
int days = Utils.getDaysBeforeCompetition(c.getDate()); int days = Utils.getDaysBeforeCompetition(c.getDate());
if (days > -7) if (days > -7)
r.setClub(combModel.getClub()); r.setClub(combModel.getClub());
if (admin) if (c.getRequiredWeight().contains(r.getCategorie2()))
if (data.getCountry() != null || !append)
r.setWeight(data.getWeight());
if (admin) {
r.setWeightReal(data.getWeightReal());
r.setLockEdit(data.isLockEdit()); r.setLockEdit(data.isLockEdit());
}
} else { } else {
r = new RegisterModel(c, combModel, data.getWeight(), data.getOverCategory(), r = new RegisterModel(c, combModel, data.getWeight(), data.getOverCategory(),
(combModel.getBirth_date() == null) ? combModel.getCategorie() : (combModel.getBirth_date() == null) ? combModel.getCategorie() :
Utils.getCategoryFormBirthDate(combModel.getBirth_date(), Utils.getCategoryFormBirthDate(combModel.getBirth_date(),
c.getDate()), c.getDate()),
(combModel.getClub() == null) ? null : combModel.getClub()); (combModel.getClub() == null) ? null : combModel.getClub());
if (admin) if (admin) {
r.setWeightReal(data.getWeightReal());
r.setLockEdit(data.isLockEdit()); r.setLockEdit(data.isLockEdit());
else } else
r.setLockEdit(false); r.setLockEdit(false);
} }
if (c.getSystem() == CompetitionSystem.SAFCA) { if (c.getSystem() == CompetitionSystem.SAFCA) {
SReqRegister.sendIfNeed(serverCustom.clients, SReqRegister.sendIfNeed(serverCustom.clients,
new CompetitionData.SimpleRegister(r.getMembre().getId(), new CompetitionData.SimpleRegister(r.getMembre().getId(),
r.getOverCategory(), r.getWeight(), r.getCategorie(), r.getOverCategory(), r.getWeight2(), r.getCategorie(),
(r.getClub() == null) ? null : r.getClub().getId(), (r.getClub() == null) ? null : r.getClub().getId(),
(r.getClub() == null) ? null : r.getClub().getName()), c.getId()); (r.getClub() == null) ? null : r.getClub().getName()), c.getId());
} }
return r; return r;
})) }))
.call(r -> Mutiny.fetch(r.getCategoriesInscrites()).chain(__ ->
catPresetRepository.list("competition = ?1 AND id IN ?2", c, data.getCategoriesInscrites())
.invoke(cats -> {
if (data.isQuick()) {
cats.removeIf(cat -> r.getCategoriesInscrites().stream()
.anyMatch(cp -> cp.equals(cat)));
} else {
r.getCategoriesInscrites().clear();
}
r.getCategoriesInscrites().addAll(cats);
r.getCategoriesInscrites()
.removeIf(cat -> cat.getCategories().stream()
.noneMatch(e -> e.getCategorie().equals(r.getCategorie2())));
})))
.chain(r -> Panache.withTransaction(() -> registerRepository.persist(r))) .chain(r -> Panache.withTransaction(() -> registerRepository.persist(r)))
.call(r -> c.getSystem() == CompetitionSystem.INTERNAL ? .call(r -> c.getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegister(c.getUuid(), r) : Uni.createFrom().voidItem()); sRegister.sendRegister(c.getUuid(), r) : Uni.createFrom().voidItem());
} }
private Uni<CompetitionGuestModel> findGuestOrInit(String fname, String lname, CompetitionModel competition) {
if (fname == null || lname == null)
return Uni.createFrom().failure(new DBadRequestException(trad.t("nom.et.prenom.requis")));
return competitionGuestRepository.find(
"unaccent(lname) ILIKE unaccent(?1) AND unaccent(fname) ILIKE unaccent(?2) AND competition = ?3",
lname, fname, competition).firstResult()
.map(guestModel -> {
if (guestModel == null) {
CompetitionGuestModel model = new CompetitionGuestModel();
model.setFname(fname);
if (lname.equals("__team"))
model.setLname("_team");
else
model.setLname(lname);
model.setCompetition(competition);
return model;
}
return guestModel;
});
}
private Uni<MembreModel> findComb(Long licence, String fname, String lname) { private Uni<MembreModel> findComb(Long licence, String fname, String lname) {
if (licence != null && licence > 0) { if (licence != null && licence > 0) {
return combRepository.find("licence = ?1", licence).firstResult() return combRepository.find("licence = ?1", licence).firstResult()
.invoke(Unchecked.consumer(combModel -> { .invoke(Unchecked.consumer(combModel -> {
if (combModel == null) if (combModel == null)
throw new DForbiddenException("Licence " + licence + " non trouvé"); throw new DForbiddenException(String.format(trad.t("licence.non.trouve"), licence));
})); }));
} else { } else {
if (fname == null || lname == null) if (fname == null || lname == null)
return Uni.createFrom().failure(new DBadRequestException("Nom et prénom requis")); return Uni.createFrom().failure(new DBadRequestException(trad.t("nom.et.prenom.requis")));
return combRepository.find("unaccent(lname) ILIKE unaccent(?1) AND unaccent(fname) ILIKE unaccent(?2)", return combRepository.find("unaccent(lname) ILIKE unaccent(?1) AND unaccent(fname) ILIKE unaccent(?2)",
lname, lname,
fname).firstResult() fname).firstResult()
.invoke(Unchecked.consumer(combModel -> { .invoke(Unchecked.consumer(combModel -> {
if (combModel == null) if (combModel == null)
throw new DForbiddenException("Combattant " + fname + " " + lname + " non trouvé"); throw new DForbiddenException(String.format(trad.t("combattant.non.trouve"), fname, lname));
})); }));
} }
} }
@@ -437,12 +663,13 @@ public class CompetitionService {
|| !securityCtx.isClubAdmin()) || !securityCtx.isClubAdmin())
throw new DForbiddenException(); throw new DForbiddenException();
if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister())) if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister()))
throw new DBadRequestException("Inscription fermée"); throw new DBadRequestException(trad.t("inscription.fermee"));
})) }))
.call(cm -> membreService.getById(combId) .call(cm -> membreService.getById(combId)
.invoke(Unchecked.consumer(model -> { .invoke(Unchecked.consumer(model -> {
if (model == null) if (model == null)
throw new DNotFoundException("Membre " + combId + " n'existe pas"); throw new DNotFoundException(
String.format(trad.t("le.membre.n.existe.pas"), combId));
if (!securityCtx.isInClubGroup(model.getClub().getId())) if (!securityCtx.isInClubGroup(model.getClub().getId()))
throw new DForbiddenException(); throw new DForbiddenException();
}))) })))
@@ -454,7 +681,7 @@ public class CompetitionService {
if (cm.getRegisterMode() != RegisterMode.FREE || !Objects.equals(model.getId(), combId)) if (cm.getRegisterMode() != RegisterMode.FREE || !Objects.equals(model.getId(), combId))
throw new DForbiddenException(); throw new DForbiddenException();
if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister())) if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister()))
throw new DBadRequestException("Inscription fermée"); throw new DBadRequestException(trad.t("inscription.fermee"));
}))) })))
.chain(c -> deleteRegister(combId, c, false)); .chain(c -> deleteRegister(combId, c, false));
} }
@@ -462,7 +689,7 @@ public class CompetitionService {
private Uni<Void> deleteRegister(Long combId, CompetitionModel c, boolean admin) { private Uni<Void> deleteRegister(Long combId, CompetitionModel c, boolean admin) {
if (admin && combId < 0) { if (admin && combId < 0) {
return competitionGuestRepository.find("competition = ?1 AND id = ?2", c, combId * -1).firstResult() return competitionGuestRepository.find("competition = ?1 AND id = ?2", c, combId * -1).firstResult()
.onFailure().transform(t -> new DBadRequestException("Combattant non inscrit")) .onFailure().transform(t -> new DBadRequestException(trad.t("combattant.non.inscrit")))
.call(Unchecked.function( .call(Unchecked.function(
model -> Panache.withTransaction(() -> competitionGuestRepository.delete(model)) model -> Panache.withTransaction(() -> competitionGuestRepository.delete(model))
.call(r -> c.getSystem() == CompetitionSystem.INTERNAL ? .call(r -> c.getSystem() == CompetitionSystem.INTERNAL ?
@@ -471,10 +698,10 @@ public class CompetitionService {
.replaceWithVoid(); .replaceWithVoid();
} }
return registerRepository.find("competition = ?1 AND membre.id = ?2", c, combId).firstResult() return registerRepository.find("competition = ?1 AND membre.id = ?2", c, combId).firstResult()
.onFailure().transform(t -> new DBadRequestException("Combattant non inscrit")) .onFailure().transform(t -> new DBadRequestException(trad.t("combattant.non.inscrit")))
.call(Unchecked.function(registerModel -> { .call(Unchecked.function(registerModel -> {
if (!admin && registerModel.isLockEdit()) if (!admin && registerModel.isLockEdit())
throw new DForbiddenException("Modification bloquée par l'administrateur de la compétition"); throw new DForbiddenException(trad.t("insc.err3"));
return Panache.withTransaction(() -> registerRepository.delete(registerModel)) return Panache.withTransaction(() -> registerRepository.delete(registerModel))
.call(r -> c.getSystem() == CompetitionSystem.INTERNAL ? .call(r -> c.getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegisterRemove(c.getUuid(), combId) : Uni.createFrom().voidItem()); sRegister.sendRegisterRemove(c.getUuid(), combId) : Uni.createFrom().voidItem());
@@ -496,11 +723,73 @@ public class CompetitionService {
.andCollectFailures())) .andCollectFailures()))
.call(competitionModel -> Panache.withTransaction( .call(competitionModel -> Panache.withTransaction(
() -> categoryRepository.delete("compet = ?1", competitionModel))) () -> categoryRepository.delete("compet = ?1", competitionModel)))
.call(competitionModel -> Panache.withTransaction(
() -> registerRepository.delete("competition = ?1", competitionModel)))
.call(competitionModel -> Panache.withTransaction(
() -> competitionGuestRepository.delete("competition = ?1", competitionModel)))
.chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId()))) .chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId())))
.invoke(o -> SReqCompet.rmCompet(serverCustom.clients, id)) .invoke(o -> SReqCompet.rmCompet(serverCustom.clients, id))
.call(__ -> cache.invalidate(id)); .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(trad.t("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(trad.t("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(String.format(trad.t("user.not.found"), username));
admin.add(opt.get().getId());
}
for (String username : data.getTable()) {
Optional<UserRepresentation> opt = keycloakService.getUser(username);
if (opt.isEmpty())
throw new DBadRequestException(String.format(trad.t("user.not.found"), username));
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) { public Uni<SimpleCompetData> getSafcaData(SecurityCtx securityCtx, Long id) {
return permService.getSafcaConfig(id) return permService.getSafcaConfig(id)
.call(Unchecked.function(o -> { .call(Unchecked.function(o -> {
@@ -537,13 +826,13 @@ public class CompetitionService {
for (String username : data.getAdmin()) { for (String username : data.getAdmin()) {
Optional<UserRepresentation> opt = keycloakService.getUser(username); Optional<UserRepresentation> opt = keycloakService.getUser(username);
if (opt.isEmpty()) if (opt.isEmpty())
throw new DBadRequestException("User " + username + " not found"); throw new DBadRequestException(String.format(trad.t("user.not.found"), username));
admin.add(UUID.fromString(opt.get().getId())); admin.add(UUID.fromString(opt.get().getId()));
} }
for (String username : data.getTable()) { for (String username : data.getTable()) {
Optional<UserRepresentation> opt = keycloakService.getUser(username); Optional<UserRepresentation> opt = keycloakService.getUser(username);
if (opt.isEmpty()) if (opt.isEmpty())
throw new DBadRequestException("User " + username + " not found"); throw new DBadRequestException(String.format(trad.t("user.not.found"), username));
table.add(UUID.fromString(opt.get().getId())); table.add(UUID.fromString(opt.get().getId()));
} }
@@ -569,6 +858,12 @@ public class CompetitionService {
.call(__ -> cache.invalidate(data.getId())); .call(__ -> cache.invalidate(data.getId()));
} }
public Uni<List<PresetData>> getPresetsForCompetition(SecurityCtx securityCtx, Long id) {
return permService.hasViewPerm(securityCtx, id)
.chain(cm -> Mutiny.fetch(cm.getCatPreset()))
.map(p -> p.stream().map(PresetData::fromModel).toList());
}
public Uni<Response> unregisterHelloAsso(NotificationData data) { public Uni<Response> unregisterHelloAsso(NotificationData data) {
if (!data.getState().equals("Refunded")) if (!data.getState().equals("Refunded"))
return Uni.createFrom().item(Response.ok().build()); return Uni.createFrom().item(Response.ok().build());
@@ -602,8 +897,8 @@ public class CompetitionService {
public Uni<Response> registerHelloAsso(NotificationData data) { public Uni<Response> registerHelloAsso(NotificationData data) {
String organizationSlug = data.getOrganizationSlug(); String organizationSlug = data.getOrganizationSlug();
String formSlug = data.getFormSlug(); String formSlug = data.getFormSlug();
RegisterRequestData req = new RegisterRequestData(null, "", "", null, 0, false, null, Categorie.CADET, Genre.NA, RegisterRequestData req = new RegisterRequestData(null, "", "", null, null, 0, false, new ArrayList<>(), null,
null, "fr"); Categorie.CADET, Genre.NA, null, "fr", false);
return repository.find("data1 = ?1 AND data2 = ?2", organizationSlug, formSlug).firstResult() return repository.find("data1 = ?1 AND data2 = ?2", organizationSlug, formSlug).firstResult()
.onFailure().recoverWithNull() .onFailure().recoverWithNull()
@@ -637,7 +932,7 @@ public class CompetitionService {
.call(m -> Panache.withTransaction(() -> .call(m -> Panache.withTransaction(() ->
helloAssoRepository.persist( helloAssoRepository.persist(
new HelloAssoRegisterModel(cm, m, data.getId())))) new HelloAssoRegisterModel(cm, m, data.getId()))))
.chain(m -> updateRegister(req, cm, m, true))) .chain(m -> updateRegister(req, cm, m, true, true)))
.onFailure().recoverWithItem(throwable -> { .onFailure().recoverWithItem(throwable -> {
fail.add("%s %s - licence n°%d".formatted(item.getUser().getLastName(), fail.add("%s %s - licence n°%d".formatted(item.getUser().getLastName(),
item.getUser().getFirstName(), optional.get())); item.getUser().getFirstName(), optional.get()));

View File

@@ -4,7 +4,6 @@ import fr.titionfire.ffsaf.rest.client.HelloAssoAuthClient;
import fr.titionfire.ffsaf.rest.client.dto.TokenResponse; import fr.titionfire.ffsaf.rest.client.dto.TokenResponse;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.rest.client.inject.RestClient; import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@@ -13,7 +12,6 @@ import org.jboss.logging.Logger;
public class HelloAssoTokenService { public class HelloAssoTokenService {
private static final Logger LOG = Logger.getLogger(HelloAssoTokenService.class); private static final Logger LOG = Logger.getLogger(HelloAssoTokenService.class);
@Inject
@RestClient @RestClient
HelloAssoAuthClient authClient; HelloAssoAuthClient authClient;
@@ -27,6 +25,11 @@ public class HelloAssoTokenService {
// Récupère un token valide (en le rafraîchissant si nécessaire) // Récupère un token valide (en le rafraîchissant si nécessaire)
public Uni<String> getValidAccessToken() { public Uni<String> getValidAccessToken() {
if (clientId == null || clientSecret == null || clientId.isEmpty() || clientSecret.isEmpty() || clientId.equalsIgnoreCase(
"null") || clientSecret.equalsIgnoreCase("null")) {
LOG.error("Client ID ou Client Secret non configuré pour HelloAsso");
return Uni.createFrom().failure(new IllegalStateException("HelloAsso client credentials not configured"));
}
if (currentToken == null || currentToken.isExpired()) { if (currentToken == null || currentToken.isExpired()) {
return fetchNewToken(clientId, clientSecret); return fetchNewToken(clientId, clientSecret);
} }

View File

@@ -156,24 +156,32 @@ 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.withText(oldEmail, Mail.withHtml(oldEmail, "FFSAF - Changement de votre adresse email",
"FFSAF - Changement de votre adresse email", String.format(Utils.HTML_HEADER, "FFSAF - Changement de votre adresse email") +
String.format( String.format("""
""" <p>Suite à la modification de votre adresse email fournie lors de votre (ré)inscription à la FFSAF,<br/>
Bonjour, vous allez recevoir dans les prochaines minutes un email de vérification de votre nouvelle adresse sur celle-ci.</p>
<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>
""",
oldEmail, email) +
Utils.HTML_FOOTER
)
.setText(String.format("""
Bonjour,
Suite à la modification de votre adresse email fournie lors de votre (ré)inscription à la FFSAF, Suite à la modification de votre adresse email fournie lors de votre (ré)inscription à la FFSAF,
vous allez recevoir dans les prochaines minutes un email de vérification de votre nouvelle adresse sur celle-ci. vous allez recevoir dans les prochaines minutes un email de vérification de votre nouvelle adresse sur celle-ci.
Ancienne adresse email : %s Ancienne adresse email : %s
Nouvelle adresse email : %s Nouvelle adresse email : %s
Si vous n'avez pas demandé cette modification, veuillez contacter le support à l'adresse support@ffsaf.fr. Si vous n'avez pas demandé cette modification, veuillez contacter le support à l'adresse support@ffsaf.fr.
Cordialement, Cordialement,
L'équipe de la FFSAF L'équipe de la FFSAF
""", oldEmail, email) """, oldEmail, email))
).setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("support@ffsaf.fr") .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)));
} }
@@ -270,13 +278,33 @@ 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.withText(user.getEmail(), Mail.withHtml(user.getEmail(), "FFSAF - Creation de votre compte sur l'intranet",
"FFSAF - Creation de votre compte sur l'intranet", String.format(Utils.HTML_HEADER, "Création de votre compte intranet FFSAF") +
String.format( String.format(
"""
<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>
""",
membreModel.getRole() == RoleAsso.MEMBRE ? "par votre club (" + membreModel.getClub()
.getName() + ") " : "", user.getUsername()) +
Utils.HTML_FOOTER
)
.setText(String.format(
""" """
Bonjour, Bonjour,
Suite à votre première inscription %sà la Fédération Française de Soft Armored Fighting (FFSAF), votre compte intranet a été créé. Suite à votre première inscription %sà la Fédération France Soft Armored Fighting (FFSAF), votre compte intranet a été créé.
Ce compte vous permettra de consulter vos informations et, dans un futur proche, de vous inscrire aux compétitions ainsi que d'en consulter les résultats. Ce compte vous permettra de consulter vos informations et, dans un futur proche, de vous inscrire aux compétitions ainsi que d'en consulter les résultats.
L'intranet est accessible à l'adresse suivante : https://intra.ffsaf.fr L'intranet est accessible à l'adresse suivante : https://intra.ffsaf.fr
@@ -291,8 +319,8 @@ public class KeycloakService {
L'équipe de la FFSAF L'équipe de la FFSAF
""", """,
membreModel.getRole() == RoleAsso.MEMBRE ? "par votre club (" + membreModel.getClub() membreModel.getRole() == RoleAsso.MEMBRE ? "par votre club (" + membreModel.getClub()
.getName() + ") " : "", user.getUsername()) .getName() + ") " : "", user.getUsername()))
).setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("support@ffsaf.fr") .setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("support@ffsaf.fr")
) : Uni.createFrom().nullItem()) ) : Uni.createFrom().nullItem())
.call(user -> membreService.setUserId(membreModel.getId(), user.getId())) .call(user -> membreService.setUserId(membreModel.getId(), user.getId()))
.call(user -> setClubGroupMembre(membreModel, membreModel.getClub())); .call(user -> setClubGroupMembre(membreModel, membreModel.getClub()));
@@ -349,11 +377,19 @@ public class KeycloakService {
public Optional<UserRepresentation> getUser(UUID userId) { public Optional<UserRepresentation> getUser(UUID userId) {
UserResource user = keycloak.realm(realm).users().get(userId.toString()); return getUserById(userId.toString());
if (user == null) }
public Optional<UserRepresentation> getUserById(String userId) {
try {
UserResource user = keycloak.realm(realm).users().get(userId);
if (user == null)
return Optional.empty();
else
return Optional.of(user.toRepresentation());
} catch (Exception e) {
return Optional.empty(); return Optional.empty();
else }
return Optional.of(user.toRepresentation());
} }
private String makeLogin(MembreModel model) { private String makeLogin(MembreModel model) {

View File

@@ -47,6 +47,9 @@ public class LicenceService {
@Inject @Inject
CheckoutService checkoutService; CheckoutService checkoutService;
@Inject
TradService trad;
public Uni<List<LicenceModel>> getLicence(long id, Consumer<MembreModel> checkPerm) { public Uni<List<LicenceModel>> getLicence(long id, Consumer<MembreModel> checkPerm) {
return combRepository.findById(id).invoke(checkPerm) return combRepository.findById(id).invoke(checkPerm)
.chain(combRepository -> Mutiny.fetch(combRepository.getLicences())); .chain(combRepository -> Mutiny.fetch(combRepository.getLicences()));
@@ -84,6 +87,26 @@ public class LicenceService {
)); ));
} }
public Uni<?> validePaymentLicences(List<Long> ids) {
Uni<String> uni = Uni.createFrom().nullItem();
for (Long id : ids) {
uni = uni.chain(__ -> repository.find("membre.id = ?1 AND saison = ?2", id, Utils.getSaison()).firstResult()
.chain(model -> {
if (!model.isPay())
ls.logUpdate("payment (admin) de la licence", model);
return validatePayLicences(model);
}))
.map(__ -> "OK");
}
return uni.call(__ -> ls.append());
}
protected Uni<LicenceModel> validatePayLicences(LicenceModel model) {
model.setPay(true);
return Panache.withTransaction(() -> repository.persist(model));
}
public Uni<LicenceModel> setLicence(long id, LicenceForm form) { public Uni<LicenceModel> setLicence(long id, LicenceForm form) {
if (form.getId() == -1) { if (form.getId() == -1) {
return combRepository.findById(id).chain(membreModel -> { return combRepository.findById(id).chain(membreModel -> {
@@ -99,7 +122,9 @@ public class LicenceService {
.call(genLicenceNumberAndAccountIfNeed()) .call(genLicenceNumberAndAccountIfNeed())
: Uni.createFrom().nullItem() : Uni.createFrom().nullItem()
)) ))
.call(licenceModel -> ls.logA(LogModel.ActionType.ADD, membreModel.getObjectName(), .call(licenceModel -> ls.logA(LogModel.ActionType.ADD,
"%s (valid=%b, pay=%b, %s)" .formatted(membreModel.getObjectName(), model.isValidate(),
model.isPay(), model.getCertificate()),
licenceModel)); licenceModel));
}); });
} else { } else {
@@ -121,7 +146,7 @@ public class LicenceService {
} }
private Function<MembreModel, Uni<?>> genLicenceNumberAndAccountIfNeed() { private Function<MembreModel, Uni<?>> genLicenceNumberAndAccountIfNeed() {
return membreModel -> ((membreModel.getLicence() <= 0) ? return membreModel -> ((membreModel.getLicence() == null || membreModel.getLicence() <= 0) ?
sequenceRepository.getNextValueInTransaction(SequenceType.Licence) sequenceRepository.getNextValueInTransaction(SequenceType.Licence)
.invoke(i -> membreModel.setLicence(Math.toIntExact(i))) .invoke(i -> membreModel.setLicence(Math.toIntExact(i)))
.chain(() -> combRepository.persist(membreModel)) .chain(() -> combRepository.persist(membreModel))
@@ -137,7 +162,7 @@ public class LicenceService {
return repository.list("membre.id IN ?1 AND saison = ?2 AND pay = FALSE", ids, Utils.getSaison()) return repository.list("membre.id IN ?1 AND saison = ?2 AND pay = FALSE", ids, Utils.getSaison())
.invoke(Unchecked.consumer(models -> { .invoke(Unchecked.consumer(models -> {
if (models.size() != ids.size()) if (models.size() != ids.size())
throw new DBadRequestException("Erreur lors de lalection des membres"); throw new DBadRequestException(trad.t("erreur.lors.de.la.selection.des.membres"));
})) }))
.call(models -> { .call(models -> {
Uni<?> uni = Uni.createFrom().nullItem(); Uni<?> uni = Uni.createFrom().nullItem();
@@ -154,10 +179,10 @@ public class LicenceService {
.call(__ -> checkoutService.canDeleteLicence(id) .call(__ -> checkoutService.canDeleteLicence(id)
.invoke(Unchecked.consumer(b -> { .invoke(Unchecked.consumer(b -> {
if (!b) throw new DBadRequestException( if (!b) throw new DBadRequestException(
"Impossible de supprimer une licence pour laquelle un paiement est en cours"); trad.t("licence.rm.err1"));
}))) })))
.call(model -> ls.logADelete(model)) .call(model -> ls.logADelete(model))
.chain(model -> repository.delete(model)); .chain(model -> Panache.withTransaction(() -> repository.delete(model)));
} }
public Uni<LicenceModel> askLicence(long id, LicenceForm form, Consumer<MembreModel> checkPerm) { public Uni<LicenceModel> askLicence(long id, LicenceForm form, Consumer<MembreModel> checkPerm) {
@@ -166,7 +191,7 @@ public class LicenceService {
return repository.find("saison = ?1 AND membre = ?2", Utils.getSaison(), membreModel).count() return repository.find("saison = ?1 AND membre = ?2", Utils.getSaison(), membreModel).count()
.invoke(Unchecked.consumer(count -> { .invoke(Unchecked.consumer(count -> {
if (count > 0) if (count > 0)
throw new DBadRequestException("Licence déjà demandée"); throw new DBadRequestException(trad.t("licence.deja.demandee"));
})).chain(__ -> combRepository.findById(id).chain(membreModel2 -> { })).chain(__ -> combRepository.findById(id).chain(membreModel2 -> {
LicenceModel model = new LicenceModel(); LicenceModel model = new LicenceModel();
model.setClub_id((membreModel2.getClub() == null) ? null : membreModel2.getClub().getId()); model.setClub_id((membreModel2.getClub() == null) ? null : membreModel2.getClub().getId());
@@ -195,13 +220,13 @@ public class LicenceService {
.call(__ -> checkoutService.canDeleteLicence(id) .call(__ -> checkoutService.canDeleteLicence(id)
.invoke(Unchecked.consumer(b -> { .invoke(Unchecked.consumer(b -> {
if (!b) throw new DBadRequestException( if (!b) throw new DBadRequestException(
"Impossible de supprimer une licence pour laquelle un paiement est en cours"); trad.t("licence.rm.err1"));
}))) })))
.invoke(Unchecked.consumer(licenceModel -> { .invoke(Unchecked.consumer(licenceModel -> {
if (licenceModel.isValidate()) if (licenceModel.isValidate())
throw new DBadRequestException("Impossible de supprimer une licence déjà validée"); throw new DBadRequestException(trad.t("impossible.de.supprimer.une.licence.deja.validee"));
if (licenceModel.isPay()) if (licenceModel.isPay())
throw new DBadRequestException("Impossible de supprimer une licence déjà payée"); throw new DBadRequestException(trad.t("impossible.de.supprimer.une.licence.deja.payee"));
})) }))
.call(model -> ls.logADelete(model)) .call(model -> ls.logADelete(model))
.chain(__ -> Panache.withTransaction(() -> repository.deleteById(id))); .chain(__ -> Panache.withTransaction(() -> repository.deleteById(id)));

View File

@@ -58,6 +58,8 @@ public class LoggerService {
} }
public Uni<?> append() { public Uni<?> append() {
if (buffer.isEmpty())
return Uni.createFrom().voidItem();
return Panache.withTransaction(() -> repository.persist(buffer)) return Panache.withTransaction(() -> repository.persist(buffer))
.invoke(__ -> buffer.clear()); .invoke(__ -> buffer.clear());
} }

View File

@@ -7,14 +7,12 @@ import fr.titionfire.ffsaf.data.repository.*;
import fr.titionfire.ffsaf.net2.ServerCustom; import fr.titionfire.ffsaf.net2.ServerCustom;
import fr.titionfire.ffsaf.net2.data.SimpleCombModel; import fr.titionfire.ffsaf.net2.data.SimpleCombModel;
import fr.titionfire.ffsaf.net2.request.SReqComb; import fr.titionfire.ffsaf.net2.request.SReqComb;
import fr.titionfire.ffsaf.rest.data.MeData; import fr.titionfire.ffsaf.rest.data.*;
import fr.titionfire.ffsaf.rest.data.SimpleLicence;
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
import fr.titionfire.ffsaf.rest.data.SimpleMembreInOutData;
import fr.titionfire.ffsaf.rest.exception.DBadRequestException; import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.exception.DInternalError; import fr.titionfire.ffsaf.rest.exception.DInternalError;
import fr.titionfire.ffsaf.rest.from.FullMemberForm; import fr.titionfire.ffsaf.rest.from.FullMemberForm;
import fr.titionfire.ffsaf.rest.from.UserSettingForm;
import fr.titionfire.ffsaf.utils.*; import fr.titionfire.ffsaf.utils.*;
import io.quarkus.hibernate.reactive.panache.Panache; import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.PanacheQuery; import io.quarkus.hibernate.reactive.panache.PanacheQuery;
@@ -64,6 +62,9 @@ public class MembreService {
@Inject @Inject
LoggerService ls; LoggerService ls;
@Inject
TradService trad;
public SimpleCombModel find(int licence, String np) throws Throwable { public SimpleCombModel find(int licence, String np) throws Throwable {
return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() -> return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() ->
repository.find( repository.find(
@@ -125,86 +126,75 @@ public class MembreService {
return sort; return sort;
} }
public Uni<PageResult<SimpleMembre>> searchAdmin(int limit, int page, String search, String club, public Uni<PageResult<SimpleMembre>> search(int limit, int page, String search, int licenceRequest, int payState,
int licenceRequest, int payState, String order, String categorie) { String order, String categorie, Boolean archive, String subject) {
if (search == null) return repository.find("userId = ?1", subject).firstResult()
search = ""; .invoke(Unchecked.consumer(membreModel -> {
search = "%" + search.replaceAll(" ", "% %") + "%"; if (membreModel == null || membreModel.getClub() == null)
throw new DForbiddenException();
String categorieFilter; }))
if (categorie == null || categorie.isBlank()) .chain(membreModel ->
categorieFilter = " True"; searchAdmin(limit, page, search, membreModel.getClub(), licenceRequest, payState, order,
else categorie, archive));
categorieFilter = "categorie = " + Categorie.valueOf(categorie).ordinal();
String finalSearch = search;
Uni<List<LicenceModel>> baseUni = getLicenceListe(licenceRequest, payState);
Sort sort = getSort(order);
if (sort == null)
return Uni.createFrom().failure(new DInternalError("Erreur lors calcul du trie"));
return baseUni
.map(l -> l.stream().map(l2 -> l2.getMembre().getId()).toList())
.chain(ids -> {
PanacheQuery<MembreModel> query;
String idf = ((licenceRequest == 0 || licenceRequest == 4) ? "NOT IN" : "IN");
if (club == null || club.isBlank()) {
query = repository.find(
"id " + idf + " ?2 AND (" + FIND_NAME_REQUEST + ") AND " + categorieFilter,
sort, finalSearch, ids)
.page(Page.ofSize(limit));
} else {
if (club.equals("null")) {
query = repository.find(
"id " + idf + " ?2 AND club IS NULL AND (" + FIND_NAME_REQUEST + ") AND " + categorieFilter,
sort, finalSearch, ids).page(Page.ofSize(limit));
} else {
query = repository.find(
"id " + idf + " ?3 AND LOWER(club.name) LIKE LOWER(?2) AND (" + FIND_NAME_REQUEST + ") AND " + categorieFilter,
sort, finalSearch, club, ids)
.page(Page.ofSize(limit));
}
}
return getPageResult(query, limit, page);
});
} }
public Uni<PageResult<SimpleMembre>> search(int limit, int page, String search, int licenceRequest, int payState, public Uni<PageResult<SimpleMembre>> searchAdmin(int limit, int page, String search, Object club,
String order, String categorie, String subject) { int licenceRequest, int payState, String order, String categorie,
Boolean archive) {
if (search == null) if (search == null)
search = ""; search = "";
search = "%" + search.replaceAll(" ", "% %") + "%"; search = "%" + search.replaceAll(" ", "% %") + "%";
String finalSearch = search;
Uni<List<LicenceModel>> baseUni = getLicenceListe(licenceRequest, payState);
String categorieFilter; String categorieFilter;
if (categorie == null || categorie.isBlank()) if (categorie == null || categorie.isBlank())
categorieFilter = " True"; categorieFilter = " True";
else else
categorieFilter = "categorie = " + Categorie.valueOf(categorie).ordinal(); categorieFilter = "categorie = " + Categorie.valueOf(categorie).ordinal();
String archiveFilter;
if (archive == null || archive)
archiveFilter = "True";
else
archiveFilter = String.format(
"EXISTS (SELECT 1 FROM LicenceModel l WHERE l.membre.id = m.id AND l.saison >= %s)",
Utils.getSaison() - 1);
String clubFilter = "(TRUE OR ?3 = ?3)";
if (club != null) {
if (club instanceof String club_) {
if (!club_.isBlank()) {
if (club_.equals("null"))
clubFilter = "club IS NULL AND ?3 = ?3";
else
clubFilter = "LOWER(club.name) LIKE LOWER(?3)";
}
} else if (club instanceof ClubModel)
clubFilter = "club = ?3";
}
String queryStr = String.format(
"SELECT m FROM MembreModel m WHERE id %s ?2 AND %s AND (%s) AND %s AND %s",
(licenceRequest == 0 || licenceRequest == 4) ? "NOT IN" : "IN", clubFilter, FIND_NAME_REQUEST,
categorieFilter,
archiveFilter);
Sort sort = getSort(order); Sort sort = getSort(order);
if (sort == null) if (sort == null)
return Uni.createFrom().failure(new DInternalError("Erreur lors calcul du trie")); return Uni.createFrom().failure(new DInternalError(trad.t("erreur.lors.calcul.du.trie")));
return baseUni String finalSearch = search;
.map(l -> l.stream().map(l2 -> l2.getMembre().getId()).toList()) return getLicenceListe(licenceRequest, payState)
.chain(ids -> { .chain(l -> {
String idf = ((licenceRequest == 0 || licenceRequest == 4) ? "NOT IN" : "IN"); PanacheQuery<MembreModel> query;
query = repository.find(queryStr, sort, finalSearch,
return repository.find("userId = ?1", subject).firstResult() l.stream().map(l2 -> l2.getMembre().getId()).toList(), club).page(Page.ofSize(limit));
.chain(membreModel -> { return getPageResult(query, limit, page)
PanacheQuery<MembreModel> query = repository.find( .call(r -> (r.getResult().isEmpty() || !l.isEmpty() ?
"id " + idf + " ?3 AND club = ?2 AND (" + FIND_NAME_REQUEST + ") AND " + categorieFilter, Uni.createFrom().item(l) :
sort, finalSearch, membreModel.getClub(), ids) licenceRepository.list("saison = ?1 AND membre.id IN ?2", Utils.getSaison(),
.page(Page.ofSize(limit)); r.getResult().stream().map(SimpleMembre::getId).toList()))
return getPageResult(query, limit, page); .invoke(l2 -> r.setAdditionalData(l2.stream().map(SimpleLicence::fromModel)))
}); );
}); });
} }
@@ -215,7 +205,7 @@ public class MembreService {
.call(result -> query.count().invoke(result::setResult_count)) .call(result -> query.count().invoke(result::setResult_count))
.call(result -> query.pageCount() .call(result -> query.pageCount()
.invoke(Unchecked.consumer(pages -> { .invoke(Unchecked.consumer(pages -> {
if (page > pages) throw new DBadRequestException("Page out of range"); if (page > pages) throw new DBadRequestException(trad.t("page.out.of.range"));
})) }))
.invoke(result::setPage_count)) .invoke(result::setPage_count))
.call(result -> query.page(Page.of(page, limit)).list() .call(result -> query.page(Page.of(page, limit)).list()
@@ -226,7 +216,7 @@ public class MembreService {
public Uni<List<SimpleMembreInOutData>> getAllExport(String subject) { public Uni<List<SimpleMembreInOutData>> getAllExport(String subject) {
return repository.find("userId = ?1", subject).firstResult() return repository.find("userId = ?1", subject).firstResult()
.chain(membreModel -> repository.list("club = ?1", membreModel.getClub())) .chain(membreModel -> repository.list("club = ?1", membreModel.getClub()))
.chain(membres -> licenceRepository.list("saison = ?1 AND membre IN ?2", Utils.getSaison(), membres) .chain(membres -> licenceRepository.list("membre IN ?1", membres)
.map(l -> membres.stream().map(m -> SimpleMembreInOutData.fromModel(m, l)).toList())); .map(l -> membres.stream().map(m -> SimpleMembreInOutData.fromModel(m, l)).toList()));
} }
@@ -260,8 +250,8 @@ public class MembreService {
for (MembreModel membreModel : membres) { for (MembreModel membreModel : membres) {
if (!Objects.equals(membreModel.getClub(), clubModel.get())) { if (!Objects.equals(membreModel.getClub(), clubModel.get())) {
LOGGER.info("Similar membres found: " + membreModel); LOGGER.info("Similar membres found: " + membreModel);
throw new DForbiddenException( throw new DForbiddenException(String.format(trad.t("le.membre.appartient.pas.a.votre.club"),
"Le membre n°" + membreModel.getLicence() + " n'appartient pas à votre club"); membreModel.getLicence()));
} }
} }
Uni<Void> uniResult = Uni.createFrom().voidItem(); Uni<Void> uniResult = Uni.createFrom().voidItem();
@@ -287,14 +277,18 @@ public class MembreService {
if (model.getEmail() != null && !model.getEmail().isBlank()) { if (model.getEmail() != null && !model.getEmail().isBlank()) {
if (model.getLicence() != null && !model.getLicence().equals(dataIn.getLicence())) { if (model.getLicence() != null && !model.getLicence().equals(dataIn.getLicence())) {
LOGGER.info("Similar membres found: " + model); LOGGER.info("Similar membres found: " + model);
throw new DBadRequestException("Email '" + model.getEmail() + "' déja utiliser"); throw new DBadRequestException(
String.format(trad.t("email.deja.utilise.par"), model.getEmail(),
model.getFname(), model.getLname()));
} }
if (StringSimilarity.similarity(model.getLname().toUpperCase(), if (StringSimilarity.similarity(model.getLname().toUpperCase(),
dataIn.getNom().toUpperCase()) > 3 || StringSimilarity.similarity( dataIn.getNom().toUpperCase()) > 3 || StringSimilarity.similarity(
model.getFname().toUpperCase(), dataIn.getPrenom().toUpperCase()) > 3) { model.getFname().toUpperCase(), dataIn.getPrenom().toUpperCase()) > 3) {
LOGGER.info("Similar membres found: " + model); LOGGER.info("Similar membres found: " + model);
throw new DBadRequestException("Email '" + model.getEmail() + "' déja utiliser"); throw new DBadRequestException(
String.format(trad.t("email.deja.utilise.par"), model.getEmail(),
model.getFname(), model.getLname()));
} }
} }
@@ -305,7 +299,8 @@ public class MembreService {
model.getFname().toUpperCase(), dataIn.getPrenom().toUpperCase()) > 3)) { model.getFname().toUpperCase(), dataIn.getPrenom().toUpperCase()) > 3)) {
LOGGER.info("Similar membres found: " + model); LOGGER.info("Similar membres found: " + model);
throw new DBadRequestException( throw new DBadRequestException(
"Pour enregistrer un nouveau membre, veuillez laisser le champ licence vide."); String.format(trad.t("try.edit.licence"), model.getLicence(), model.getFname(),
model.getFname()));
} }
ls.logChange("Nom", model.getLname(), dataIn.getNom().toUpperCase(), model); ls.logChange("Nom", model.getLname(), dataIn.getNom().toUpperCase(), model);
@@ -313,8 +308,7 @@ public class MembreService {
dataIn.getPrenom().toUpperCase().charAt(0) + dataIn.getPrenom().substring(1), model); dataIn.getPrenom().toUpperCase().charAt(0) + dataIn.getPrenom().substring(1), model);
model.setLname(dataIn.getNom().toUpperCase()); model.setLname(dataIn.getNom().toUpperCase());
model.setFname(dataIn.getPrenom().toUpperCase().charAt(0) + dataIn.getPrenom().substring(1)); model.setFname(Utils.formatPrenom(dataIn.getPrenom()));
if (dataIn.getEmail() != null && !dataIn.getEmail().isBlank()) { if (dataIn.getEmail() != null && !dataIn.getEmail().isBlank()) {
ls.logChange("Email", model.getEmail(), dataIn.getEmail(), model); ls.logChange("Email", model.getEmail(), dataIn.getEmail(), model);
@@ -380,7 +374,7 @@ public class MembreService {
.call(__ -> repository.count("email LIKE ?1 AND id != ?2", membre.getEmail(), id) .call(__ -> repository.count("email LIKE ?1 AND id != ?2", membre.getEmail(), id)
.invoke(Unchecked.consumer(c -> { .invoke(Unchecked.consumer(c -> {
if (c > 0 && !membre.getEmail().isBlank()) if (c > 0 && !membre.getEmail().isBlank())
throw new DBadRequestException("Email déjà utiliser"); throw new DBadRequestException(trad.t("email.deja.utilise"));
}))) })))
.chain(membreModel -> clubRepository.findById(membre.getClub()) .chain(membreModel -> clubRepository.findById(membre.getClub())
.map(club -> new Pair<>(membreModel, club))) .map(club -> new Pair<>(membreModel, club)))
@@ -402,7 +396,7 @@ public class MembreService {
.call(__ -> repository.count("email LIKE ?1 AND id != ?2", membre.getEmail(), id) .call(__ -> repository.count("email LIKE ?1 AND id != ?2", membre.getEmail(), id)
.invoke(Unchecked.consumer(c -> { .invoke(Unchecked.consumer(c -> {
if (c > 0 && !membre.getEmail().isBlank()) if (c > 0 && !membre.getEmail().isBlank())
throw new DBadRequestException("Email déjà utiliser"); throw new DBadRequestException(trad.t("email.deja.utilise"));
}))) })))
.invoke(Unchecked.consumer(membreModel -> { .invoke(Unchecked.consumer(membreModel -> {
if (!securityCtx.isInClubGroup(membreModel.getClub().getId())) if (!securityCtx.isInClubGroup(membreModel.getClub().getId()))
@@ -411,7 +405,7 @@ public class MembreService {
membre.getLname().toUpperCase()) > 3 || StringSimilarity.similarity( membre.getLname().toUpperCase()) > 3 || StringSimilarity.similarity(
membreModel.getFname().toUpperCase(), membre.getFname().toUpperCase()) > 3) { membreModel.getFname().toUpperCase(), membre.getFname().toUpperCase()) > 3) {
throw new DBadRequestException( throw new DBadRequestException(
"Pour enregistrer un nouveau membre, veuillez utilisez le bouton prévue a cette effet."); trad.t("regiter.new.membre"));
} }
})) }))
.invoke(Unchecked.consumer(membreModel -> { .invoke(Unchecked.consumer(membreModel -> {
@@ -420,7 +414,7 @@ public class MembreService {
else if (securityCtx.roleHas("club_secretaire")) source = RoleAsso.SECRETAIRE; else if (securityCtx.roleHas("club_secretaire")) source = RoleAsso.SECRETAIRE;
else if (securityCtx.roleHas("club_respo_intra")) source = RoleAsso.MEMBREBUREAU; else if (securityCtx.roleHas("club_respo_intra")) source = RoleAsso.MEMBREBUREAU;
if (!membre.getRole().equals(membreModel.getRole()) && membre.getRole().level >= source.level) if (!membre.getRole().equals(membreModel.getRole()) && membre.getRole().level >= source.level)
throw new DForbiddenException("Permission insuffisante"); throw new DForbiddenException(trad.t("permission.insuffisante"));
})) }))
.onItem().transform(target -> { .onItem().transform(target -> {
if (!securityCtx.getSubject().equals(target.getUserId())) { if (!securityCtx.getSubject().equals(target.getUserId())) {
@@ -434,7 +428,7 @@ public class MembreService {
private Uni<String> update(Uni<MembreModel> uni, FullMemberForm membre, boolean admin) { private Uni<String> update(Uni<MembreModel> uni, FullMemberForm membre, boolean admin) {
return uni.chain(target -> { return uni.chain(target -> {
ls.logChange("Prénom", target.getFname(), membre.getFname(), target); ls.logChange("Prénom", target.getFname(), membre.getFname(), target);
target.setFname(membre.getFname()); target.setFname(Utils.formatPrenom(membre.getFname()));
ls.logChange("Nom", target.getLname(), membre.getLname(), target); ls.logChange("Nom", target.getLname(), membre.getLname(), target);
target.setLname(membre.getLname().toUpperCase()); target.setLname(membre.getLname().toUpperCase());
ls.logChange("Pays", target.getCountry(), membre.getCountry(), target); ls.logChange("Pays", target.getCountry(), membre.getCountry(), target);
@@ -482,7 +476,8 @@ public class MembreService {
.call(membreModel -> licenceRepository.update("club_id = ?1 where membre = ?2 AND saison = ?3", .call(membreModel -> licenceRepository.update("club_id = ?1 where membre = ?2 AND saison = ?3",
(membreModel.getClub() == null) ? null : membreModel.getClub().getId(), membreModel, (membreModel.getClub() == null) ? null : membreModel.getClub().getId(), membreModel,
Utils.getSaison())) Utils.getSaison()))
.call(membreModel -> membre.getPhoto_data().length > 0 ? ls.logAUpdate("Photo", .call(membreModel -> (membre.getPhoto_data() != null && membre.getPhoto_data()
.size() > 0) ? ls.logAUpdate("Photo",
membreModel) : Uni.createFrom().nullItem()) membreModel) : Uni.createFrom().nullItem())
.map(__ -> "OK"); .map(__ -> "OK");
} }
@@ -492,7 +487,8 @@ public class MembreService {
return clubRepository.findById(input.getClub()) return clubRepository.findById(input.getClub())
.call(__ -> repository.count("email LIKE ?1", input.getEmail()) .call(__ -> repository.count("email LIKE ?1", input.getEmail())
.invoke(Unchecked.consumer(c -> { .invoke(Unchecked.consumer(c -> {
if (c > 0) throw new DBadRequestException("Email déjà utiliser"); if (c > 0 && input.getEmail() != null && !input.getEmail().isBlank())
throw new DBadRequestException(trad.t("email.deja.utilise"));
}))) })))
.chain(clubModel -> { .chain(clubModel -> {
MembreModel model = getMembreModel(input, clubModel); MembreModel model = getMembreModel(input, clubModel);
@@ -508,7 +504,7 @@ public class MembreService {
return repository.find("userId = ?1", subject).firstResult() return repository.find("userId = ?1", subject).firstResult()
.call(__ -> repository.count("email LIKE ?1", input.getEmail()) .call(__ -> repository.count("email LIKE ?1", input.getEmail())
.invoke(Unchecked.consumer(c -> { .invoke(Unchecked.consumer(c -> {
if (c > 0) throw new DBadRequestException("Email déjà utiliser"); if (c > 0) throw new DBadRequestException(trad.t("email.deja.utilise"));
}))) })))
.call(membreModel -> .call(membreModel ->
repository.count( repository.count(
@@ -516,7 +512,7 @@ public class MembreService {
input.getLname(), input.getFname(), membreModel.getClub()) input.getLname(), input.getFname(), membreModel.getClub())
.invoke(Unchecked.consumer(c -> { .invoke(Unchecked.consumer(c -> {
if (c > 0) if (c > 0)
throw new DBadRequestException("Membre déjà existent"); throw new DBadRequestException(trad.t("membre.deja.existent"));
}))) })))
.chain(membreModel -> { .chain(membreModel -> {
MembreModel model = getMembreModel(input, membreModel.getClub()); MembreModel model = getMembreModel(input, membreModel.getClub());
@@ -549,13 +545,13 @@ public class MembreService {
.invoke(Unchecked.consumer(membreModel -> { .invoke(Unchecked.consumer(membreModel -> {
if (membreModel.getLicence() != null) { if (membreModel.getLicence() != null) {
throw new DBadRequestException( throw new DBadRequestException(
"Impossible de supprimer un membre qui a déjà un numéro de licence"); trad.t("membre.rm.err1"));
} }
})) }))
.call(membreModel -> licenceRepository.find("membre = ?1", membreModel).count() .call(membreModel -> licenceRepository.find("membre = ?1", membreModel).count()
.invoke(Unchecked.consumer(l -> { .invoke(Unchecked.consumer(l -> {
if (l > 0) if (l > 0)
throw new DBadRequestException("Impossible de supprimer un membre avec des licences"); throw new DBadRequestException(trad.t("membre.rm.err2"));
}))) })))
.call(membreModel -> (membreModel.getUserId() != null) ? .call(membreModel -> (membreModel.getUserId() != null) ?
keycloakService.removeAccount(membreModel.getUserId()) : Uni.createFrom().nullItem()) keycloakService.removeAccount(membreModel.getUserId()) : Uni.createFrom().nullItem())
@@ -577,8 +573,8 @@ public class MembreService {
private static MembreModel getMembreModel(FullMemberForm input, ClubModel clubModel) { private static MembreModel getMembreModel(FullMemberForm input, ClubModel clubModel) {
MembreModel model = new MembreModel(); MembreModel model = new MembreModel();
model.setFname(input.getFname()); model.setFname(Utils.formatPrenom(input.getFname()));
model.setLname(input.getLname()); model.setLname(input.getLname().toUpperCase());
model.setEmail(input.getEmail()); model.setEmail(input.getEmail());
model.setLicence(null); model.setLicence(null);
model.setGenre(input.getGenre()); model.setGenre(input.getGenre());
@@ -601,13 +597,30 @@ public class MembreService {
public Uni<MeData> getMembre(String subject) { public Uni<MeData> getMembre(String subject) {
MeData meData = new MeData(); MeData meData = new MeData();
return repository.find("userId = ?1", subject).firstResult() return repository.find("userId = ?1", subject).firstResult()
.invoke(meData::setMembre) .invoke(m -> meData.setMembre(m, trad))
.chain(membreModel -> Mutiny.fetch(membreModel.getLicences())) .call(membreModel -> Mutiny.fetch(membreModel.getLicences())
.map(licences -> licences.stream().map(SimpleLicence::fromModel).toList()) .map(licences -> licences.stream().map(SimpleLicence::fromModel).toList())
.invoke(meData::setLicences) .invoke(meData::setLicences))
.call(membreModel -> Mutiny.fetch(membreModel.getSelections())
.map(licences -> licences.stream().map(SimpleSelection::fromModel).toList())
.invoke(meData::setSelections))
.map(__ -> meData); .map(__ -> meData);
} }
public Uni<Void> updateSettings(String subject, UserSettingForm settingForm) {
return repository.find("userId = ?1", subject).firstResult()
.invoke(Unchecked.consumer(membreModel -> {
if (settingForm.getResultPrivacy() != null) {
ls.logChange("Confidentialité des résultats", membreModel.getResultPrivacy(),
settingForm.getResultPrivacy(), membreModel);
membreModel.setResultPrivacy(settingForm.getResultPrivacy());
}
}))
.call(membreModel -> Panache.withTransaction(() -> repository.persist(membreModel)))
.call(membreModel -> ls.append())
.replaceWithVoid();
}
@Scheduled(cron = "0 0 1 1 9 ?") @Scheduled(cron = "0 0 1 1 9 ?")
Uni<Void> everySeason() { Uni<Void> everySeason() {
return repository.list("birth_date IS NOT NULL") return repository.list("birth_date IS NOT NULL")
@@ -617,5 +630,4 @@ public class MembreService {
}).toList()).andCollectFailures()) }).toList()).andCollectFailures())
.map(__ -> null); .map(__ -> null);
} }
} }

View File

@@ -46,6 +46,9 @@ public class PDFService {
@ConfigProperty(name = "pdf-maker.sign-file") @ConfigProperty(name = "pdf-maker.sign-file")
String sign_file; String sign_file;
@Inject
TradService trad;
public Uni<Response> getLicencePdf(String subject) { public Uni<Response> getLicencePdf(String subject) {
return getLicencePdf(combRepository.find("userId = ?1", subject).firstResult() return getLicencePdf(combRepository.find("userId = ?1", subject).firstResult()
@@ -58,7 +61,7 @@ public class PDFService {
LicenceModel licence = m.getLicences().stream() LicenceModel licence = m.getLicences().stream()
.filter(licenceModel -> licenceModel.getSaison() == Utils.getSaison() && licenceModel.isValidate()) .filter(licenceModel -> licenceModel.getSaison() == Utils.getSaison() && licenceModel.isValidate())
.findFirst() .findFirst()
.orElseThrow(() -> new DNotFoundException("Pas de licence pour la saison en cours")); .orElseThrow(() -> new DNotFoundException(trad.t("pas.de.licence.pour.la.saison.en.cours")));
try { try {
byte[] buff = make_pdf(m, licence); byte[] buff = make_pdf(m, licence);
@@ -119,7 +122,7 @@ public class PDFService {
combRepository.find("userId = ?1", subject).firstResult() combRepository.find("userId = ?1", subject).firstResult()
.invoke(Unchecked.consumer(m -> { .invoke(Unchecked.consumer(m -> {
if (m == null || m.getClub() == null) if (m == null || m.getClub() == null)
throw new DNotFoundException("Club non trouvé"); throw new DNotFoundException(trad.t("club.non.trouve"));
})) }))
.map(MembreModel::getClub) .map(MembreModel::getClub)
.call(m -> Mutiny.fetch(m.getAffiliations()))); .call(m -> Mutiny.fetch(m.getAffiliations())));
@@ -130,7 +133,7 @@ public class PDFService {
clubRepository.findById(id) clubRepository.findById(id)
.invoke(Unchecked.consumer(m -> { .invoke(Unchecked.consumer(m -> {
if (m == null) if (m == null)
throw new DNotFoundException("Club non trouvé"); throw new DNotFoundException(trad.t("club.non.trouve"));
})) }))
.call(m -> Mutiny.fetch(m.getAffiliations()))); .call(m -> Mutiny.fetch(m.getAffiliations())));
} }
@@ -141,7 +144,7 @@ public class PDFService {
.map(Unchecked.function(m -> { .map(Unchecked.function(m -> {
if (m.getAffiliations().stream() if (m.getAffiliations().stream()
.noneMatch(licenceModel -> licenceModel.getSaison() == Utils.getSaison())) .noneMatch(licenceModel -> licenceModel.getSaison() == Utils.getSaison()))
throw new DNotFoundException("Pas d'affiliation pour la saison en cours"); throw new DNotFoundException(trad.t("pas.d.affiliation.pour.la.saison.en.cours"));
try { try {
byte[] buff = make_pdf(m); byte[] buff = make_pdf(m);

View File

@@ -0,0 +1,64 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.LogModel;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.data.model.SelectionModel;
import fr.titionfire.ffsaf.data.repository.CombRepository;
import fr.titionfire.ffsaf.data.repository.SelectionRepository;
import fr.titionfire.ffsaf.rest.data.SimpleSelection;
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 org.hibernate.reactive.mutiny.Mutiny;
import java.util.List;
import java.util.function.Consumer;
@WithSession
@ApplicationScoped
public class SelectionService {
@Inject
CombRepository combRepository;
@Inject
SelectionRepository repository;
@Inject
LoggerService ls;
public Uni<List<SelectionModel>> getSelection(long id, Consumer<MembreModel> checkPerm) {
return combRepository.findById(id).invoke(checkPerm)
.chain(combRepository -> Mutiny.fetch(combRepository.getSelections()));
}
public Uni<SelectionModel> setSelection(long id, SimpleSelection data) {
if (data.getId() == -1) {
return combRepository.findById(id).chain(membreModel -> {
SelectionModel model = new SelectionModel();
model.setMembre(membreModel);
model.setSaison(data.getSaison());
model.setCategorie(data.getCategorie());
return Panache.withTransaction(() -> repository.persist(model))
.call(licenceModel -> ls.logA(LogModel.ActionType.ADD, membreModel.getObjectName(),
licenceModel));
});
} else {
return repository.findById(data.getId()).chain(model -> {
ls.logChange("Catégorie", model.getCategorie(), data.getCategorie(), model);
model.setCategorie(data.getCategorie());
return Panache.withTransaction(() -> repository.persist(model))
.call(__ -> ls.append());
});
}
}
public Uni<Void> deleteSelection(long id) {
return repository.findById(id)
.call(model -> ls.logADelete(model))
.chain(model -> Panache.withTransaction(() -> repository.delete(model)));
}
}

View File

@@ -36,7 +36,7 @@ public class StatsService {
LicenceStats.YearStats yearStats = stats.getLicences().get(licence.getSaison()); LicenceStats.YearStats yearStats = stats.getLicences().get(licence.getSaison());
System.out.println("stats: " + licence.getMembre().getFname()); // System.out.println("stats: " + licence.getMembre().getFname());
if (licence.isValidate()) { if (licence.isValidate()) {
if (licence.getMembre().getGenre() == Genre.H) if (licence.getMembre().getGenre() == Genre.H)
yearStats.addH(); yearStats.addH();

View File

@@ -0,0 +1,52 @@
package fr.titionfire.ffsaf.domain.service;
import io.quarkus.websockets.next.WebSocketConnection;
import jakarta.enterprise.context.RequestScoped;
import jakarta.enterprise.inject.Instance;
import jakarta.inject.Inject;
import jakarta.ws.rs.container.ContainerRequestContext;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
@RequestScoped
public class TradService {
@Inject
Instance<ContainerRequestContext> requestContextInstance;
public static final Locale fallbackLocale = Locale.FRANCE;
public String t(String key) {
return translate(key);
}
public String t(String key, WebSocketConnection connection) {
List<String> lang = connection.handshakeRequest().headers().get("Accept-Language");
Locale userLocale = lang != null && !lang.isEmpty() ? Locale.forLanguageTag(lang.get(0)) : fallbackLocale;
return translate(key, userLocale);
}
public String translate(String key) {
ContainerRequestContext requestContext = requestContextInstance.get();
Locale userLocale = (Locale) requestContext.getProperty("userLocale");
return translate(key, userLocale);
}
public String translate(String key, Locale userLocale) {
try {
ResourceBundle messages = ResourceBundle.getBundle("lang.messages", userLocale);
return messages.getString(key);
} catch (MissingResourceException e) {
try {
ResourceBundle fallbackMessages = ResourceBundle.getBundle("lang.messages", fallbackLocale);
return fallbackMessages.getString(key);
} catch (MissingResourceException ex) {
return key;
}
}
}
}

View File

@@ -0,0 +1,23 @@
package fr.titionfire.ffsaf.domain.service;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.HashMap;
@ApplicationScoped
public class UpdateService { // For public result page
static HashMap<Long, Long> lastUpdate = new HashMap<>();
public void setNewData(long id) {
lastUpdate.put(id, System.currentTimeMillis());
}
public boolean needUpdate(long id, long last_update) {
if (!lastUpdate.containsKey(id)) {
lastUpdate.put(id, System.currentTimeMillis() - 5000);
return true;
}
return lastUpdate.getOrDefault(id, 0L) > last_update;
}
}

View File

@@ -0,0 +1,28 @@
package fr.titionfire.ffsaf.domain.service;
import io.quarkiverse.antivirus.runtime.Antivirus;
import io.quarkiverse.antivirus.runtime.AntivirusScanResult;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.io.InputStream;
import java.util.List;
@ApplicationScoped
public class VirusScannerService {
@Inject
Antivirus antivirus;
public Uni<List<AntivirusScanResult>> scanFileReactive(String fileName, InputStream inputStream) {
System.out.println("Starting reactive virus scan for file: " + fileName);
// Wrap the blocking antivirus scan in a reactive context
// This moves the blocking operation to a worker thread
return Uni.createFrom().item(() -> {
System.out.println("Scanning file on worker thread: " + fileName);
return antivirus.scan(fileName, inputStream);
}).runSubscriptionOn(io.smallrye.mutiny.infrastructure.Infrastructure.getDefaultWorkerPool());
}
}

View File

@@ -49,7 +49,7 @@ public class AffiliationEndpoints {
@GET @GET
@Path("{id}") @Path("{id}")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Renvoie les affiliations pour un club", description = "Cette méthode renvoie les affiliations pour un club donné. Seuls les administrateurs de la fédération et les présidents, secrétaires et responsables intranet du club peuvent accéder à cette méthode.") @Operation(summary = "Renvoie les affiliations pour un club", description = "Cette méthode renvoie les affiliations pour un club donné. Seuls les administrateurs de la fédération et les présidents, secrétaires et responsables intranet du club peuvent accéder à cette méthode.")
@APIResponses(value = { @APIResponses(value = {

View File

@@ -73,7 +73,7 @@ public class AffiliationRequestEndpoints {
@GET @GET
@Path("/{id}") @Path("/{id}")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Renvoie une demande d'affiliation", description = "Cette méthode renvoie une demande d'affiliation " + @Operation(summary = "Renvoie une demande d'affiliation", description = "Cette méthode renvoie une demande d'affiliation " +
"pour l'identifiant spécifié.") "pour l'identifiant spécifié.")
@@ -92,7 +92,7 @@ public class AffiliationRequestEndpoints {
@DELETE @DELETE
@Path("/{id}") @Path("/{id}")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Supprime une demande d'affiliation", description = "Cette méthode supprime une demande " + @Operation(summary = "Supprime une demande d'affiliation", description = "Cette méthode supprime une demande " +
"d'affiliation pour l'identifiant spécifié.") "d'affiliation pour l'identifiant spécifié.")
@@ -128,7 +128,7 @@ public class AffiliationRequestEndpoints {
@PUT @PUT
@Path("/edit") @Path("/edit")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.TEXT_PLAIN) @Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.MULTIPART_FORM_DATA) @Consumes(MediaType.MULTIPART_FORM_DATA)
@Operation(summary = "Modifie une demande d'affiliation", description = "Cette méthode modifie une demande " + @Operation(summary = "Modifie une demande d'affiliation", description = "Cette méthode modifie une demande " +
@@ -186,6 +186,6 @@ public class AffiliationRequestEndpoints {
public Uni<Response> getStatus( public Uni<Response> getStatus(
@Parameter(description = "L'identifiant de la demande d'affiliation") @PathParam("id") long id) throws URISyntaxException { @Parameter(description = "L'identifiant de la demande d'affiliation") @PathParam("id") long id) throws URISyntaxException {
return Utils.getMediaFile(id, media, "aff_request/status", "affiliation_request_" + id, return Utils.getMediaFile(id, media, "aff_request/status", "affiliation_request_" + id,
Uni.createFrom().nullItem()); Uni.createFrom().nullItem(), false);
} }
} }

View File

@@ -1,10 +1,12 @@
package fr.titionfire.ffsaf.rest; package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.domain.service.TradService;
import fr.titionfire.ffsaf.rest.client.SirenService; import fr.titionfire.ffsaf.rest.client.SirenService;
import fr.titionfire.ffsaf.rest.client.StateIdService; import fr.titionfire.ffsaf.rest.client.StateIdService;
import fr.titionfire.ffsaf.rest.data.AssoData; import fr.titionfire.ffsaf.rest.data.AssoData;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException; import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
import jakarta.inject.Inject;
import jakarta.ws.rs.*; import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.Operation;
@@ -19,6 +21,9 @@ public class AssoEndpoints {
@RestClient @RestClient
SirenService sirenService; SirenService sirenService;
@Inject
TradService trad;
@GET @GET
@Path("state_id/{stateId}") @Path("state_id/{stateId}")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@@ -28,9 +33,9 @@ public class AssoEndpoints {
stateId).chain(stateIdService::getAssoDataFromUnit)).onFailure().transform(throwable -> { stateId).chain(stateIdService::getAssoDataFromUnit)).onFailure().transform(throwable -> {
if (throwable instanceof WebApplicationException exception) { if (throwable instanceof WebApplicationException exception) {
if (exception.getResponse().getStatus() == 404) if (exception.getResponse().getStatus() == 404)
return new DNotFoundException("Service momentanément indisponible"); return new DNotFoundException(trad.t("service.momentanement.indisponible"));
if (exception.getResponse().getStatus() == 400) if (exception.getResponse().getStatus() == 400)
return new DNotFoundException("Asso introuvable"); return new DNotFoundException(trad.t("asso.introuvable"));
} }
return throwable; return throwable;
}); });

View File

@@ -3,10 +3,11 @@ package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.data.model.ClubModel; import fr.titionfire.ffsaf.data.model.ClubModel;
import fr.titionfire.ffsaf.domain.service.ClubService; import fr.titionfire.ffsaf.domain.service.ClubService;
import fr.titionfire.ffsaf.domain.service.PDFService; import fr.titionfire.ffsaf.domain.service.PDFService;
import fr.titionfire.ffsaf.domain.service.TradService;
import fr.titionfire.ffsaf.domain.service.VirusScannerService;
import fr.titionfire.ffsaf.net2.data.SimpleClubModel; import fr.titionfire.ffsaf.net2.data.SimpleClubModel;
import fr.titionfire.ffsaf.rest.data.*; import fr.titionfire.ffsaf.rest.data.*;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.exception.DInternalError;
import fr.titionfire.ffsaf.rest.from.FullClubForm; import fr.titionfire.ffsaf.rest.from.FullClubForm;
import fr.titionfire.ffsaf.rest.from.PartClubForm; import fr.titionfire.ffsaf.rest.from.PartClubForm;
import fr.titionfire.ffsaf.utils.Contact; import fr.titionfire.ffsaf.utils.Contact;
@@ -47,9 +48,15 @@ public class ClubEndpoints {
@Inject @Inject
SecurityCtx securityCtx; SecurityCtx securityCtx;
@Inject
VirusScannerService scannerService;
@ConfigProperty(name = "upload_dir") @ConfigProperty(name = "upload_dir")
String media; String media;
@Inject
TradService trad;
Consumer<ClubModel> checkPerm = Unchecked.consumer(clubModel -> { Consumer<ClubModel> checkPerm = Unchecked.consumer(clubModel -> {
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(clubModel.getId())) if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(clubModel.getId()))
throw new DForbiddenException(); throw new DForbiddenException();
@@ -81,7 +88,7 @@ public class ClubEndpoints {
@Operation(summary = "Renvoie les types de contacts pour les clubs", description = "Renvoie la liste des types de " + @Operation(summary = "Renvoie les types de contacts pour les clubs", description = "Renvoie la liste des types de " +
"contacts possibles pour les clubs") "contacts possibles pour les clubs")
public Uni<HashMap<String, String>> getConcatType() { public Uni<HashMap<String, String>> getConcatType() {
return Uni.createFrom().item(Contact.toSite()); return Uni.createFrom().item(Contact.toSite(trad));
} }
@GET @GET
@@ -109,7 +116,7 @@ public class ClubEndpoints {
@GET @GET
@Path("{id}") @Path("{id}")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Renvoie les détails d'un club en fonction de son identifiant", description = "Renvoie les " + @Operation(summary = "Renvoie les détails d'un club en fonction de son identifiant", description = "Renvoie les " +
"détails d'un club en fonction de son identifiant, y compris les informations sur les membres et les affiliations") "détails d'un club en fonction de son identifiant, y compris les informations sur les membres et les affiliations")
@@ -121,9 +128,8 @@ public class ClubEndpoints {
}) })
public Uni<SimpleClub> getById( public Uni<SimpleClub> getById(
@Parameter(description = "Identifiant de club") @PathParam("id") long id) { @Parameter(description = "Identifiant de club") @PathParam("id") long id) {
return clubService.getById(id).onItem().invoke(checkPerm).map(SimpleClub::fromModel).invoke(m -> { return clubService.getById(id).onItem().invoke(checkPerm).map(SimpleClub::fromModel)
m.setContactMap(Contact.toSite()); .invoke(m -> m.setContactMap(Contact.toSite(trad)));
});
} }
@PUT @PUT
@@ -145,25 +151,9 @@ public class ClubEndpoints {
return clubService.update(id, input) return clubService.update(id, input)
.invoke(Unchecked.consumer(out -> { .invoke(Unchecked.consumer(out -> {
if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out); if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out);
})).chain(() -> { }))
if (input.getLogo().length > 0) .chain(() -> Utils.uploadFile(scannerService, input.getLogo(), id, media, "ppClub"))
return Uni.createFrom().future(Utils.replacePhoto(id, input.getLogo(), media, "ppClub" .chain(() -> Utils.uploadFile(scannerService, input.getStatus(), id, media, "clubStatus"));
)).invoke(Unchecked.consumer(out -> {
if (!out.equals("OK"))
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
})); // TODO log
else
return Uni.createFrom().nullItem();
}).chain(() -> {
if (input.getStatus().length > 0)
return Uni.createFrom().future(Utils.replacePhoto(id, input.getStatus(), media, "clubStatus"
)).invoke(Unchecked.consumer(out -> {
if (!out.equals("OK"))
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
})); // TODO log
else
return Uni.createFrom().nullItem();
});
} }
@PUT @PUT
@@ -181,19 +171,9 @@ public class ClubEndpoints {
return clubService.add(input) return clubService.add(input)
.invoke(Unchecked.consumer(id -> { .invoke(Unchecked.consumer(id -> {
if (id == null) throw new InternalError("Fail to create club data"); if (id == null) throw new InternalError("Fail to create club data");
})).call(id -> { }))
if (input.getLogo().length > 0) .call(id -> Utils.uploadFile(scannerService, input.getLogo(), id, media, "ppClub"))
return Uni.createFrom().future(Utils.replacePhoto(id, input.getLogo(), media, "ppClub" .call(id -> Utils.uploadFile(scannerService, input.getStatus(), id, media, "clubStatus"));
)); // TODO log
else
return Uni.createFrom().nullItem();
}).call(id -> {
if (input.getStatus().length > 0)
return Uni.createFrom().future(Utils.replacePhoto(id, input.getStatus(), media, "clubStatus"
)); // TODO log
else
return Uni.createFrom().nullItem();
});
} }
@DELETE @DELETE
@@ -230,7 +210,7 @@ public class ClubEndpoints {
@GET @GET
@Path("/me") @Path("/me")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Renvoie les informations du club de l'utilisateur connecté", description = "Renvoie les " + @Operation(summary = "Renvoie les informations du club de l'utilisateur connecté", description = "Renvoie les " +
"informations du club de l'utilisateur connecté") "informations du club de l'utilisateur connecté")
@@ -242,12 +222,12 @@ public class ClubEndpoints {
}) })
public Uni<SimpleClub> getOfUser() { public Uni<SimpleClub> getOfUser() {
return clubService.getOfUser(securityCtx).map(SimpleClub::fromModel) return clubService.getOfUser(securityCtx).map(SimpleClub::fromModel)
.invoke(m -> m.setContactMap(Contact.toSite())); .invoke(m -> m.setContactMap(Contact.toSite(trad)));
} }
@PUT @PUT
@Path("/me") @Path("/me")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.MULTIPART_FORM_DATA) @Consumes(MediaType.MULTIPART_FORM_DATA)
@Operation(summary = "Met à jour les informations du club de l'utilisateur connecté", description = "Met à jour les" + @Operation(summary = "Met à jour les informations du club de l'utilisateur connecté", description = "Met à jour les" +
@@ -264,7 +244,7 @@ public class ClubEndpoints {
@GET @GET
@Path("/me/affiliation") @Path("/me/affiliation")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Operation(summary = "Renvoie l'attestation d'affiliation du club de l'utilisateur connecté", description = @Operation(summary = "Renvoie l'attestation d'affiliation du club de l'utilisateur connecté", description =
"Renvoie l'attestation d'affiliation du club de l'utilisateur connecté") "Renvoie l'attestation d'affiliation du club de l'utilisateur connecté")
@APIResponses(value = { @APIResponses(value = {
@@ -296,7 +276,7 @@ public class ClubEndpoints {
@GET @GET
@Path("/renew/{id}") @Path("/renew/{id}")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Operation(hidden = true) @Operation(hidden = true)
public Uni<RenewAffData> getRenew(@PathParam("id") long id, @QueryParam("m1") long m1_id, public Uni<RenewAffData> getRenew(@PathParam("id") long id, @QueryParam("m1") long m1_id,
@@ -307,7 +287,7 @@ public class ClubEndpoints {
@GET @GET
@Path("/desk/{id}") @Path("/desk/{id}")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Renvoie la liste des membres du bureau du club", description = "Renvoie la liste des membres " + @Operation(summary = "Renvoie la liste des membres du bureau du club", description = "Renvoie la liste des membres " +
"du bureau du club spécifié") "du bureau du club spécifié")
@@ -335,7 +315,7 @@ public class ClubEndpoints {
@Parameter(description = "Identifiant long (clubId) de club") @PathParam("clubId") String clubId) { @Parameter(description = "Identifiant long (clubId) de club") @PathParam("clubId") String clubId) {
return clubService.getByClubId(clubId).chain(Unchecked.function(clubModel -> { return clubService.getByClubId(clubId).chain(Unchecked.function(clubModel -> {
try { try {
return Utils.getMediaFile((clubModel != null) ? clubModel.getId() : -1, media, "ppClub", return Utils.getMediaFileNoDefault((clubModel != null) ? clubModel.getId() : -1, media, "ppClub",
Uni.createFrom().nullItem()); Uni.createFrom().nullItem());
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
throw new InternalError(); throw new InternalError();
@@ -345,7 +325,7 @@ public class ClubEndpoints {
@GET @GET
@Path("{id}/status") @Path("{id}/status")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Operation(summary = "Renvoie le statut du club", description = "Renvoie le statut du club spécifié") @Operation(summary = "Renvoie le statut du club", description = "Renvoie le statut du club spécifié")
@APIResponses(value = { @APIResponses(value = {
@APIResponse(responseCode = "200", description = "Le statut du club"), @APIResponse(responseCode = "200", description = "Le statut du club"),
@@ -358,7 +338,7 @@ public class ClubEndpoints {
return clubService.getById(id).onItem().invoke(checkPerm).chain(Unchecked.function(clubModel -> { return clubService.getById(id).onItem().invoke(checkPerm).chain(Unchecked.function(clubModel -> {
try { try {
return Utils.getMediaFile(clubModel.getId(), media, "clubStatus", return Utils.getMediaFile(clubModel.getId(), media, "clubStatus",
"statue-" + clubModel.getName(), Uni.createFrom().nullItem()); "statue-" + clubModel.getName(), Uni.createFrom().nullItem(), false);
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
throw new InternalError(); throw new InternalError();
} }

View File

@@ -47,4 +47,12 @@ public class CompetitionAdminEndpoints {
public Uni<List<CompetitionData>> getAllSystemAdmin(@PathParam("system") CompetitionSystem system) { public Uni<List<CompetitionData>> getAllSystemAdmin(@PathParam("system") CompetitionSystem system) {
return service.getAllSystemAdmin(securityCtx, system); return service.getAllSystemAdmin(securityCtx, system);
} }
@GET
@Path("all/{system}/table")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<List<CompetitionData>> getAllSystemTable(@PathParam("system") CompetitionSystem system) {
return service.getAllSystemTable(securityCtx, system);
}
} }

View File

@@ -1,10 +1,7 @@
package fr.titionfire.ffsaf.rest; package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.domain.service.CompetitionService; import fr.titionfire.ffsaf.domain.service.CompetitionService;
import fr.titionfire.ffsaf.rest.data.CompetitionData; import fr.titionfire.ffsaf.rest.data.*;
import fr.titionfire.ffsaf.rest.data.RegisterRequestData;
import fr.titionfire.ffsaf.rest.data.SimpleCompetData;
import fr.titionfire.ffsaf.rest.data.SimpleRegisterComb;
import fr.titionfire.ffsaf.utils.SecurityCtx; import fr.titionfire.ffsaf.utils.SecurityCtx;
import io.quarkus.security.Authenticated; import io.quarkus.security.Authenticated;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
@@ -53,6 +50,16 @@ public class CompetitionEndpoints {
return service.addRegisterComb(securityCtx, id, data, source); return service.addRegisterComb(securityCtx, id, data, source);
} }
@POST
@Path("{id}/registers/{source}")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
@Operation(hidden = true)
public Uni<List<SimpleRegisterComb>> addRegistersComb(@PathParam("id") Long id, @PathParam("source") String source,
List<RegisterRequestData> data) {
return service.addRegistersComb(securityCtx, id, data, source);
}
@DELETE @DELETE
@Path("{id}/register/{comb_id}/{source}") @Path("{id}/register/{comb_id}/{source}")
@Authenticated @Authenticated
@@ -71,6 +78,21 @@ public class CompetitionEndpoints {
return service.getSafcaData(securityCtx, id); 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("{id}/categories")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<List<PresetData>> getPresetsForCompetition(@PathParam("id") Long id) {
return service.getPresetsForCompetition(securityCtx, id);
}
@GET @GET
@Path("all") @Path("all")
@@ -95,6 +117,14 @@ public class CompetitionEndpoints {
return service.setSafcaData(securityCtx, data); return service.setSafcaData(securityCtx, data);
} }
@POST
@Path("/internalData")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<?> setInternalData(SimpleCompetData data) {
return service.setInternalData(securityCtx, data);
}
@DELETE @DELETE
@Path("{id}") @Path("{id}")
@Authenticated @Authenticated

View File

@@ -37,7 +37,7 @@ public class CompteEndpoints {
@GET @GET
@Path("{id}") @Path("{id}")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Operation(summary = "Renvoie les informations d'un compte utilisateur", description = "Renvoie les informations d'un" + @Operation(summary = "Renvoie les informations d'un compte utilisateur", description = "Renvoie les informations d'un" +
" compte utilisateur en fonction de son identifiant long (UUID)") " compte utilisateur en fonction de son identifiant long (UUID)")
@APIResponses(value = { @APIResponses(value = {

View File

@@ -0,0 +1,92 @@
package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.domain.service.ResultService;
import fr.titionfire.ffsaf.domain.service.UpdateService;
import io.smallrye.mutiny.Uni;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import java.util.HashMap;
import java.util.List;
@Path("api/public/result/{id}")
public class ExternalResultEndpoints {
@Inject
ResultService resultService;
@Inject
UpdateService updateService;
@PathParam("id")
private String id;
@GET
@Path("/poule/list")
@Produces(MediaType.APPLICATION_JSON)
public Uni<HashMap<String, Long>> list() {
return resultService.getCategoryList(id);
}
@GET
@Path("/poule/data")
@Produces(MediaType.APPLICATION_JSON)
public Uni<?> getArray(@QueryParam("poule") long poule, @DefaultValue("-1") @QueryParam("rf") long rf) {
if (poule == 0)
return Uni.createFrom().voidItem();
if (updateService.needUpdate(poule, rf)) {
return resultService.getCategory(id, poule);
} else {
return Uni.createFrom().voidItem();
}
}
@GET
@Path("/comb/list")
@Produces(MediaType.APPLICATION_JSON)
public Uni<HashMap<String, String>> combList() {
return resultService.getCombList(id);
}
@GET
@Path("/comb/data")
@Produces(MediaType.APPLICATION_JSON)
public Uni<?> getArray(@QueryParam("comb") String comb) {
if (comb.equals("0"))
return Uni.createFrom().item("");
return resultService.getCombArrayPublic(id, comb);
}
@GET
@Path("/comb/get_all")
@Produces(MediaType.APPLICATION_JSON)
public Uni<?> getAll() {
return resultService.getAllCombArrayPublic(id);
}
@GET
@Path("/club/list")
@Produces(MediaType.APPLICATION_JSON)
public Uni<HashMap<String, Long>> clubList() {
return resultService.getClubList(id);
}
@GET
@Path("/club/classement")
@Produces(MediaType.APPLICATION_JSON)
public Uni<List<ResultService.ClubClassement>> clubClassement() {
return resultService.getAllClubArray(id);
}
@GET
@Path("/club/data")
@Produces(MediaType.APPLICATION_JSON)
public Uni<?> getClubArray(@QueryParam("club") long club) {
if (club == 0)
return Uni.createFrom().item("");
return resultService.getClubArray2(id, club, null);
}
}

View File

@@ -36,7 +36,7 @@ public class LicenceEndpoints {
@GET @GET
@Path("{id}") @Path("{id}")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Renvoie les licences d'un membre", description = "Renvoie les licences d'un membre en fonction " + @Operation(summary = "Renvoie les licences d'un membre", description = "Renvoie les licences d'un membre en fonction " +
"de son identifiant") "de son identifiant")
@@ -68,7 +68,7 @@ public class LicenceEndpoints {
@GET @GET
@Path("current/club") @Path("current/club")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Renvoie les licences de la saison en cours (pour les clubs)", description = "Renvoie les " + @Operation(summary = "Renvoie les licences de la saison en cours (pour les clubs)", description = "Renvoie les " +
"licences de la saison en cours (pour les clubs)") "licences de la saison en cours (pour les clubs)")
@@ -83,7 +83,7 @@ public class LicenceEndpoints {
@POST @POST
@Path("pay") @Path("pay")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Operation(summary = "Paiement des licence", description = "Retourne le lien de paiement pour les licence des membre fournie") @Operation(summary = "Paiement des licence", description = "Retourne le lien de paiement pour les licence des membre fournie")
@@ -101,7 +101,7 @@ public class LicenceEndpoints {
@RolesAllowed("federation_admin") @RolesAllowed("federation_admin")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.MULTIPART_FORM_DATA) @Consumes(MediaType.MULTIPART_FORM_DATA)
@Operation(summary = "Créer une licence", description = "Créer unr licence en fonction de son identifiant et des " + @Operation(summary = "Créer une licence", description = "Créer une licence en fonction de son identifiant et des " +
"informations fournies dans le formulaire (pour les administrateurs)") "informations fournies dans le formulaire (pour les administrateurs)")
@APIResponses(value = { @APIResponses(value = {
@APIResponse(responseCode = "200", description = "La licence a été mise à jour avec succès"), @APIResponse(responseCode = "200", description = "La licence a été mise à jour avec succès"),
@@ -118,7 +118,7 @@ public class LicenceEndpoints {
@RolesAllowed("federation_admin") @RolesAllowed("federation_admin")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Operation(summary = "Validation licence", description = "Valide en masse les licence de l'année en cours (pour les administrateurs)") @Operation(summary = "Validation licence", description = "Valide en masse les licences de l'année en cours (pour les administrateurs)")
@APIResponses(value = { @APIResponses(value = {
@APIResponse(responseCode = "200", description = "Les licences ont été mise à jour avec succès"), @APIResponse(responseCode = "200", description = "Les licences ont été mise à jour avec succès"),
@APIResponse(responseCode = "403", description = "Accès refusé"), @APIResponse(responseCode = "403", description = "Accès refusé"),
@@ -128,6 +128,21 @@ public class LicenceEndpoints {
return licenceService.valideLicences(ids); return licenceService.valideLicences(ids);
} }
@POST
@Path("validate-pay")
@RolesAllowed("federation_admin")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Operation(summary = "Validation du payment des licences", description = "Valide en masse le payment des licences de l'année en cours (pour les administrateurs)")
@APIResponses(value = {
@APIResponse(responseCode = "200", description = "Les licences ont été mise à jour avec succès"),
@APIResponse(responseCode = "403", description = "Accès refusé"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Uni<?> validePaymentLicences(@Parameter(description = "Id des membres a valider") List<Long> ids) {
return licenceService.validePaymentLicences(ids);
}
@DELETE @DELETE
@Path("{id}") @Path("{id}")
@RolesAllowed("federation_admin") @RolesAllowed("federation_admin")
@@ -146,7 +161,7 @@ public class LicenceEndpoints {
@POST @POST
@Path("club/{id}") @Path("club/{id}")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.MULTIPART_FORM_DATA) @Consumes(MediaType.MULTIPART_FORM_DATA)
@Operation(summary = "Demande une nouvelle licence", description = "Demande une nouvelle licence en fonction de" + @Operation(summary = "Demande une nouvelle licence", description = "Demande une nouvelle licence en fonction de" +
@@ -163,7 +178,7 @@ public class LicenceEndpoints {
@DELETE @DELETE
@Path("club/{id}") @Path("club/{id}")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.TEXT_PLAIN) @Produces(MediaType.TEXT_PLAIN)
@Operation(summary = "Supprime une demande de licence", description = "Supprime une demande de licence en fonction " + @Operation(summary = "Supprime une demande de licence", description = "Supprime une demande de licence en fonction " +
"de son identifiant (pour les clubs)") "de son identifiant (pour les clubs)")

View File

@@ -2,6 +2,7 @@ package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.domain.service.MembreService; import fr.titionfire.ffsaf.domain.service.MembreService;
import fr.titionfire.ffsaf.domain.service.VirusScannerService;
import fr.titionfire.ffsaf.rest.data.SimpleMembre; import fr.titionfire.ffsaf.rest.data.SimpleMembre;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.exception.DInternalError; import fr.titionfire.ffsaf.rest.exception.DInternalError;
@@ -21,6 +22,7 @@ import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses; import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.tags.Tag; import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.logging.Logger;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -29,10 +31,14 @@ import java.util.function.Consumer;
@Path("api/member") @Path("api/member")
@RolesAllowed({"federation_admin"}) @RolesAllowed({"federation_admin"})
public class MembreAdminEndpoints { public class MembreAdminEndpoints {
private static final Logger LOGGER = Logger.getLogger(MembreAdminEndpoints.class);
@Inject @Inject
MembreService membreService; MembreService membreService;
@Inject
VirusScannerService scannerService;
@ConfigProperty(name = "upload_dir") @ConfigProperty(name = "upload_dir")
String media; String media;
@@ -59,6 +65,7 @@ public class MembreAdminEndpoints {
@Parameter(description = "Text à rechercher") @QueryParam("search") String search, @Parameter(description = "Text à rechercher") @QueryParam("search") String search,
@Parameter(description = "Club à filter") @QueryParam("club") String club, @Parameter(description = "Club à filter") @QueryParam("club") String club,
@Parameter(description = "Catégorie à filter") @QueryParam("categorie") String categorie, @Parameter(description = "Catégorie à filter") @QueryParam("categorie") String categorie,
@Parameter(description = "Inclure les archive") @QueryParam("archive") Boolean archive,
@Parameter(description = "État de la demande de licence: 0 -> sans demande, 1 -> avec demande ou validée, 2 -> toute les demande non validée, 3 -> validée, 4 -> tout, 5 -> demande complete, 6 -> demande incomplete") @QueryParam("licenceRequest") int licenceRequest, @Parameter(description = "État de la demande de licence: 0 -> sans demande, 1 -> avec demande ou validée, 2 -> toute les demande non validée, 3 -> validée, 4 -> tout, 5 -> demande complete, 6 -> demande incomplete") @QueryParam("licenceRequest") int licenceRequest,
@Parameter(description = "État du payment: 0 -> non payer, 1 -> payer, 2 -> tout") @QueryParam("payment") int payment, @Parameter(description = "État du payment: 0 -> non payer, 1 -> payer, 2 -> tout") @QueryParam("payment") int payment,
@Parameter(description = "Ordre") @QueryParam("order") String order) { @Parameter(description = "Ordre") @QueryParam("order") String order) {
@@ -66,7 +73,8 @@ public class MembreAdminEndpoints {
limit = 50; limit = 50;
if (page == null || page < 1) if (page == null || page < 1)
page = 1; page = 1;
return membreService.searchAdmin(limit, page - 1, search, club, licenceRequest, payment, order, categorie); return membreService.searchAdmin(limit, page - 1, search, club, licenceRequest, payment, order,
categorie, archive);
} }
@GET @GET
@@ -95,16 +103,8 @@ public class MembreAdminEndpoints {
.invoke(Unchecked.consumer(out -> { .invoke(Unchecked.consumer(out -> {
if (!out.equals("OK")) if (!out.equals("OK"))
throw new DInternalError("Impossible de reconnaitre le fichier: " + out); throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
})).chain(() -> { }))
if (input.getPhoto_data().length > 0) .call(__ -> Utils.uploadFile(scannerService, input.getPhoto_data(), id, media, "ppMembre"));
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
)).invoke(Unchecked.consumer(out -> {
if (!out.equals("OK"))
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
}));
else
return Uni.createFrom().nullItem();
});
} }
@POST @POST
@@ -121,13 +121,8 @@ public class MembreAdminEndpoints {
return membreService.add(input) return membreService.add(input)
.invoke(Unchecked.consumer(id -> { .invoke(Unchecked.consumer(id -> {
if (id == null) throw new InternalError("Fail to creat member data"); if (id == null) throw new InternalError("Fail to creat member data");
})).call(id -> { }))
if (input.getPhoto_data().length > 0) .call(id -> Utils.uploadFile(scannerService, input.getPhoto_data(), id, media, "ppMembre"));
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
));
else
return Uni.createFrom().nullItem();
});
} }
@DELETE @DELETE

View File

@@ -1,9 +1,9 @@
package fr.titionfire.ffsaf.rest; package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.domain.service.MembreService; import fr.titionfire.ffsaf.domain.service.MembreService;
import fr.titionfire.ffsaf.domain.service.VirusScannerService;
import fr.titionfire.ffsaf.rest.data.SimpleMembre; import fr.titionfire.ffsaf.rest.data.SimpleMembre;
import fr.titionfire.ffsaf.rest.data.SimpleMembreInOutData; import fr.titionfire.ffsaf.rest.data.SimpleMembreInOutData;
import fr.titionfire.ffsaf.rest.exception.DInternalError;
import fr.titionfire.ffsaf.rest.from.FullMemberForm; import fr.titionfire.ffsaf.rest.from.FullMemberForm;
import fr.titionfire.ffsaf.utils.PageResult; import fr.titionfire.ffsaf.utils.PageResult;
import fr.titionfire.ffsaf.utils.SecurityCtx; import fr.titionfire.ffsaf.utils.SecurityCtx;
@@ -24,7 +24,7 @@ import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import java.util.List; import java.util.List;
@Tag(name = "Membre club", description = "Gestion des membres (pour les clubs)") @Tag(name = "Membre club", description = "Gestion des membres (pour les clubs)")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Path("api/member") @Path("api/member")
public class MembreClubEndpoints { public class MembreClubEndpoints {
@@ -37,6 +37,9 @@ public class MembreClubEndpoints {
@Inject @Inject
SecurityCtx securityCtx; SecurityCtx securityCtx;
@Inject
VirusScannerService scannerService;
@GET @GET
@Path("/find/club") @Path("/find/club")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@@ -51,6 +54,7 @@ public class MembreClubEndpoints {
@Parameter(description = "Page à consulter") @QueryParam("page") Integer page, @Parameter(description = "Page à consulter") @QueryParam("page") Integer page,
@Parameter(description = "Text à rechercher") @QueryParam("search") String search, @Parameter(description = "Text à rechercher") @QueryParam("search") String search,
@Parameter(description = "Catégorie à filter") @QueryParam("categorie") String categorie, @Parameter(description = "Catégorie à filter") @QueryParam("categorie") String categorie,
@Parameter(description = "Inclure les archive") @QueryParam("archive") Boolean archive,
@Parameter(description = "Etat de la demande de licence: 0 -> sans demande, 1 -> avec demande ou validée, 2 -> toute les demande non validée, 3 -> validée, 4 -> tout, 5 -> demande complete, 6 -> demande incomplete") @QueryParam("licenceRequest") int licenceRequest, @Parameter(description = "Etat de la demande de licence: 0 -> sans demande, 1 -> avec demande ou validée, 2 -> toute les demande non validée, 3 -> validée, 4 -> tout, 5 -> demande complete, 6 -> demande incomplete") @QueryParam("licenceRequest") int licenceRequest,
@Parameter(description = "Etat du payment: 0 -> non payer, 1 -> payer, 2 -> tout") @QueryParam("payment") int payment, @Parameter(description = "Etat du payment: 0 -> non payer, 1 -> payer, 2 -> tout") @QueryParam("payment") int payment,
@Parameter(description = "Ordre") @QueryParam("order") String order) { @Parameter(description = "Ordre") @QueryParam("order") String order) {
@@ -58,7 +62,8 @@ public class MembreClubEndpoints {
limit = 50; limit = 50;
if (page == null || page < 1) if (page == null || page < 1)
page = 1; page = 1;
return membreService.search(limit, page - 1, search, licenceRequest, payment, order, categorie, securityCtx.getSubject()); return membreService.search(limit, page - 1, search, licenceRequest, payment, order, categorie, archive,
securityCtx.getSubject());
} }
@GET @GET
@@ -106,16 +111,7 @@ public class MembreClubEndpoints {
return membreService.update(id, input, securityCtx) return membreService.update(id, input, securityCtx)
.invoke(Unchecked.consumer(out -> { .invoke(Unchecked.consumer(out -> {
if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out); if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out);
})).chain(() -> { })).chain(() -> Utils.uploadFile(scannerService, input.getPhoto_data(), id, media, "ppMembre"));
if (input.getPhoto_data().length > 0)
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
)).invoke(Unchecked.consumer(out -> {
if (!out.equals("OK"))
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
}));
else
return Uni.createFrom().nullItem();
});
} }
@POST @POST
@@ -133,13 +129,7 @@ public class MembreClubEndpoints {
return membreService.add(input, securityCtx.getSubject()) return membreService.add(input, securityCtx.getSubject())
.invoke(Unchecked.consumer(id -> { .invoke(Unchecked.consumer(id -> {
if (id == null) throw new InternalError("Fail to creat member data"); if (id == null) throw new InternalError("Fail to creat member data");
})).call(id -> { })).call(id -> Utils.uploadFile(scannerService, input.getPhoto_data(), id, media, "ppMembre"));
if (input.getPhoto_data().length > 0)
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
));
else
return Uni.createFrom().nullItem();
});
} }
@DELETE @DELETE

View File

@@ -6,6 +6,7 @@ import fr.titionfire.ffsaf.domain.service.PDFService;
import fr.titionfire.ffsaf.rest.data.MeData; import fr.titionfire.ffsaf.rest.data.MeData;
import fr.titionfire.ffsaf.rest.data.SimpleMembre; import fr.titionfire.ffsaf.rest.data.SimpleMembre;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.from.UserSettingForm;
import fr.titionfire.ffsaf.utils.SecurityCtx; import fr.titionfire.ffsaf.utils.SecurityCtx;
import fr.titionfire.ffsaf.utils.Utils; import fr.titionfire.ffsaf.utils.Utils;
import io.quarkus.security.Authenticated; import io.quarkus.security.Authenticated;
@@ -51,7 +52,7 @@ public class MembreEndpoints {
@GET @GET
@Path("{id}") @Path("{id}")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Renvoie les détails d'un membre en fonction de son identifiant", description = "Renvoie les " + @Operation(summary = "Renvoie les détails d'un membre en fonction de son identifiant", description = "Renvoie les " +
"détails d'un membre en fonction de son identifiant") "détails d'un membre en fonction de son identifiant")
@@ -68,7 +69,7 @@ public class MembreEndpoints {
@GET @GET
@Path("/find/licence") @Path("/find/licence")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Operation(summary = "Renvoie les détails d'un membre en fonction de son numéro de licence", description = "Renvoie " + @Operation(summary = "Renvoie les détails d'un membre en fonction de son numéro de licence", description = "Renvoie " +
"les détails d'un membre en fonction de son numéro de licence") "les détails d'un membre en fonction de son numéro de licence")
@APIResponses(value = { @APIResponses(value = {
@@ -113,6 +114,21 @@ public class MembreEndpoints {
return pdfService.getLicencePdf(securityCtx.getSubject()); return pdfService.getLicencePdf(securityCtx.getSubject());
} }
@PUT
@Path("me/setting")
@Authenticated
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
@Operation(summary = "Met à jour les paramètres du membre connecté", description = "Met à jour les paramètres du membre connecté")
@APIResponses(value = {
@APIResponse(responseCode = "200", description = "Les paramètres ont été mis à jour"),
@APIResponse(responseCode = "403", description = "Accès refusé"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Uni<Void> updateMeSettings(UserSettingForm settingForm) {
return membreService.updateSettings(securityCtx.getSubject(), settingForm);
}
@GET @GET
@Path("me/photo") @Path("me/photo")
@Authenticated @Authenticated
@@ -130,7 +146,7 @@ public class MembreEndpoints {
@GET @GET
@Path("{id}/photo") @Path("{id}/photo")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Operation(summary = "Renvoie la photo d'un membre", description = "Renvoie la photo d'un membre en fonction de son identifiant") @Operation(summary = "Renvoie la photo d'un membre", description = "Renvoie la photo d'un membre en fonction de son identifiant")
@APIResponses(value = { @APIResponses(value = {
@APIResponse(responseCode = "200", description = "La photo du membre"), @APIResponse(responseCode = "200", description = "La photo du membre"),
@@ -146,7 +162,7 @@ public class MembreEndpoints {
@GET @GET
@Path("{id}/licence") @Path("{id}/licence")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"}) @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Operation(summary = "Renvoie le pdf de la licence d'un membre", description = "Renvoie le pdf de la licence d'un membre en fonction de son identifiant") @Operation(summary = "Renvoie le pdf de la licence d'un membre", description = "Renvoie le pdf de la licence d'un membre en fonction de son identifiant")
@APIResponses(value = { @APIResponses(value = {
@APIResponse(responseCode = "200", description = "Le pdf de la licence"), @APIResponse(responseCode = "200", description = "Le pdf de la licence"),

View File

@@ -10,6 +10,7 @@ import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path; import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam; import jakarta.ws.rs.PathParam;
import java.util.HashMap;
import java.util.List; import java.util.List;
@Authenticated @Authenticated
@@ -29,15 +30,45 @@ public class ResultEndpoints {
} }
@GET @GET
@Path("{uuid}") @Path("{uuid}/category/list")
public Uni<List<ResultCategoryData>> getCategory(@PathParam("uuid") String uuid) { public Uni<HashMap<String, Long>> getCategoryList(@PathParam("uuid") String uuid) {
return resultService.getCategory(uuid, securityCtx); return resultService.getCategoryList(uuid, securityCtx);
} }
@GET @GET
@Path("{uuid}/club") @Path("{uuid}/category/{id}")
public Uni<ResultService.ClubArrayData> getClub(@PathParam("uuid") String uuid) { public Uni<ResultCategoryData> getCategory(@PathParam("uuid") String uuid, @PathParam("id") long id) {
return resultService.getClubArray(uuid, securityCtx); return resultService.getCategory(uuid, id, securityCtx);
}
@GET
@Path("{uuid}/club/list")
public Uni<HashMap<String, Long>> getClubList(@PathParam("uuid") String uuid) {
return resultService.getClubList(uuid, securityCtx);
}
@GET
@Path("{uuid}/club/classement")
public Uni<List<ResultService.ClubClassement>> getClubClassement(@PathParam("uuid") String uuid) {
return resultService.getAllClubArray(uuid, securityCtx);
}
@GET
@Path("{uuid}/club/{id}")
public Uni<ResultService.ClubArrayData> getClub(@PathParam("uuid") String uuid, @PathParam("id") long id) {
return resultService.getClubArray(uuid, id, securityCtx);
}
@GET
@Path("{uuid}/comb/list")
public Uni<HashMap<String, String>> getCombList(@PathParam("uuid") String uuid) {
return resultService.getCombList(uuid, securityCtx);
}
@GET
@Path("{uuid}/comb/{id}")
public Uni<?> getCombList(@PathParam("uuid") String uuid, @PathParam("id") String id) {
return resultService.getCombArrayPublic(uuid, id, securityCtx);
} }
@GET @GET

View File

@@ -0,0 +1,85 @@
package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.domain.service.SelectionService;
import fr.titionfire.ffsaf.rest.data.SimpleSelection;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.utils.SecurityCtx;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import java.util.List;
import java.util.function.Consumer;
@Path("api/selection")
public class SelectionEndpoints {
@Inject
SelectionService selectionService;
@Inject
SecurityCtx securityCtx;
Consumer<MembreModel> checkPerm = Unchecked.consumer(membreModel -> {
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(membreModel.getClub().getId()))
throw new DForbiddenException();
});
@GET
@Path("{id}")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra",
"ffsaf_selectionneur"})
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Renvoie les séléctions d'un membre", description = "Renvoie les séléctions d'un membre en fonction " +
"de son identifiant")
@APIResponses(value = {
@APIResponse(responseCode = "200", description = "La liste des séléctions du membre"),
@APIResponse(responseCode = "403", description = "Accès refusé"),
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Uni<List<SimpleSelection>> getSelection(@PathParam("id") long id) {
return selectionService.getSelection(id, checkPerm)
.map(selectionModels -> selectionModels.stream().map(SimpleSelection::fromModel).toList());
}
@POST
@Path("{id}")
@RolesAllowed({"federation_admin", "ffsaf_selectionneur"})
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Operation(summary = "Créer une séléction", description = "Créer une séléction en fonction de son identifiant et des " +
"informations fournies dans le formulaire (pour les administrateurs)")
@APIResponses(value = {
@APIResponse(responseCode = "200", description = "La séléction a été mise à jour avec succès"),
@APIResponse(responseCode = "403", description = "Accès refusé"),
@APIResponse(responseCode = "404", description = "La séléction n'existe pas"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Uni<SimpleSelection> setSelection(@PathParam("id") long id, SimpleSelection data) {
return selectionService.setSelection(id, data).map(SimpleSelection::fromModel);
}
@DELETE
@Path("{id}")
@RolesAllowed({"federation_admin", "ffsaf_selectionneur"})
@Produces(MediaType.TEXT_PLAIN)
@Operation(summary = "Supprime une séléction", description = "Supprime une séléction en fonction de son identifiant " +
"(pour les administrateurs)")
@APIResponses(value = {
@APIResponse(responseCode = "204", description = "La séléction a été supprimée avec succès"),
@APIResponse(responseCode = "403", description = "Accès refusé"),
@APIResponse(responseCode = "404", description = "La séléction n'existe pas"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Uni<?> deleteSelection(@PathParam("id") long id) {
return selectionService.deleteSelection(id);
}
}

View File

@@ -1,5 +1,6 @@
package fr.titionfire.ffsaf.rest.data; package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.data.model.CatPresetModel;
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel; import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
import fr.titionfire.ffsaf.data.model.CompetitionModel; import fr.titionfire.ffsaf.data.model.CompetitionModel;
import fr.titionfire.ffsaf.data.model.RegisterModel; import fr.titionfire.ffsaf.data.model.RegisterModel;
@@ -10,6 +11,7 @@ import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -33,12 +35,23 @@ public class CompetitionData {
private Long club; private Long club;
private String clubName; private String clubName;
private String owner; private String owner;
private List<SimpleRegister> registers; private List<SimpleRegister> registers; // for SAFCA
private boolean canEdit; private boolean canEdit;
private boolean canEditRegisters;
private String data1; private String data1;
private String data2; private String data2;
private String data3; private String data3;
private String data4; private String data4;
private String config;
private List<PresetData> presets;
private List<Categorie> requiredWeight;
public CompetitionData() {
this(null, "", "", "", "", new Date(), new Date(),
CompetitionSystem.INTERNAL, RegisterMode.FREE, new Date(), new Date(), true,
null, "", "", null, true, true,
"", "", "", "", "{}", new ArrayList<>(), new ArrayList<>());
}
public static CompetitionData fromModel(CompetitionModel model) { public static CompetitionData fromModel(CompetitionModel model) {
if (model == null) if (model == null)
@@ -47,8 +60,9 @@ public class CompetitionData {
return new CompetitionData(model.getId(), model.getName(), model.getDescription(), model.getAdresse(), return new CompetitionData(model.getId(), model.getName(), model.getDescription(), model.getAdresse(),
model.getUuid(), model.getDate(), model.getTodate(), model.getSystem(), model.getUuid(), model.getDate(), model.getTodate(), model.getSystem(),
model.getRegisterMode(), model.getStartRegister(), model.getEndRegister(), model.isPublicVisible(), model.getRegisterMode(), model.getStartRegister(), model.getEndRegister(), model.isPublicVisible(),
model.getClub().getId(), model.getClub().getName(), model.getOwner(), null, false, model.getClub().getId(), model.getClub().getName(), model.getOwner(), null, false, false,
model.getData1(), model.getData2(), model.getData3(), model.getData4()); model.getData1(), model.getData2(), model.getData3(), model.getData4(), model.getConfig(),
new ArrayList<>(), model.getRequiredWeight());
} }
public static CompetitionData fromModelLight(CompetitionModel model) { public static CompetitionData fromModelLight(CompetitionModel model) {
@@ -58,36 +72,42 @@ public class CompetitionData {
CompetitionData out = new CompetitionData(model.getId(), model.getName(), model.getDescription(), CompetitionData out = new CompetitionData(model.getId(), model.getName(), model.getDescription(),
model.getAdresse(), "", model.getDate(), model.getTodate(), null, model.getAdresse(), "", model.getDate(), model.getTodate(), null,
model.getRegisterMode(), model.getStartRegister(), model.getEndRegister(), model.isPublicVisible(), model.getRegisterMode(), model.getStartRegister(), model.getEndRegister(), model.isPublicVisible(),
null, model.getClub().getName(), "", null, false, null, model.getClub().getName(), "", null, false, false,
"", "", "", ""); "", "", "", "", "{}", new ArrayList<>(), model.getRequiredWeight());
if (model.getRegisterMode() == RegisterMode.HELLOASSO) { if (model.getRegisterMode() == RegisterMode.HELLOASSO) {
out.setData1(model.getData1()); out.setData1(model.getData1());
out.setData2(model.getData2()); out.setData2(model.getData2());
} }
return out; return out;
} }
public CompetitionData addInsc(List<RegisterModel> insc, List<CompetitionGuestModel> guests) { public CompetitionData addInsc(List<RegisterModel> insc, List<CompetitionGuestModel> guests) {
this.registers = Stream.concat( this.registers = Stream.concat(
insc.stream() insc.stream()
.map(i -> new SimpleRegister(i.getMembre().getId(), i.getOverCategory(), i.getWeight(), .map(i -> new SimpleRegister(i.getMembre().getId(), i.getOverCategory(), i.getWeight2(),
i.getCategorie(), (i.getClub() == null) ? null : i.getClub().getId(), i.getCategorie(), (i.getClub() == null) ? null : i.getClub().getId(),
(i.getClub() == null) ? null : i.getClub().getName())), (i.getClub() == null) ? null : i.getClub().getName())),
guests.stream() guests.stream()
.map(i -> new SimpleRegister(i.getId() * -1, 0, i.getWeight(), .map(i -> new SimpleRegister(i.getId() * -1, 0, i.getWeight2(),
i.getCategorie(), null, i.getClub()))).toList(); i.getCategorie(), null, i.getClub()))).toList();
return this; return this;
} }
public CompetitionData addPresets(List<CatPresetModel> presets) {
this.presets = presets.stream().map(PresetData::fromModel).toList();
return this;
}
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@RegisterForReflection @RegisterForReflection
public static class SimpleRegister { public static class SimpleRegister {
long id; long id;
int overCategory; int overCategory;
Integer weight; Float weight;
Categorie categorie; Categorie categorie;
Long club; Long club;
String club_str; String club_str;

View File

@@ -1,6 +1,8 @@
package fr.titionfire.ffsaf.rest.data; package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.domain.service.TradService;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -15,45 +17,50 @@ import java.util.List;
@NoArgsConstructor @NoArgsConstructor
@RegisterForReflection @RegisterForReflection
public class MeData { public class MeData {
@Schema(description = "L'identifiant du membre.", example = "1") @Schema(description = "L'identifiant du membre.", examples = "1")
private long id; private long id;
@Schema(description = "Le nom du membre.", example = "Dupont") @Schema(description = "Le nom du membre.", examples = "Dupont")
private String lname = ""; private String lname = "";
@Schema(description = "Le prénom du membre.", example = "Jean") @Schema(description = "Le prénom du membre.", examples = "Jean")
private String fname = ""; private String fname = "";
@Schema(description = "La catégorie du membre.", example = "SENIOR") @Schema(description = "La catégorie du membre.", examples = "SENIOR")
private String categorie; private String categorie;
@Schema(description = "Le nom du club du membre.", example = "Association sportive") @Schema(description = "Le nom du club du membre.", examples = "Association sportive")
private String club; private String club;
@Schema(description = "Le genre du membre.", example = "Homme") @Schema(description = "Le genre du membre.", examples = "Homme")
private String genre; private String genre;
@Schema(description = "Le numéro de licence du membre.", example = "12345") @Schema(description = "Le numéro de licence du membre.", examples = "12345")
private int licence; private int licence;
@Schema(description = "Le pays du membre.", example = "FR") @Schema(description = "Le pays du membre.", examples = "FR")
private String country; private String country;
@Schema(description = "La date de naissance du membre.") @Schema(description = "La date de naissance du membre.")
private Date birth_date; private Date birth_date;
@Schema(description = "L'adresse e-mail du membre.", example = "jean.dupont@example.com") @Schema(description = "L'adresse e-mail du membre.", examples = "jean.dupont@examples.com")
private String email; private String email;
@Schema(description = "Le rôle du membre dans l'association.", example = "MEMBRE") @Schema(description = "Le rôle du membre dans l'association.", examples = "MEMBRE")
private String role; private String role;
@Schema(description = "Le grade d'arbitrage du membre.", example = "N/A") @Schema(description = "Le grade d'arbitrage du membre.", examples = "N/A")
private String grade_arbitrage; private String grade_arbitrage;
@Schema(description = "La confidentialité des résultats", examples = "PUBLIC")
private ResultPrivacy resultPrivacy;
@Schema(description = "La liste des licences du membre.") @Schema(description = "La liste des licences du membre.")
private List<SimpleLicence> licences; private List<SimpleLicence> licences;
@Schema(description = "La liste des séléctions du membre.")
private List<SimpleSelection> selections;
public void setMembre(MembreModel membreModel) { public void setMembre(MembreModel membreModel, TradService trad) {
this.id = membreModel.getId(); this.id = membreModel.getId();
this.lname = membreModel.getLname(); this.lname = membreModel.getLname();
this.fname = membreModel.getFname(); this.fname = membreModel.getFname();
this.categorie = membreModel.getCategorie().getName(); this.categorie = membreModel.getCategorie() == null ? trad.t("categorie.inconnue") : membreModel.getCategorie().getName(trad);
this.club = membreModel.getClub() == null ? "Sans club" : membreModel.getClub().getName(); this.club = membreModel.getClub() == null ? trad.t("sans.club") : membreModel.getClub().getName();
this.genre = membreModel.getGenre().str; this.genre = membreModel.getGenre().getString(trad);
this.licence = membreModel.getLicence(); this.licence = membreModel.getLicence();
this.country = membreModel.getCountry(); this.country = membreModel.getCountry();
this.birth_date = membreModel.getBirth_date(); this.birth_date = membreModel.getBirth_date();
this.email = membreModel.getEmail(); this.email = membreModel.getEmail();
this.role = membreModel.getRole().str; this.role = membreModel.getRole().getString(trad);
this.grade_arbitrage = membreModel.getGrade_arbitrage().str; this.grade_arbitrage = membreModel.getGrade_arbitrage().getString(trad);
this.resultPrivacy = membreModel.getResultPrivacy();
} }
} }

View File

@@ -0,0 +1,31 @@
package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.data.model.CatPresetModel;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class PresetData {
private Long id;
private String name;
private CatPresetModel.SwordType sword;
private CatPresetModel.ShieldType shield;
private List<CatPresetModel.CategorieEmbeddable> categories;
private int mandatoryProtection1;
private int mandatoryProtection2;
public static PresetData fromModel(CatPresetModel model) {
if (model == null)
return null;
return new PresetData(model.getId(), model.getName(), model.getSwordType(), model.getShieldType(),
model.getCategories(), model.getMandatoryProtection1(), model.getMandatoryProtection2());
}
}

View File

@@ -7,6 +7,8 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.util.List;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@@ -16,9 +18,11 @@ public class RegisterRequestData {
private String fname; private String fname;
private String lname; private String lname;
private Integer weight; private Float weight;
private int overCategory; private Float weightReal;
private Integer overCategory;
private boolean lockEdit = false; private boolean lockEdit = false;
private List<Long> categoriesInscrites;
// for guest registration only // for guest registration only
private Long id = null; private Long id = null;
@@ -26,4 +30,6 @@ public class RegisterRequestData {
private Genre genre = Genre.NA; private Genre genre = Genre.NA;
private String club = null; private String club = null;
private String country = null; private String country = null;
private boolean quick = false;
} }

View File

@@ -1,6 +1,10 @@
package fr.titionfire.ffsaf.rest.data; package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.data.model.MatchModel; import com.fasterxml.jackson.annotation.JsonIgnore;
import fr.titionfire.ffsaf.data.model.CombModel;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.domain.entity.MatchModelExtend;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
import fr.titionfire.ffsaf.utils.ScoreEmbeddable; import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
import fr.titionfire.ffsaf.utils.TreeNode; import fr.titionfire.ffsaf.utils.TreeNode;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
@@ -9,6 +13,7 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -18,16 +23,23 @@ import java.util.List;
public class ResultCategoryData { public class ResultCategoryData {
int type; int type;
String name; String name;
boolean treeIsClassement;
HashMap<Character, List<PouleArrayData>> matchs = new HashMap<>(); HashMap<Character, List<PouleArrayData>> matchs = new HashMap<>();
HashMap<Character, List<RankArray>> rankArray = new HashMap<>(); HashMap<Character, List<RankArray>> rankArray = new HashMap<>();
ArrayList<TreeNode<TreeData>> trees; ArrayList<TreeNode<TreeData>> trees;
List<ClassementData> classement = new ArrayList<>();
String[] liceName;
long genTime;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@RegisterForReflection @RegisterForReflection
public static class RankArray { public static class RankArray {
int rank; int rank;
@JsonIgnore
CombModel comb;
String name; String name;
int score;
int win; int win;
int pointMake; int pointMake;
int pointTake; int pointTake;
@@ -35,25 +47,37 @@ public class ResultCategoryData {
} }
@RegisterForReflection @RegisterForReflection
public record PouleArrayData(String red, boolean red_w, List<Integer[]> score, boolean blue_w, String blue, boolean end) { public record PouleArrayData(String red, boolean red_w, List<Integer[]> score, boolean blue_w, String blue,
public static PouleArrayData fromModel(MatchModel matchModel) { boolean eq, boolean end, Date date) {
public static PouleArrayData fromModel(MatchModelExtend matchModel, MembreModel membreModel,
ResultPrivacy privacy) {
return new PouleArrayData( return new PouleArrayData(
matchModel.getC1Name(), matchModel.getC1Name(membreModel, privacy),
matchModel.isEnd() && matchModel.win() > 0, matchModel.isEnd() && matchModel.getWin() > 0,
matchModel.isEnd() ? matchModel.isEnd() ?
matchModel.getScores().stream().map(s -> new Integer[]{s.getS1(), s.getS2()}).toList() matchModel.getScoresToPrint().stream().map(s -> new Integer[]{s.getS1(), s.getS2()})
.toList()
: new ArrayList<>(), : new ArrayList<>(),
matchModel.isEnd() && matchModel.win() < 0, matchModel.isEnd() && matchModel.getWin() < 0,
matchModel.getC2Name(), matchModel.getC2Name(membreModel, privacy),
matchModel.isEnd()); matchModel.isEnd() && matchModel.getWin() == 0,
matchModel.isEnd(),
matchModel.getDate());
} }
} }
@RegisterForReflection @RegisterForReflection
public static record TreeData(long id, String c1FullName, String c2FullName, List<ScoreEmbeddable> scores, public static record TreeData(long id, String c1FullName, String c2FullName, List<ScoreEmbeddable> scores,
boolean end) { boolean end, int win, int level, Date date) {
public static TreeData from(MatchModel match) { public static TreeData from(MatchModelExtend match, int level, MembreModel membreModel, ResultPrivacy privacy) {
return new TreeData(match.getId(), match.getC1Name(), match.getC2Name(), match.getScores(), match.isEnd()); return new TreeData(match.getId(), match.getC1Name(membreModel, privacy),
match.getC2Name(membreModel, privacy), match.getScoresToPrint(), match.isEnd(), match.getWin(),
level, match.getDate());
} }
} }
@RegisterForReflection
public static record ClassementData(int rank, @JsonIgnore CombModel comb, String name) {
}
} }

View File

@@ -1,5 +1,9 @@
package fr.titionfire.ffsaf.rest.data; 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 fr.titionfire.ffsaf.net2.data.SimpleCompet;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@@ -25,4 +29,41 @@ public class SimpleCompetData {
return new SimpleCompetData(compet.id(), compet.show_blason(), compet.show_flag(), return new SimpleCompetData(compet.id(), compet.show_blason(), compet.show_flag(),
new ArrayList<>(), new ArrayList<>()); 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 "{}";
}
}
} }

View File

@@ -19,34 +19,32 @@ import java.util.Date;
@AllArgsConstructor @AllArgsConstructor
@RegisterForReflection @RegisterForReflection
public class SimpleMembre { public class SimpleMembre {
@Schema(description = "L'identifiant du membre.", example = "1") @Schema(description = "L'identifiant du membre.", examples = "1")
private long id; private long id;
@Schema(description = "L'identifiant long du membre (userID).", example = "e81d1d35-d897-421e-8086-6c5e74d13c6e") @Schema(description = "L'identifiant long du membre (userID).", examples = "e81d1d35-d897-421e-8086-6c5e74d13c6e")
private String userId; private String userId;
@Schema(description = "Le nom du membre.", example = "Dupont") @Schema(description = "Le nom du membre.", examples = "Dupont")
private String lname = ""; private String lname = "";
@Schema(description = "Le prénom du membre.", example = "Jean") @Schema(description = "Le prénom du membre.", examples = "Jean")
private String fname = ""; private String fname = "";
@Schema(description = "La catégorie du membre.", example = "SENIOR") @Schema(description = "La catégorie du membre.", examples = "SENIOR")
private Categorie categorie; private Categorie categorie;
@Schema(description = "Le club du membre.") @Schema(description = "Le club du membre.")
private SimpleClubModel club; private SimpleClubModel club;
@Schema(description = "Le genre du membre.", example = "H") @Schema(description = "Le genre du membre.", examples = "H")
private Genre genre; private Genre genre;
@Schema(description = "Le numéro de licence du membre.", example = "12345") @Schema(description = "Le numéro de licence du membre.", examples = "12345")
private Integer licence; private Integer licence;
@Schema(description = "Le pays du membre.", example = "FR") @Schema(description = "Le pays du membre.", examples = "FR")
private String country; private String country;
@Schema(description = "La date de naissance du membre.") @Schema(description = "La date de naissance du membre.")
private Date birth_date; private Date birth_date;
@Schema(description = "L'adresse e-mail du membre.", example = "jean.dupont@example.com") @Schema(description = "L'adresse e-mail du membre.", examples = "jean.dupont@examples.com")
private String email; private String email;
@Schema(description = "Le rôle du membre dans l'association.", example = "MEMBRE") @Schema(description = "Le rôle du membre dans l'association.", examples = "MEMBRE")
private RoleAsso role; private RoleAsso role;
@Schema(description = "Le grade d'arbitrage du membre.", example = "N/A") @Schema(description = "Le grade d'arbitrage du membre.", examples = "N/A")
private GradeArbitrage grade_arbitrage; private GradeArbitrage grade_arbitrage;
@Schema(hidden = true)
private String url_photo;
public static SimpleMembre fromModel(MembreModel model) { public static SimpleMembre fromModel(MembreModel model) {
if (model == null) if (model == null)
@@ -66,7 +64,6 @@ public class SimpleMembre {
.email(model.getEmail()) .email(model.getEmail())
.role(model.getRole()) .role(model.getRole())
.grade_arbitrage(model.getGrade_arbitrage()) .grade_arbitrage(model.getGrade_arbitrage())
.url_photo(model.getUrl_photo())
.build(); .build();
} }
} }

View File

@@ -2,10 +2,14 @@ package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.data.model.LicenceModel; import fr.titionfire.ffsaf.data.model.LicenceModel;
import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.utils.Utils;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@@ -24,7 +28,27 @@ public class SimpleMembreInOutData {
public static SimpleMembreInOutData fromModel(MembreModel membreModel, List<LicenceModel> lc) { public static SimpleMembreInOutData fromModel(MembreModel membreModel, List<LicenceModel> lc) {
LicenceModel currentLicence = lc.stream().filter(l -> l.getMembre().getId().equals(membreModel.getId())) LicenceModel currentLicence = lc.stream().filter(l -> l.getMembre().getId().equals(membreModel.getId()))
.findFirst().orElse(null); .max(Comparator.comparingInt(LicenceModel::getSaison)).orElse(null);
String certif = null;
if (currentLicence != null && currentLicence.getCertificate() != null) {
String[] strings = currentLicence.getCertificate().split("¤");
if (currentLicence.getSaison() == Utils.getSaison()) {
certif = currentLicence.getCertificate();
} else if (strings.length > 1) {
try {
Date date = new SimpleDateFormat("yyyy-MM-dd").parse(strings[1]);
Calendar max = Utils.getFirstDateOfSaison();
max.add(Calendar.YEAR, -2);
if (max.getTime().compareTo(date) < 0) {
certif = currentLicence.getCertificate();
}
} catch (Exception ignored) {
}
}
}
return new SimpleMembreInOutData( return new SimpleMembreInOutData(
membreModel.getLicence(), membreModel.getLicence(),
@@ -33,8 +57,8 @@ public class SimpleMembreInOutData {
membreModel.getEmail(), membreModel.getEmail(),
membreModel.getGenre().str, membreModel.getGenre().str,
membreModel.getBirth_date(), membreModel.getBirth_date(),
currentLicence != null, currentLicence != null && currentLicence.getSaison() == Utils.getSaison(),
currentLicence == null ? null : currentLicence.getCertificate() certif
); );
} }
} }

View File

@@ -1,9 +1,6 @@
package fr.titionfire.ffsaf.rest.data; package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel; import fr.titionfire.ffsaf.data.model.*;
import fr.titionfire.ffsaf.data.model.LicenceModel;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.data.model.RegisterModel;
import fr.titionfire.ffsaf.net2.data.SimpleClubModel; import fr.titionfire.ffsaf.net2.data.SimpleClubModel;
import fr.titionfire.ffsaf.utils.Categorie; import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.Genre; import fr.titionfire.ffsaf.utils.Genre;
@@ -11,11 +8,14 @@ import fr.titionfire.ffsaf.utils.Utils;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List; import java.util.List;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection @RegisterForReflection
public class SimpleRegisterComb { public class SimpleRegisterComb {
private long id; private long id;
@@ -26,10 +26,12 @@ public class SimpleRegisterComb {
private Categorie categorie; private Categorie categorie;
private SimpleClubModel club; private SimpleClubModel club;
private Integer licence; private Integer licence;
private Integer weight; private Float weight;
private Float weightReal;
private int overCategory; private int overCategory;
private boolean hasLicenceActive; private boolean hasLicenceActive;
private boolean lockEdit; private boolean lockEdit;
private List<Long> categoriesInscrites;
public static SimpleRegisterComb fromModel(RegisterModel register, List<LicenceModel> licences) { public static SimpleRegisterComb fromModel(RegisterModel register, List<LicenceModel> licences) {
MembreModel membreModel = register.getMembre(); MembreModel membreModel = register.getMembre();
@@ -37,15 +39,21 @@ public class SimpleRegisterComb {
membreModel.getGenre(), membreModel.getCountry(), membreModel.getGenre(), membreModel.getCountry(),
(register.getCategorie() == null) ? null : register.getCategorie(), (register.getCategorie() == null) ? null : register.getCategorie(),
SimpleClubModel.fromModel(register.getClub()), membreModel.getLicence(), register.getWeight(), SimpleClubModel.fromModel(register.getClub()), membreModel.getLicence(), register.getWeight(),
register.getOverCategory(), register.getWeightReal(), register.getOverCategory(),
licences.stream().anyMatch(l -> l.isValidate() && l.getSaison() == Utils.getSaison()), licences.stream().anyMatch(l -> l.isValidate() && l.getSaison() == Utils.getSaison()),
register.isLockEdit()); register.isLockEdit(), new ArrayList<>());
} }
public static SimpleRegisterComb fromModel(CompetitionGuestModel guest) { public static SimpleRegisterComb fromModel(CompetitionGuestModel guest) {
return new SimpleRegisterComb(guest.getId() * -1, guest.getFname(), guest.getLname(), return new SimpleRegisterComb(guest.getId() * -1, guest.getFname(), guest.getLname(),
guest.getGenre(), guest.getCountry(), guest.getCategorie(), guest.getGenre(), guest.getCountry(), guest.getCategorie(),
new SimpleClubModel(null, guest.getClub(), "fr", null), new SimpleClubModel(null, guest.getClub(), "fr", null),
null, guest.getWeight(), 0, false, false); null, guest.getWeight(), guest.getWeightReal(), 0, false, false,
new ArrayList<>());
}
public SimpleRegisterComb setCategorieInscrite(List<CatPresetModel> presets) {
this.categoriesInscrites = presets.stream().map(CatPresetModel::getId).toList();
return this;
} }
} }

View File

@@ -0,0 +1,36 @@
package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.data.model.SelectionModel;
import fr.titionfire.ffsaf.utils.Categorie;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
@Data
@Builder
@AllArgsConstructor
@RegisterForReflection
public class SimpleSelection {
@Schema(description = "ID de la séléction", examples = "1")
Long id;
@Schema(description = "ID du membre", examples = "1")
Long membre;
@Schema(description = "Saison de la séléction", examples = "2024")
int saison;
@Schema(description = "Catégorie de la séléction", examples = "JUNIOR")
Categorie categorie;
public static SimpleSelection fromModel(SelectionModel model) {
if (model == null)
return null;
return new SimpleSelection.SimpleSelectionBuilder()
.id(model.getId())
.membre(model.getMembre().getId())
.saison(model.getSaison())
.categorie(model.getCategorie())
.build();
}
}

View File

@@ -9,6 +9,7 @@ import lombok.ToString;
import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
import org.eclipse.microprofile.openapi.annotations.media.Schema; import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.jboss.resteasy.reactive.PartType; import org.jboss.resteasy.reactive.PartType;
import org.jboss.resteasy.reactive.multipart.FileUpload;
@Getter @Getter
@ToString(exclude = {"status", "logo"}) @ToString(exclude = {"status", "logo"})
@@ -17,85 +18,85 @@ public class AffiliationRequestForm {
@FormParam("id") @FormParam("id")
private Long id = null; private Long id = null;
@Schema(description = "Le nom de l'association.", example = "Association sportive", required = true) @Schema(description = "Le nom de l'association.", examples = "Association sportive", required = true)
@FormParam("name") @FormParam("name")
private String name = null; private String name = null;
@Schema(description = "Le numéro SIRET/RNA de l'association.", example = "12345678901234", required = true) @Schema(description = "Le numéro SIRET/RNA de l'association.", examples = "12345678901234", required = true)
@FormParam("state_id") @FormParam("state_id")
private String state_id = null; private String state_id = null;
@Schema(description = "L'adresse de l'association.", example = "1 rue de l'exemple, 75000 Paris", required = true) @Schema(description = "L'adresse de l'association.", examples = "1 rue de l'exemple, 75000 Paris", required = true)
@FormParam("adresse") @FormParam("adresse")
private String adresse = null; private String adresse = null;
@Schema(description = "Email de contact de l'association", example = "test@test.fr") @Schema(description = "Email de contact de l'association", examples = "test@test.fr")
@FormParam("contact") @FormParam("contact")
private String contact = null; private String contact = null;
@Schema(description = "La saison de l'affiliation.", example = "2025", required = true) @Schema(description = "La saison de l'affiliation.", examples = "2025", required = true)
@FormParam("saison") @FormParam("saison")
private int saison = -1; private int saison = -1;
@Schema(description = "Le statut de l'association.", type = SchemaType.ARRAY, implementation = byte.class) @Schema(description = "Le statut de l'association.", type = SchemaType.ARRAY)
@FormParam("status") @FormParam("status")
@PartType(MediaType.APPLICATION_OCTET_STREAM) @PartType(MediaType.APPLICATION_OCTET_STREAM)
private byte[] status = new byte[0]; private FileUpload status = null;
@Schema(description = "Le logo de l'association.", type = SchemaType.ARRAY, implementation = byte.class) @Schema(description = "Le logo de l'association.", type = SchemaType.ARRAY)
@FormParam("logo") @FormParam("logo")
@PartType(MediaType.APPLICATION_OCTET_STREAM) @PartType(MediaType.APPLICATION_OCTET_STREAM)
private byte[] logo = new byte[0]; private FileUpload logo = null;
@Schema(description = "Le nom du premier membre de l'association.", example = "Doe", required = true) @Schema(description = "Le nom du premier membre de l'association.", examples = "Doe", required = true)
@FormParam("m1_nom") @FormParam("m1_nom")
private String m1_lname = null; private String m1_lname = null;
@Schema(description = "Le prénom du premier membre de l'association.", example = "John", required = true) @Schema(description = "Le prénom du premier membre de l'association.", examples = "John", required = true)
@FormParam("m1_prenom") @FormParam("m1_prenom")
private String m1_fname = null; private String m1_fname = null;
@Schema(description = "L'adresse e-mail du premier membre de l'association.", example = "john.doe@test.com", required = true) @Schema(description = "L'adresse e-mail du premier membre de l'association.", examples = "john.doe@test.com", required = true)
@FormParam("m1_mail") @FormParam("m1_mail")
private String m1_email = null; private String m1_email = null;
@Schema(description = "Le numéro de licence du premier membre de l'association. (null si non licencié)", example = "12345") @Schema(description = "Le numéro de licence du premier membre de l'association. (null si non licencié)", examples = "12345")
@FormParam("m1_licence") @FormParam("m1_licence")
private String m1_lincence = null; private String m1_lincence = null;
@Schema(description = "Le rôle du premier membre de l'association. (doit être PRESIDENT)", example = "PRESIDENT", required = true) @Schema(description = "Le rôle du premier membre de l'association. (doit être PRESIDENT)", examples = "PRESIDENT", required = true)
@FormParam("m1_role") @FormParam("m1_role")
private RoleAsso m1_role = null; private RoleAsso m1_role = null;
@Schema(description = "Le nom du deuxième membre de l'association.", example = "Xavier", required = true) @Schema(description = "Le nom du deuxième membre de l'association.", examples = "Xavier", required = true)
@FormParam("m2_nom") @FormParam("m2_nom")
private String m2_lname = null; private String m2_lname = null;
@Schema(description = "Le prénom du deuxième membre de l'association.", example = "Login", required = true) @Schema(description = "Le prénom du deuxième membre de l'association.", examples = "Login", required = true)
@FormParam("m2_prenom") @FormParam("m2_prenom")
private String m2_fname = null; private String m2_fname = null;
@Schema(description = "L'adresse e-mail du deuxième membre de l'association.", example = "xavier.login@test.com", required = true) @Schema(description = "L'adresse e-mail du deuxième membre de l'association.", examples = "xavier.login@test.com", required = true)
@FormParam("m2_mail") @FormParam("m2_mail")
private String m2_email = null; private String m2_email = null;
@Schema(description = "Le numéro de licence du deuxième membre de l'association. (null si non licencié)", example = "04242") @Schema(description = "Le numéro de licence du deuxième membre de l'association. (null si non licencié)", examples = "04242")
@FormParam("m2_licence") @FormParam("m2_licence")
private String m2_lincence = null; private String m2_lincence = null;
@Schema(description = "Le rôle du deuxième membre de l'association.", example = "SECRETAIRE", required = true) @Schema(description = "Le rôle du deuxième membre de l'association.", examples = "SECRETAIRE", required = true)
@FormParam("m2_role") @FormParam("m2_role")
private RoleAsso m2_role = null; private RoleAsso m2_role = null;
@Schema(description = "Le nom du troisième membre de l'association.", example = "Doe2", required = true) @Schema(description = "Le nom du troisième membre de l'association.", examples = "Doe2", required = true)
@FormParam("m3_nom") @FormParam("m3_nom")
private String m3_lname = null; private String m3_lname = null;
@Schema(description = "Le prénom du troisième membre de l'association.", example = "John2", required = true) @Schema(description = "Le prénom du troisième membre de l'association.", examples = "John2", required = true)
@FormParam("m3_prenom") @FormParam("m3_prenom")
private String m3_fname = null; private String m3_fname = null;
@Schema(description = "L'adresse e-mail du troisième membre de l'association.", example = "john.doe22@test.com", required = true) @Schema(description = "L'adresse e-mail du troisième membre de l'association.", examples = "john.doe22@test.com", required = true)
@FormParam("m3_mail") @FormParam("m3_mail")
private String m3_email = null; private String m3_email = null;
@@ -103,7 +104,7 @@ public class AffiliationRequestForm {
@FormParam("m3_licence") @FormParam("m3_licence")
private String m3_lincence = null; private String m3_lincence = null;
@Schema(description = "Le rôle du troisième membre de l'association.", example = "MEMBREBUREAU", required = true) @Schema(description = "Le rôle du troisième membre de l'association.", examples = "MEMBREBUREAU", required = true)
@FormParam("m3_role") @FormParam("m3_role")
private RoleAsso m3_role = null; private RoleAsso m3_role = null;

View File

@@ -6,123 +6,124 @@ import jakarta.ws.rs.core.MediaType;
import lombok.Getter; import lombok.Getter;
import org.eclipse.microprofile.openapi.annotations.media.Schema; import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.jboss.resteasy.reactive.PartType; import org.jboss.resteasy.reactive.PartType;
import org.jboss.resteasy.reactive.multipart.FileUpload;
@Getter @Getter
public class AffiliationRequestSaveForm { public class AffiliationRequestSaveForm {
@Schema(description = "L'identifiant de l'affiliation.", example = "1", required = true) @Schema(description = "L'identifiant de l'affiliation.", examples = "1", required = true)
@FormParam("id") @FormParam("id")
private Long id = null; private Long id = null;
@Schema(description = "Le nom de l'association.", example = "Association sportive", required = true) @Schema(description = "Le nom de l'association.", examples = "Association sportive", required = true)
@FormParam("name") @FormParam("name")
private String name = null; private String name = null;
@Schema(description = "Le numéro SIRET ou RNA de l'association.", example = "12345678901234", required = true) @Schema(description = "Le numéro SIRET ou RNA de l'association.", examples = "12345678901234", required = true)
@FormParam("state_id") @FormParam("state_id")
private String state_id = null; private String state_id = null;
@Schema(description = "L'adresse de l'association.", example = "1 rue de l'exemple, 75000 Paris", required = true) @Schema(description = "L'adresse de l'association.", examples = "1 rue de l'exemple, 75000 Paris", required = true)
@FormParam("address") @FormParam("address")
private String address = null; private String address = null;
@Schema(description = "Email de contact de l'association", example = "test@test.fr") @Schema(description = "Email de contact de l'association", examples = "test@test.fr")
@FormParam("contact") @FormParam("contact")
private String contact = null; private String contact = null;
@Schema(description = "Le statut de l'association.") @Schema(description = "Le statut de l'association.")
@FormParam("status") @FormParam("status")
@PartType(MediaType.APPLICATION_OCTET_STREAM) @PartType(MediaType.APPLICATION_OCTET_STREAM)
private byte[] status = new byte[0]; private FileUpload status = null;
@Schema(description = "Le logo de l'association.") @Schema(description = "Le logo de l'association.")
@FormParam("logo") @FormParam("logo")
@PartType(MediaType.APPLICATION_OCTET_STREAM) @PartType(MediaType.APPLICATION_OCTET_STREAM)
private byte[] logo = new byte[0]; private FileUpload logo = null;
@Schema(description = "Mode utiliser pour la sauvegarde du membre 1 (0 = licence mode, 2 = nom, prénom)", example = "0", required = true) @Schema(description = "Mode utiliser pour la sauvegarde du membre 1 (0 = licence mode, 2 = nom, prénom)", examples = "0", required = true)
@FormParam("m1_mode") @FormParam("m1_mode")
private Integer m1_mode = null; private Integer m1_mode = null;
@Schema(description = "Le rôle du premier membre de l'association.", example = "PRÉSIDENT", required = true) @Schema(description = "Le rôle du premier membre de l'association.", examples = "PRÉSIDENT", required = true)
@FormParam("m1_role") @FormParam("m1_role")
private RoleAsso m1_role = null; private RoleAsso m1_role = null;
@Schema(description = "Le numéro de licence du premier membre de l'association. (null si non licencié)", example = "1234567", required = true) @Schema(description = "Le numéro de licence du premier membre de l'association. (null si non licencié)", examples = "1234567", required = true)
@FormParam("m1_licence") @FormParam("m1_licence")
private String m1_lincence = null; private String m1_lincence = null;
@Schema(description = "Le nom du premier membre de l'association.", example = "Dupont", required = true) @Schema(description = "Le nom du premier membre de l'association.", examples = "Dupont", required = true)
@FormParam("m1_lname") @FormParam("m1_lname")
private String m1_lname = null; private String m1_lname = null;
@Schema(description = "Le prénom du premier membre de l'association.", example = "Jean", required = true) @Schema(description = "Le prénom du premier membre de l'association.", examples = "Jean", required = true)
@FormParam("m1_fname") @FormParam("m1_fname")
private String m1_fname = null; private String m1_fname = null;
@Schema(description = "L'adresse e-mail du premier membre de l'association.", example = "jean.dupont@example.com", required = true) @Schema(description = "L'adresse e-mail du premier membre de l'association.", examples = "jean.dupont@examples.com", required = true)
@FormParam("m1_email") @FormParam("m1_email")
private String m1_email = null; private String m1_email = null;
@Schema(name = "keep_email", @Schema(name = "keep_email",
description = "Conserver l'email de la base de donner (1 = conserve, 0 = replacer par 'm1_email')", example = "1", required = true) description = "Conserver l'email de la base de donner (1 = conserve, 0 = replacer par 'm1_email')", examples = "1", required = true)
@FormParam("m1_email_mode") @FormParam("m1_email_mode")
private Integer m1_email_mode = null; private Integer m1_email_mode = null;
@Schema(description = "Mode utiliser pour la sauvegarde du membre 2 (0 = licence mode, 2 = nom, prénom)", example = "0", required = true) @Schema(description = "Mode utiliser pour la sauvegarde du membre 2 (0 = licence mode, 2 = nom, prénom)", examples = "0", required = true)
@FormParam("m2_mode") @FormParam("m2_mode")
private Integer m2_mode = null; private Integer m2_mode = null;
@Schema(description = "Le rôle du deuxième membre de l'association.", example = "TRÉSORIER", required = true) @Schema(description = "Le rôle du deuxième membre de l'association.", examples = "TRÉSORIER", required = true)
@FormParam("m2_role") @FormParam("m2_role")
private RoleAsso m2_role = null; private RoleAsso m2_role = null;
@Schema(description = "Le numéro de licence du deuxième membre de l'association. (null si non licencié)", example = "2345678", required = true) @Schema(description = "Le numéro de licence du deuxième membre de l'association. (null si non licencié)", examples = "2345678", required = true)
@FormParam("m2_licence") @FormParam("m2_licence")
private String m2_lincence = null; private String m2_lincence = null;
@Schema(description = "Le nom du deuxième membre de l'association.", example = "Durand", required = true) @Schema(description = "Le nom du deuxième membre de l'association.", examples = "Durand", required = true)
@FormParam("m2_lname") @FormParam("m2_lname")
private String m2_lname = null; private String m2_lname = null;
@Schema(description = "Le prénom du deuxième membre de l'association.", example = "Paul", required = true) @Schema(description = "Le prénom du deuxième membre de l'association.", examples = "Paul", required = true)
@FormParam("m2_fname") @FormParam("m2_fname")
private String m2_fname = null; private String m2_fname = null;
@Schema(description = "L'adresse e-mail du deuxième membre de l'association.", example = "paul.durand@example.com", required = true) @Schema(description = "L'adresse e-mail du deuxième membre de l'association.", examples = "paul.durand@examples.com", required = true)
@FormParam("m2_email") @FormParam("m2_email")
private String m2_email = null; private String m2_email = null;
@Schema(name = "keep_email", @Schema(name = "keep_email",
description = "Conserver l'email de la base de donner (1 = conserve, 0 = replacer par 'm2_email')", example = "1", required = true) description = "Conserver l'email de la base de donner (1 = conserve, 0 = replacer par 'm2_email')", examples = "1", required = true)
@FormParam("m2_email_mode") @FormParam("m2_email_mode")
private Integer m2_email_mode = null; private Integer m2_email_mode = null;
@Schema(description = "Mode utiliser pour la sauvegarde du membre 3 (0 = licence mode, 2 = nom, prénom)", example = "0", required = true) @Schema(description = "Mode utiliser pour la sauvegarde du membre 3 (0 = licence mode, 2 = nom, prénom)", examples = "0", required = true)
@FormParam("m3_mode") @FormParam("m3_mode")
private Integer m3_mode = null; private Integer m3_mode = null;
@Schema(description = "Le rôle du troisième membre de l'association.", example = "SECRÉTAIRE", required = true) @Schema(description = "Le rôle du troisième membre de l'association.", examples = "SECRÉTAIRE", required = true)
@FormParam("m3_role") @FormParam("m3_role")
private RoleAsso m3_role = null; private RoleAsso m3_role = null;
@Schema(description = "Le numéro de licence du troisième membre de l'association. (null si non licencié)", example = "3456789", required = true) @Schema(description = "Le numéro de licence du troisième membre de l'association. (null si non licencié)", examples = "3456789", required = true)
@FormParam("m3_licence") @FormParam("m3_licence")
private String m3_lincence = null; private String m3_lincence = null;
@Schema(description = "Le nom du troisième membre de l'association.", example = "Martin", required = true) @Schema(description = "Le nom du troisième membre de l'association.", examples = "Martin", required = true)
@FormParam("m3_lname") @FormParam("m3_lname")
private String m3_lname = null; private String m3_lname = null;
@Schema(description = "Le prénom du troisième membre de l'association.", example = "Pierre", required = true) @Schema(description = "Le prénom du troisième membre de l'association.", examples = "Pierre", required = true)
@FormParam("m3_fname") @FormParam("m3_fname")
private String m3_fname = null; private String m3_fname = null;
@Schema(description = "L'adresse e-mail du troisième membre de l'association.", example = "pierre.martin@example.com", required = true) @Schema(description = "L'adresse e-mail du troisième membre de l'association.", examples = "pierre.martin@examples.com", required = true)
@FormParam("m3_email") @FormParam("m3_email")
private String m3_email = null; private String m3_email = null;
@Schema(name = "keep_email", @Schema(name = "keep_email",
description = "Conserver l'email de la base de donner (1 = conserve, 0 = replacer par 'm3_email')", example = "1", required = true) description = "Conserver l'email de la base de donner (1 = conserve, 0 = replacer par 'm3_email')", examples = "1", required = true)
@FormParam("m3_email_mode") @FormParam("m3_email_mode")
private Integer m3_email_mode = null; private Integer m3_email_mode = null;
@@ -174,8 +175,6 @@ public class AffiliationRequestSaveForm {
", state_id=" + state_id + ", state_id=" + state_id +
", address='" + address + '\'' + ", address='" + address + '\'' +
", contact='" + contact + '\'' + ", contact='" + contact + '\'' +
", status_len=" + status.length +
", logo_len=" + logo.length +
", m1_mode=" + m1_mode + ", m1_mode=" + m1_mode +
", m1_role=" + m1_role + ", m1_role=" + m1_role +
", m1_lincence='" + m1_lincence + '\'' + ", m1_lincence='" + m1_lincence + '\'' +

View File

@@ -7,57 +7,58 @@ import lombok.ToString;
import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
import org.eclipse.microprofile.openapi.annotations.media.Schema; import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.jboss.resteasy.reactive.PartType; import org.jboss.resteasy.reactive.PartType;
import org.jboss.resteasy.reactive.multipart.FileUpload;
@ToString @ToString
@Getter @Getter
public class FullClubForm { public class FullClubForm {
@FormParam("id") @FormParam("id")
@Schema(description = "Identifiant du club", example = "1", required = true) @Schema(description = "Identifiant du club", examples = "1", required = true)
private String id = null; private String id = null;
@FormParam("name") @FormParam("name")
@Schema(description = "Nom du club", example = "Association sportive", required = true) @Schema(description = "Nom du club", examples = "Association sportive", required = true)
private String name = null; private String name = null;
@FormParam("country") @FormParam("country")
@Schema(description = "Pays du club", example = "FR", required = true) @Schema(description = "Pays du club", examples = "FR", required = true)
private String country = null; private String country = null;
@FormParam("contact") @FormParam("contact")
@Schema(description = "Les contacts du club", example = "{\"SITE\": \"www.test.com\", \"COURRIEL\": \"test@test.com\"}", required = true) @Schema(description = "Les contacts du club", examples = "{\"SITE\": \"www.test.com\", \"COURRIEL\": \"test@test.com\"}", required = true)
private String contact = null; private String contact = null;
@FormParam("training_location") @FormParam("training_location")
@Schema(description = "Liste des lieux d'entraînement", example = "[{\"text\":\"addr 1\",\"lng\":2.24654,\"lat\":52.4868658},{\"text\":\"addr 2\",\"lng\":2.88654,\"lat\":52.7865456}]", required = true) @Schema(description = "Liste des lieux d'entraînement", examples = "[{\"text\":\"addr 1\",\"lng\":2.24654,\"lat\":52.4868658},{\"text\":\"addr 2\",\"lng\":2.88654,\"lat\":52.7865456}]", required = true)
private String training_location = null; private String training_location = null;
@FormParam("training_day_time") @FormParam("training_day_time")
@Schema(description = "Liste des jours et horaires d'entraînement (jours 0-6, 0=>lundi) (temps en minute depuis 00:00, 122=>2h02)", example = "[{\"day\":0,\"time_start\":164,\"time_end\":240},{\"day\":3,\"time_start\":124,\"time_end\":250}]", required = true) @Schema(description = "Liste des jours et horaires d'entraînement (jours 0-6, 0=>lundi) (temps en minute depuis 00:00, 122=>2h02)", examples = "[{\"day\":0,\"time_start\":164,\"time_end\":240},{\"day\":3,\"time_start\":124,\"time_end\":250}]", required = true)
private String training_day_time = null; private String training_day_time = null;
@FormParam("contact_intern") @FormParam("contact_intern")
@Schema(description = "Contact interne du club", example = "john.doe@test.com") @Schema(description = "Contact interne du club", examples = "john.doe@test.com")
private String contact_intern = null; private String contact_intern = null;
@FormParam("address") @FormParam("address")
@Schema(description = "Adresse postale du club", example = "1 rue de l'exemple, 75000 Paris", required = true) @Schema(description = "Adresse postale du club", examples = "1 rue de l'exemple, 75000 Paris", required = true)
private String address = null; private String address = null;
@FormParam("state_id") @FormParam("state_id")
@Schema(description = "Numéro SIRET ou RNA du club", example = "12345678901234", required = true) @Schema(description = "Numéro SIRET ou RNA du club", examples = "12345678901234", required = true)
private String state_id = null; private String state_id = null;
@FormParam("international") @FormParam("international")
@Schema(description = "Club international", example = "false", required = true) @Schema(description = "Club international", examples = "false", required = true)
private boolean international = false; private boolean international = false;
@FormParam("status") @FormParam("status")
@PartType(MediaType.APPLICATION_OCTET_STREAM) @PartType(MediaType.APPLICATION_OCTET_STREAM)
@Schema(description = "Le statut de l'association.", type = SchemaType.ARRAY, implementation = byte.class) @Schema(description = "Le statut de l'association.", type = SchemaType.ARRAY)
private byte[] status = new byte[0]; private FileUpload status = null;
@FormParam("logo") @FormParam("logo")
@PartType(MediaType.APPLICATION_OCTET_STREAM) @PartType(MediaType.APPLICATION_OCTET_STREAM)
@Schema(description = "Le logo de l'association.", type = SchemaType.ARRAY, implementation = byte.class) @Schema(description = "Le logo de l'association.", type = SchemaType.ARRAY)
private byte[] logo = new byte[0]; private FileUpload logo = null;
} }

View File

@@ -8,32 +8,33 @@ import jakarta.ws.rs.core.MediaType;
import lombok.Getter; import lombok.Getter;
import org.eclipse.microprofile.openapi.annotations.media.Schema; import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.jboss.resteasy.reactive.PartType; import org.jboss.resteasy.reactive.PartType;
import org.jboss.resteasy.reactive.multipart.FileUpload;
import java.util.Date; import java.util.Date;
@Getter @Getter
public class FullMemberForm { public class FullMemberForm {
@Schema(description = "L'identifiant du membre.", example = "1") @Schema(description = "L'identifiant du membre.", examples = "1")
@FormParam("id") @FormParam("id")
private String id = null; private String id = null;
@Schema(description = "Le nom du membre.", example = "Dupont") @Schema(description = "Le nom du membre.", examples = "Dupont")
@FormParam("lname") @FormParam("lname")
private String lname = null; private String lname = null;
@Schema(description = "Le prénom du membre.", example = "Jean") @Schema(description = "Le prénom du membre.", examples = "Jean")
@FormParam("fname") @FormParam("fname")
private String fname = null; private String fname = null;
@Schema(description = "L'identifiant du club du membre.", example = "1") @Schema(description = "L'identifiant du club du membre.", examples = "1")
@FormParam("club") @FormParam("club")
private Long club = null; private Long club = null;
@Schema(description = "Le genre du membre.", example = "H") @Schema(description = "Le genre du membre.", examples = "H")
@FormParam("genre") @FormParam("genre")
private Genre genre; private Genre genre;
@Schema(description = "Le pays du membre.", example = "FR") @Schema(description = "Le pays du membre.", examples = "FR")
@FormParam("country") @FormParam("country")
private String country; private String country;
@@ -41,22 +42,22 @@ public class FullMemberForm {
@FormParam("birth_date") @FormParam("birth_date")
private Date birth_date = null; private Date birth_date = null;
@Schema(description = "L'adresse e-mail du membre.", example = "jean.dupont@example.com") @Schema(description = "L'adresse e-mail du membre.", examples = "jean.dupont@example.com")
@FormParam("email") @FormParam("email")
private String email; private String email;
@Schema(description = "Le rôle du membre dans l'association.", example = "MEMBRE") @Schema(description = "Le rôle du membre dans l'association.", examples = "MEMBRE")
@FormParam("role") @FormParam("role")
private RoleAsso role; private RoleAsso role;
@Schema(description = "Le grade d'arbitrage du membre.", example = "ASSESSEUR") @Schema(description = "Le grade d'arbitrage du membre.", examples = "ASSESSEUR")
@FormParam("grade_arbitrage") @FormParam("grade_arbitrage")
private GradeArbitrage grade_arbitrage = GradeArbitrage.NA; private GradeArbitrage grade_arbitrage = GradeArbitrage.NA;
@Schema(description = "La photo du membre.") @Schema(description = "La photo du membre.")
@FormParam("photo_data") @FormParam("photo_data")
@PartType(MediaType.APPLICATION_OCTET_STREAM) @PartType(MediaType.APPLICATION_OCTET_STREAM)
private byte[] photo_data = new byte[0]; private FileUpload photo_data = null;
@Override @Override
public String toString() { public String toString() {
@@ -70,7 +71,6 @@ public class FullMemberForm {
", email='" + email + '\'' + ", email='" + email + '\'' +
", role=" + role + ", role=" + role +
", grade_arbitrage=" + grade_arbitrage + ", grade_arbitrage=" + grade_arbitrage +
", url_photo=" + photo_data.length +
'}'; '}';
} }
} }

View File

@@ -0,0 +1,15 @@
package fr.titionfire.ffsaf.rest.from;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
import jakarta.ws.rs.FormParam;
import lombok.Getter;
import lombok.ToString;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
@ToString
@Getter
public class UserSettingForm {
@FormParam("resultPrivacy")
@Schema(description = "La confidentialité des résultats", examples = "PUBLIC", defaultValue = "PUBLIC", required = true)
private ResultPrivacy resultPrivacy;
}

View File

@@ -1,5 +1,6 @@
package fr.titionfire.ffsaf.utils; package fr.titionfire.ffsaf.utils;
import fr.titionfire.ffsaf.domain.service.TradService;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@@ -19,7 +20,7 @@ public enum Categorie {
VETERAN2; VETERAN2;
public String getName(ResourceBundle BUNDLE) { public String getName(ResourceBundle BUNDLE) {
return switch (this){ return switch (this) {
case SUPER_MINI -> BUNDLE.getString("Cat.SUPER_MINI"); case SUPER_MINI -> BUNDLE.getString("Cat.SUPER_MINI");
case MINI_POUSSIN -> BUNDLE.getString("Cat.MINI_POUSSIN"); case MINI_POUSSIN -> BUNDLE.getString("Cat.MINI_POUSSIN");
case POUSSIN -> BUNDLE.getString("Cat.POUSSIN"); case POUSSIN -> BUNDLE.getString("Cat.POUSSIN");
@@ -34,8 +35,24 @@ public enum Categorie {
}; };
} }
public String getName(TradService tradService) {
return switch (this) {
case SUPER_MINI -> tradService.translate("Cat.SUPER_MINI");
case MINI_POUSSIN -> tradService.translate("Cat.MINI_POUSSIN");
case POUSSIN -> tradService.translate("Cat.POUSSIN");
case BENJAMIN -> tradService.translate("Cat.BENJAMIN");
case MINIME -> tradService.translate("Cat.MINIME");
case CADET -> tradService.translate("Cat.CADET");
case JUNIOR -> tradService.translate("Cat.JUNIOR");
case SENIOR1 -> tradService.translate("Cat.SENIOR1");
case SENIOR2 -> tradService.translate("Cat.SENIOR2");
case VETERAN1 -> tradService.translate("Cat.VETERAN1");
case VETERAN2 -> tradService.translate("Cat.VETERAN2");
};
}
public String getName() { public String getName() {
return switch (this){ return switch (this) {
case SUPER_MINI -> "Super Mini"; case SUPER_MINI -> "Super Mini";
case MINI_POUSSIN -> "Mini Poussin"; case MINI_POUSSIN -> "Mini Poussin";
case POUSSIN -> "Poussin"; case POUSSIN -> "Poussin";

View File

@@ -1,32 +1,34 @@
package fr.titionfire.ffsaf.utils; package fr.titionfire.ffsaf.utils;
import fr.titionfire.ffsaf.domain.service.TradService;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import java.util.HashMap; import java.util.HashMap;
@RegisterForReflection @RegisterForReflection
public enum Contact { public enum Contact {
COURRIEL("Courriel"), COURRIEL,
TELEPHONE("Téléphone"), TELEPHONE,
SITE("Site web"), SITE,
FACEBOOK("Facebook"), FACEBOOK,
INSTAGRAM("Instagram"), INSTAGRAM,
AUTRE("Autre"); AUTRE;
public String name; public String getName(TradService trad) {
return switch (this) {
Contact(String name) { case COURRIEL -> trad.translate("Contact.COURRIEL");
this.name = name; case TELEPHONE -> trad.translate("Contact.TELEPHONE");
case SITE -> trad.translate("Contact.SITE");
case FACEBOOK -> trad.translate("Contact.FACEBOOK");
case INSTAGRAM -> trad.translate("Contact.INSTAGRAM");
case AUTRE -> trad.translate("Contact.AUTRE");
};
} }
public void setName(String name) { public static HashMap<String, String> toSite(TradService trad) {
this.name = name;
}
public static HashMap<String, String> toSite() {
HashMap<String, String> map = new HashMap<>(); HashMap<String, String> map = new HashMap<>();
for (Contact contact : Contact.values()) { for (Contact contact : Contact.values()) {
map.put(contact.toString(), contact.name); map.put(contact.toString(), contact.getName(trad));
} }
return map; return map;
} }

View File

@@ -1,5 +1,6 @@
package fr.titionfire.ffsaf.utils; package fr.titionfire.ffsaf.utils;
import fr.titionfire.ffsaf.domain.service.TradService;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
@RegisterForReflection @RegisterForReflection
@@ -27,8 +28,17 @@ public enum Genre {
} }
} }
public String getString(TradService trad) {
return switch (this) {
case H -> trad.translate("Genre.Homme");
case F -> trad.translate("Genre.Femme");
case NA -> trad.translate("Genre.NA");
};
}
@Override @Override
public String toString() { public String toString() {
return str; return str;
} }
} }

View File

@@ -1,5 +1,6 @@
package fr.titionfire.ffsaf.utils; package fr.titionfire.ffsaf.utils;
import fr.titionfire.ffsaf.domain.service.TradService;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
@RegisterForReflection @RegisterForReflection
@@ -14,6 +15,14 @@ public enum GradeArbitrage {
this.str = name; this.str = name;
} }
public String getString(TradService trad) {
return switch (this) {
case NA -> trad.translate("GradeArbitrage.NA");
case ASSESSEUR -> trad.translate("GradeArbitrage.ASSESSEUR");
case ARBITRE -> trad.translate("GradeArbitrage.ARBITRE");
};
}
@Override @Override
public String toString() { public String toString() {
return str; return str;

View File

@@ -10,13 +10,14 @@ import java.util.List;
@Data @Data
@RegisterForReflection @RegisterForReflection
public class PageResult<T> { public class PageResult<T> {
@Schema(description = "Le numéro de la page courante.", example = "1") @Schema(description = "Le numéro de la page courante.", examples = "1")
private int page; private int page;
@Schema(description = "Le nombre d'éléments par page.", example = "10") @Schema(description = "Le nombre d'éléments par page.", examples = "10")
private int page_size; private int page_size;
@Schema(description = "Le nombre total de pages.", example = "5") @Schema(description = "Le nombre total de pages.", examples = "5")
private int page_count; private int page_count;
@Schema(description = "Le nombre total d'éléments.", example = "47") @Schema(description = "Le nombre total d'éléments.", examples = "47")
private long result_count; private long result_count;
private List<T> result = new ArrayList<>(); private List<T> result = new ArrayList<>();
private Object additionalData;
} }

View File

@@ -0,0 +1,8 @@
package fr.titionfire.ffsaf.utils;
public enum ResultPrivacy {
PUBLIC,
REGISTERED_ONLY,
REGISTERED_ONLY_NO_DETAILS,
PRIVATE;
}

View File

@@ -1,5 +1,6 @@
package fr.titionfire.ffsaf.utils; package fr.titionfire.ffsaf.utils;
import fr.titionfire.ffsaf.domain.service.TradService;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
@RegisterForReflection @RegisterForReflection
@@ -21,6 +22,19 @@ public enum RoleAsso {
this.level = level; this.level = level;
} }
public String getString(TradService trad) {
return switch (this){
case MEMBRE -> trad.translate("RoleAsso.MEMBRE");
case PRESIDENT -> trad.translate("RoleAsso.PRESIDENT");
case VPRESIDENT -> trad.translate("RoleAsso.VPRESIDENT");
case SECRETAIRE -> trad.translate("RoleAsso.SECRETAIRE");
case VSECRETAIRE -> trad.translate("RoleAsso.VSECRETAIRE");
case TRESORIER -> trad.translate("RoleAsso.TRESORIER");
case VTRESORIER -> trad.translate("RoleAsso.VTRESORIER");
case MEMBREBUREAU -> trad.translate("RoleAsso.MEMBREBUREAU");
};
}
@Override @Override
public String toString() { public String toString() {
return str; return str;

View File

@@ -1,17 +1,27 @@
package fr.titionfire.ffsaf.utils; package fr.titionfire.ffsaf.utils;
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.domain.service.VirusScannerService;
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
import fr.titionfire.ffsaf.rest.exception.DInternalError;
import fr.titionfire.ffsaf.rest.exception.DetailException;
import io.quarkiverse.antivirus.runtime.AntivirusScanResult;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked;
import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import jodd.net.MimeTypes; import jodd.net.MimeTypes;
import org.apache.tika.Tika;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.multipart.FileUpload;
import java.io.*; import java.io.*;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URLConnection; import java.net.URLConnection;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@@ -21,6 +31,79 @@ 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());
} }
@@ -36,6 +119,15 @@ public class Utils {
} }
} }
public static Calendar getFirstDateOfSaison() {
Calendar calendar = Calendar.getInstance();
calendar.set(getSaison(), Calendar.SEPTEMBER, 1, 0, 0, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar;
}
public static Calendar toCalendar(Date date) { public static Calendar toCalendar(Date date) {
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
cal.setTime(date); cal.setTime(date);
@@ -108,53 +200,87 @@ public class Utils {
}); });
} }
public static Future<String> replacePhoto(long id, byte[] input, String media, String dir) { public static Uni<String> uploadFile(VirusScannerService ss, FileUpload file, long id, String media, String dir) {
return CompletableFuture.supplyAsync(() -> { if (file == null || file.size() == 0)
if (input == null || input.length == 0) return Uni.createFrom().item("Ok");
return "OK";
try (InputStream is = new BufferedInputStream(new ByteArrayInputStream(input))) { LOGGER.infof("Received file upload request for: %s (size: %d bytes)", file.fileName(), file.size());
String mimeType; return Uni.createFrom().<InputStream>item(() -> {
try { try {
Tika tika = new Tika(); // Read the entire file into memory for virus scanning
mimeType = tika.detect(is);// Magic.getMagicMatch(input, false).getMimeType(); // ByteArrayInputStream fully supports mark/reset operations required by ClamAV
} catch (IOException e) { // This ensures we can scan the file content before any filesystem storage
mimeType = URLConnection.guessContentTypeFromStream(is); InputStream fileStream = java.nio.file.Files.newInputStream(file.uploadedFile());
} byte[] fileBytes = fileStream.readAllBytes();
String[] detectedExtensions = MimeTypes.findExtensionsByMimeTypes(mimeType, false); fileStream.close(); // Close the file stream immediately
if (detectedExtensions.length == 0) return new ByteArrayInputStream(fileBytes);
throw new IOException("Fail to detect file extension for MIME type " + mimeType); } catch (IOException e) {
throw new RuntimeException("Failed to read uploaded file: " + e.getMessage(), e);
File dirFile = new File(media, dir); }
if (!dirFile.exists()) }).runSubscriptionOn(io.smallrye.mutiny.infrastructure.Infrastructure.getDefaultWorkerPool()).onItem()
if (!dirFile.mkdirs()) .transformToUni(inputStream -> {
throw new IOException("Fail to create directory " + dir); // Perform virus scanning reactively using the input stream
return ss.scanFileReactive(file.fileName(), inputStream).onItem().invoke(() -> {
FilenameFilter filter = (directory, filename) -> filename.startsWith(id + "."); try {
File[] files = dirFile.listFiles(filter); inputStream.close();
if (files != null) { } catch (IOException e) {
for (File file : files) { LOGGER.warn("Warning: Failed to close input stream: " + e.getMessage());
//noinspection ResultOfMethodCallIgnored }
file.delete(); });
})
.onItem().transformToUni(scanResults -> Uni.createFrom().item(Unchecked.supplier(() -> {
// Check if any scanner found a threat
for (AntivirusScanResult result : scanResults) {
if (result.getStatus() != Response.Status.OK.getStatusCode()) {
LOGGER.warnf("THREAT DETECTED in %s: %s", file.fileName(), result.getMessage());
throw new DetailException(Response.Status.fromStatusCode(result.getStatus()),
"THREAT_DETECTED on File " + file.fileName() + " is infected: " + result.getMessage());
}
} }
}
String extension = "." + detectedExtensions[0]; // File is clean - now we can safely process it
Files.write(new File(dirFile, id + extension).toPath(), input); LOGGER.infof("File is clean: %s (size=%d) (contentType=%s)", file.fileName(), file.size(),
return "OK"; file.contentType());
} catch (IOException e) {
return e.getMessage(); String[] detectedExtensions = MimeTypes.findExtensionsByMimeTypes(file.contentType(), false);
} if (detectedExtensions.length == 0)
}); throw new DBadRequestException(
"Fail to detect file extension for MIME type " + file.contentType());
File dirFile = new File(media, dir);
if (!dirFile.exists())
if (!dirFile.mkdirs())
throw new DInternalError("Fail to create directory " + dir);
FilenameFilter filter = (directory, filename) -> filename.startsWith(id + ".");
File[] files = dirFile.listFiles(filter);
if (files != null) {
for (File f2 : files) {
//noinspection ResultOfMethodCallIgnored
f2.delete();
}
}
Path f = file.filePath();
System.out.println(f + " -> " + new File(dirFile, id + "." + detectedExtensions[0]));
Files.copy(f, new File(dirFile, id + "." + detectedExtensions[0]).toPath(),
StandardCopyOption.REPLACE_EXISTING);
return "ok";
})));
} }
public static Uni<Response> getMediaFile(long id, String media, String dirname, public static Uni<Response> getMediaFile(long id, String media, String dirname,
Uni<?> uniBase) throws URISyntaxException { Uni<?> uniBase) throws URISyntaxException {
return getMediaFile(id, media, dirname, null, uniBase); return getMediaFile(id, media, dirname, null, uniBase, true);
}
public static Uni<Response> getMediaFileNoDefault(long id, String media, String dirname,
Uni<?> uniBase) throws URISyntaxException {
return getMediaFile(id, media, dirname, null, uniBase, false);
} }
public static Uni<Response> getMediaFile(long id, String media, String dirname, String out_filename, public static Uni<Response> getMediaFile(long id, String media, String dirname, String out_filename,
Uni<?> uniBase) throws URISyntaxException { Uni<?> uniBase, boolean default_) throws URISyntaxException {
Future<Pair<File, byte[]>> future = CompletableFuture.supplyAsync(() -> { Future<Pair<File, byte[]>> future = CompletableFuture.supplyAsync(() -> {
FilenameFilter filter = (directory, filename) -> filename.startsWith(id + "."); FilenameFilter filter = (directory, filename) -> filename.startsWith(id + ".");
File[] files = new File(media, dirname).listFiles(filter); File[] files = new File(media, dirname).listFiles(filter);
@@ -182,19 +308,25 @@ public class Utils {
return uniBase.chain(__ -> Uni.createFrom().future(future) return uniBase.chain(__ -> Uni.createFrom().future(future)
.chain(filePair -> { .chain(filePair -> {
if (filePair == null) { if (filePair == null) {
return Uni.createFrom().future(future2).map(data -> { if (default_) {
if (data == null) return Uni.createFrom().future(future2).map(data -> {
return Response.noContent().build(); if (data == null)
return Response.noContent().build();
String mimeType = "image/apng"; String mimeType = "image/apng";
Response.ResponseBuilder resp = Response.ok(data); Response.ResponseBuilder resp = Response.ok(data);
resp.type(MediaType.APPLICATION_OCTET_STREAM); resp.type(MediaType.APPLICATION_OCTET_STREAM);
resp.header(HttpHeaders.CONTENT_LENGTH, data.length); resp.header(HttpHeaders.CONTENT_LENGTH, data.length);
resp.header(HttpHeaders.CONTENT_TYPE, mimeType); resp.header(HttpHeaders.CONTENT_TYPE, mimeType);
resp.header(HttpHeaders.CONTENT_DISPOSITION, resp.header(HttpHeaders.CONTENT_DISPOSITION,
"inline; " + ((out_filename == null) ? "" : "filename=\"" + out_filename + "\"")); "inline; " + ((out_filename == null) ? "" : "filename=\"" + out_filename + "\""));
return resp.build(); return resp.build();
}); });
} else {
Response.ResponseBuilder resp = Response.status(404);
resp.header(HttpHeaders.CACHE_CONTROL, "max-age=600");
return Uni.createFrom().item(resp.build());
}
} else { } else {
return Uni.createFrom().item(() -> { return Uni.createFrom().item(() -> {
String mimeType = URLConnection.guessContentTypeFromName(filePair.getKey().getName()); String mimeType = URLConnection.guessContentTypeFromName(filePair.getKey().getName());
@@ -251,4 +383,46 @@ public class Utils {
return (int) ((calendar.getTimeInMillis() - now.getTimeInMillis()) / (1000 * 60 * 60 * 24)); return (int) ((calendar.getTimeInMillis() - now.getTimeInMillis()) / (1000 * 60 * 60 * 24));
} }
public static String formatPrenom(String input) {
if (input == null || input.isEmpty()) {
return input;
}
StringBuilder result = new StringBuilder();
String[] mots = input.split(" ");
for (String mot : mots) {
if (!mot.isEmpty()) {
String[] parties = mot.split("[-']");
StringBuilder motFormate = new StringBuilder();
for (int i = 0; i < parties.length; i++) {
if (!parties[i].isEmpty()) {
String premiereLettre = parties[i].substring(0, 1).toUpperCase();
String reste = parties[i].substring(1).toLowerCase();
motFormate.append(premiereLettre).append(reste);
}
if (i < parties.length - 1) {
motFormate.append(mot.charAt(mot.indexOf(parties[i]) + parties[i].length()));
}
}
result.append(motFormate).append(" ");
}
}
return result.toString().trim();
}
public static String getFullName(Object... models) {
for (Object model : models) {
if (model == null)
continue;
if (model instanceof MembreModel membreModel)
return membreModel.getFname() + " " + membreModel.getLname();
if (model instanceof CompetitionGuestModel guestModel)
return guestModel.getFname() + " " + guestModel.getLname();
}
return "";
}
} }

View File

@@ -4,12 +4,10 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import fr.titionfire.ffsaf.data.repository.CompetitionRepository; import fr.titionfire.ffsaf.data.repository.CompetitionRepository;
import fr.titionfire.ffsaf.domain.service.CompetPermService; import fr.titionfire.ffsaf.domain.service.CompetPermService;
import fr.titionfire.ffsaf.net2.MessageType; import fr.titionfire.ffsaf.net2.MessageType;
import fr.titionfire.ffsaf.rest.data.SimpleCompetData;
import fr.titionfire.ffsaf.utils.SecurityCtx; import fr.titionfire.ffsaf.utils.SecurityCtx;
import fr.titionfire.ffsaf.ws.data.WelcomeInfo; import fr.titionfire.ffsaf.ws.data.WelcomeInfo;
import fr.titionfire.ffsaf.ws.recv.RCategorie; import fr.titionfire.ffsaf.ws.recv.*;
import fr.titionfire.ffsaf.ws.recv.RMatch;
import fr.titionfire.ffsaf.ws.recv.RRegister;
import fr.titionfire.ffsaf.ws.recv.WSReceiver;
import fr.titionfire.ffsaf.ws.send.JsonUni; import fr.titionfire.ffsaf.ws.send.JsonUni;
import io.quarkus.hibernate.reactive.panache.common.WithSession; import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.security.Authenticated; import io.quarkus.security.Authenticated;
@@ -19,12 +17,15 @@ import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked; import io.smallrye.mutiny.unchecked.Unchecked;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.ws.rs.ForbiddenException; import jakarta.ws.rs.ForbiddenException;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.time.Duration;
import java.util.*; import java.util.*;
import java.util.concurrent.Executor;
import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER; import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER;
@@ -42,7 +43,19 @@ public class CompetitionWS {
RCategorie rCategorie; RCategorie rCategorie;
@Inject @Inject
RRegister rRegister; RRegister rRegister;
@Inject
RCard rCard;
@Inject
RTeam rTeam;
@Inject
RState rState;
@Inject
RPDF rpdf;
@Inject @Inject
SecurityCtx securityCtx; SecurityCtx securityCtx;
@@ -50,6 +63,15 @@ public class CompetitionWS {
@Inject @Inject
CompetPermService competPermService; CompetPermService competPermService;
@SuppressWarnings("CdiInjectionPointsInspection")
@Inject
OpenConnections connections;
@Inject
@Named("notify-executor")
Executor notifyExecutor;
private static Executor executor;
@Inject @Inject
CompetitionRepository competitionRepository; CompetitionRepository competitionRepository;
@@ -77,6 +99,12 @@ public class CompetitionWS {
getWSReceiverMethods(RMatch.class, rMatch); getWSReceiverMethods(RMatch.class, rMatch);
getWSReceiverMethods(RCategorie.class, rCategorie); getWSReceiverMethods(RCategorie.class, rCategorie);
getWSReceiverMethods(RRegister.class, rRegister); getWSReceiverMethods(RRegister.class, rRegister);
getWSReceiverMethods(RCard.class, rCard);
getWSReceiverMethods(RTeam.class, rTeam);
getWSReceiverMethods(RState.class, rState);
getWSReceiverMethods(RPDF.class, rpdf);
executor = notifyExecutor;
} }
@OnOpen @OnOpen
@@ -93,8 +121,8 @@ public class CompetitionWS {
.call(cm -> competPermService.hasEditPerm(securityCtx, cm).map(__ -> PermLevel.ADMIN) .call(cm -> competPermService.hasEditPerm(securityCtx, cm).map(__ -> PermLevel.ADMIN)
.onFailure() .onFailure()
.recoverWithUni(competPermService.hasTablePerm(securityCtx, cm).map(__ -> PermLevel.TABLE)) .recoverWithUni(competPermService.hasTablePerm(securityCtx, cm).map(__ -> PermLevel.TABLE))
.onFailure() //.onFailure()
.recoverWithUni(competPermService.hasViewPerm(securityCtx, cm).map(__ -> PermLevel.VIEW)) //.recoverWithUni(competPermService.hasViewPerm(securityCtx, cm).map(__ -> PermLevel.VIEW))
.invoke(prem -> connection.userData().put(UserData.TypedKey.forString("prem"), prem.toString())) .invoke(prem -> connection.userData().put(UserData.TypedKey.forString("prem"), prem.toString()))
.invoke(prem -> LOGGER.infof("Connection permission: %s", prem)) .invoke(prem -> LOGGER.infof("Connection permission: %s", prem))
.onFailure().transform(t -> new ForbiddenException())) .onFailure().transform(t -> new ForbiddenException()))
@@ -103,10 +131,13 @@ public class CompetitionWS {
waitingResponse.put(connection, new HashMap<>()); waitingResponse.put(connection, new HashMap<>());
}) })
.map(cm -> { .map(cm -> {
SimpleCompetData data = SimpleCompetData.fromModel(cm);
WelcomeInfo welcomeInfo = new WelcomeInfo(); WelcomeInfo welcomeInfo = new WelcomeInfo();
welcomeInfo.setName(cm.getName()); welcomeInfo.setName(cm.getName());
welcomeInfo.setPerm(connection.userData().get(UserData.TypedKey.forString("prem"))); welcomeInfo.setPerm(connection.userData().get(UserData.TypedKey.forString("prem")));
welcomeInfo.setShow_blason(data.isShow_blason());
welcomeInfo.setShow_flag(data.isShow_flag());
return new MessageOut(UUID.randomUUID(), "welcomeInfo", MessageType.NOTIFY, welcomeInfo); return new MessageOut(UUID.randomUUID(), "welcomeInfo", MessageType.NOTIFY, welcomeInfo);
}); });
@@ -118,6 +149,7 @@ public class CompetitionWS {
LOGGER.debugf("Active connections: %d", connection.getOpenConnections().size()); LOGGER.debugf("Active connections: %d", connection.getOpenConnections().size());
waitingResponse.remove(connection); waitingResponse.remove(connection);
rState.removeConnection(connection);
} }
private MessageOut makeReply(MessageIn message, Object data) { private MessageOut makeReply(MessageIn message, Object data) {
@@ -165,6 +197,7 @@ public class CompetitionWS {
return Uni.createFrom().item(makeError(message, "Permission denied")).toMulti(); return Uni.createFrom().item(makeError(message, "Permission denied")).toMulti();
return ((Uni<?>) method.invoke(entry.getValue(), connection, return ((Uni<?>) method.invoke(entry.getValue(), connection,
MAPPER.treeToValue(message.data(), method.getParameterTypes()[1]))) MAPPER.treeToValue(message.data(), method.getParameterTypes()[1])))
.ifNoItem().after(Duration.ofSeconds(5)).fail()
.map(o -> makeReply(message, o)) .map(o -> makeReply(message, o))
.onFailure() .onFailure()
.recoverWithItem(t -> { .recoverWithItem(t -> {
@@ -183,7 +216,7 @@ public class CompetitionWS {
// return Uni.createFrom().item(new Message<>(message.uuid(), message.code(), MessageType.REPLY, "ko")); // return Uni.createFrom().item(new Message<>(message.uuid(), message.code(), MessageType.REPLY, "ko"));
} }
public static Uni<Void> sendNotifyToOtherEditor(WebSocketConnection connection, String code, Object data) { public static void sendNotifyToOtherEditor(WebSocketConnection connection, String code, Object data) {
String uuid = connection.pathParam("uuid"); String uuid = connection.pathParam("uuid");
List<Uni<Void>> queue = new ArrayList<>(); List<Uni<Void>> queue = new ArrayList<>();
@@ -195,7 +228,39 @@ public class CompetitionWS {
} }
}); });
return Uni.join().all(queue).andCollectFailures().onFailure().recoverWithNull().replaceWithVoid(); Uni.join().all(queue)
.andCollectFailures()
.runSubscriptionOn(executor)
.subscribeAsCompletionStage()
.whenComplete((v, t) -> {
if (t != null) {
LOGGER.error("Error sending ws_out message", t);
}
});
}
public static void sendNotifyState(WebSocketConnection connection, String code, Object data) {
String uuid = connection.pathParam("uuid");
List<Uni<Void>> queue = new ArrayList<>();
queue.add(Uni.createFrom().voidItem()); // For avoid empty queue
connection.getOpenConnections().forEach(c -> {
Boolean s = c.userData().get(UserData.TypedKey.forBoolean("needState"));
if (uuid.equals(c.pathParam("uuid")) && s != null && s) {
queue.add(c.sendText(new MessageOut(UUID.randomUUID(), code, MessageType.NOTIFY, data)));
}
});
Uni.join().all(queue)
.andCollectFailures()
.runSubscriptionOn(executor)
.subscribeAsCompletionStage()
.whenComplete((v, t) -> {
if (t != null) {
LOGGER.error("Error sending ws_out message", t);
}
});
} }
@OnError @OnError

View File

@@ -0,0 +1,19 @@
package fr.titionfire.ffsaf.ws;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@Singleton
public class ExecutorProducer {
@Produces
@Named("notify-executor")
public Executor produceNotifyExecutor() {
// Créez un pool de threads avec une taille fixe (par exemple, 10 threads)
return Executors.newFixedThreadPool(10);
}
}

View File

@@ -8,4 +8,6 @@ import lombok.Data;
public class WelcomeInfo { public class WelcomeInfo {
private String name; private String name;
private String perm; private String perm;
private boolean show_blason;
private boolean show_flag;
} }

View File

@@ -0,0 +1,159 @@
package fr.titionfire.ffsaf.ws.recv;
import fr.titionfire.ffsaf.data.model.CardModel;
import fr.titionfire.ffsaf.data.model.MatchModel;
import fr.titionfire.ffsaf.data.repository.CardRepository;
import fr.titionfire.ffsaf.data.repository.ClubCardRepository;
import fr.titionfire.ffsaf.data.repository.CompetitionRepository;
import fr.titionfire.ffsaf.data.repository.MatchRepository;
import fr.titionfire.ffsaf.domain.service.CardService;
import fr.titionfire.ffsaf.domain.service.TradService;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import fr.titionfire.ffsaf.ws.PermLevel;
import fr.titionfire.ffsaf.ws.send.SSCard;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.runtime.annotations.RegisterForReflection;
import io.quarkus.websockets.next.WebSocketConnection;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.Date;
import java.util.List;
@WithSession
@ApplicationScoped
@RegisterForReflection
public class RCard {
@Inject
MatchRepository matchRepository;
@Inject
ClubCardRepository clubCardRepository;
@Inject
CardRepository cardRepository;
@Inject
CardService cardService;
@Inject
CompetitionRepository competitionRepository;
@Inject
TradService trad;
private Uni<MatchModel> getById(long id, WebSocketConnection connection) {
return matchRepository.findById(id)
.invoke(Unchecked.consumer(o -> {
if (o == null)
throw new DNotFoundException(trad.t("matche.non.trouver", connection));
if (!o.getCategory().getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException(trad.t("permission.denied", connection));
}));
}
@WSReceiver(code = "getCardForMatch", permission = PermLevel.VIEW)
public Uni<List<CardModel>> getCardForMatch(WebSocketConnection connection, Long matchId) {
if (matchId == null)
return Uni.createFrom().nullItem();
return getById(matchId, connection).chain(matchModel -> cardService.getForMatch(matchModel));
}
@WSReceiver(code = "getAllForTeamNoDetail", permission = PermLevel.VIEW)
public Uni<List<SendTeamCards>> getAllForTeamNoDetail(WebSocketConnection connection, Object o) {
return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
.chain(c -> clubCardRepository.list("competition = ?1", c.getId()))
.map(cards -> cards.stream()
.map(card -> new SendTeamCards(card.getTeamUuid(), card.getTeamName(), List.of(),
card.getType(), card.getReason(), card.getDate()))
.toList());
}
@WSReceiver(code = "sendCardAdd", permission = PermLevel.TABLE)
public Uni<Void> sendCardAdd(WebSocketConnection connection, SendCardAdd card) {
return getById(card.matchId(), connection)
.chain(matchModel -> cardService.checkCanBeAdded(card, matchModel)
.chain(c -> {
CardModel model = new CardModel();
model.setComb(card.combId());
model.setMatch(card.matchId());
model.setCategory(matchModel.getCategory().getId());
model.setCompetition(matchModel.getCategory().getCompet());
model.setCompetitionId(matchModel.getCategory().getCompet().getId());
model.setType(card.type());
model.setReason(card.reason());
return Panache.withTransaction(() -> cardRepository.persist(model));
})
)
.invoke(cardModel -> SSCard.sendCards(connection, List.of(cardModel)))
.replaceWithVoid();
}
@WSReceiver(code = "sendCardRm", permission = PermLevel.ADMIN)
public Uni<Void> sendCardRm(WebSocketConnection connection, SendCardAdd card) {
return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
.chain(competition -> cardRepository.find(
"match IS NULL AND comb = ?1 AND type = ?2 AND competition = ?3 AND match " + (card.matchId() == null ? "IS NULL" : "= " + card.matchId()),
card.combId(), card.type(), competition)
.firstResult()
.invoke(Unchecked.consumer(o -> {
if (o == null)
throw new DNotFoundException(trad.t("carton.non.trouver", connection));
SSCard.sendRmCards(connection, List.of(o.getId()));
}))
.chain(cardModel -> Panache.withTransaction(() -> cardRepository.delete(cardModel)))
)
.replaceWithVoid();
}
@WSReceiver(code = "applyTeamCards", permission = PermLevel.TABLE)
public Uni<Void> applyTeamCards(WebSocketConnection connection, SendTeamCards teamCards) {
return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
.chain(c -> cardService.addTeamCard(c, teamCards.teamUuid(), teamCards.teamName(), teamCards.type,
teamCards.reason()))
.invoke(cards -> SSCard.sendTeamCard(connection,
new SendTeamCards(teamCards.teamUuid(), teamCards.teamName(), cards, teamCards.type(),
teamCards.reason(), new Date())))
.replaceWithVoid();
}
@WSReceiver(code = "sendTeamCardReturnState", permission = PermLevel.TABLE)
public Uni<Void> sendTeamCardReturnState(WebSocketConnection connection, SendTeamCardReturnState state) {
if (state.state <= 0 || state.state > 2)
return Uni.createFrom().voidItem();
return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
.chain(c -> cardService.recvReturnState(c, state))
.invoke(cards -> SSCard.sendCards(connection, cards))
.replaceWithVoid();
}
@WSReceiver(code = "removeTeamCards", permission = PermLevel.TABLE)
public Uni<Void> removeTeamCards(WebSocketConnection connection, SendTeamCards teamCards) {
return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
.chain(c -> cardService.rmTeamCard(c, teamCards.teamUuid(), teamCards.teamName(), teamCards.type))
.invoke(cards -> SSCard.sendRmCards(connection, cards))
.invoke(__ -> SSCard.rmTeamCard(connection, teamCards))
.replaceWithVoid();
}
@RegisterForReflection
public record SendCardAdd(Long matchId, long combId, CardModel.CardType type, String reason) {
}
@RegisterForReflection
public record SendTeamCards(String teamUuid, String teamName, List<CardModel> cards, CardModel.CardType type,
String reason, Date date) {
}
@RegisterForReflection
public record SendTeamCardReturnState(String teamUuid, String teamName, CardModel.CardType type,
int state, Long selectedCategory, Long selectedMatch) {
}
}

View File

@@ -1,22 +1,25 @@
package fr.titionfire.ffsaf.ws.recv; package fr.titionfire.ffsaf.ws.recv;
import fr.titionfire.ffsaf.data.model.CategoryModel; import fr.titionfire.ffsaf.data.model.*;
import fr.titionfire.ffsaf.data.model.MatchModel; import fr.titionfire.ffsaf.data.repository.*;
import fr.titionfire.ffsaf.data.model.TreeModel;
import fr.titionfire.ffsaf.data.repository.CategoryRepository;
import fr.titionfire.ffsaf.data.repository.CompetitionRepository;
import fr.titionfire.ffsaf.data.repository.MatchRepository;
import fr.titionfire.ffsaf.data.repository.TreeRepository;
import fr.titionfire.ffsaf.domain.entity.MatchEntity; import fr.titionfire.ffsaf.domain.entity.MatchEntity;
import fr.titionfire.ffsaf.domain.entity.MatchModelExtend;
import fr.titionfire.ffsaf.domain.entity.TreeEntity; import fr.titionfire.ffsaf.domain.entity.TreeEntity;
import fr.titionfire.ffsaf.domain.service.CardService;
import fr.titionfire.ffsaf.domain.service.ResultService;
import fr.titionfire.ffsaf.domain.service.TradService;
import fr.titionfire.ffsaf.rest.data.PresetData;
import fr.titionfire.ffsaf.rest.data.ResultCategoryData;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException; import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import fr.titionfire.ffsaf.utils.TreeNode; import fr.titionfire.ffsaf.utils.TreeNode;
import fr.titionfire.ffsaf.ws.PermLevel; import fr.titionfire.ffsaf.ws.PermLevel;
import fr.titionfire.ffsaf.ws.send.SSCategorie; import fr.titionfire.ffsaf.ws.send.SSCategorie;
import fr.titionfire.ffsaf.ws.send.SSMatch;
import io.quarkus.hibernate.reactive.panache.Panache; import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.common.WithSession; import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import io.quarkus.websockets.next.UserData;
import io.quarkus.websockets.next.WebSocketConnection; import io.quarkus.websockets.next.WebSocketConnection;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked; import io.smallrye.mutiny.unchecked.Unchecked;
@@ -25,8 +28,9 @@ import jakarta.inject.Inject;
import lombok.Data; import lombok.Data;
import org.hibernate.reactive.mutiny.Mutiny; import org.hibernate.reactive.mutiny.Mutiny;
import java.util.ArrayList; import java.util.*;
import java.util.List; import java.util.stream.Collectors;
import java.util.stream.Stream;
@WithSession @WithSession
@ApplicationScoped @ApplicationScoped
@@ -46,13 +50,28 @@ public class RCategorie {
@Inject @Inject
TreeRepository treeRepository; TreeRepository treeRepository;
@Inject
CardService cardService;
@Inject
CardRepository cardRepository;
@Inject
CatPresetRepository catPresetRepository;
@Inject
ResultService resultService;
@Inject
TradService trad;
private Uni<CategoryModel> getById(long id, WebSocketConnection connection) { private Uni<CategoryModel> getById(long id, WebSocketConnection connection) {
return categoryRepository.findById(id) return categoryRepository.findById(id)
.invoke(Unchecked.consumer(o -> { .invoke(Unchecked.consumer(o -> {
if (o == null) if (o == null)
throw new DNotFoundException("Catégorie non trouver"); throw new DNotFoundException(trad.t("categorie.non.trouver", connection));
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid"))) if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException("Permission denied"); throw new DForbiddenException(trad.t("permission.denied", connection));
})); }));
} }
@@ -72,6 +91,9 @@ public class RCategorie {
fullCategory.setName(cat.getName()); fullCategory.setName(cat.getName());
fullCategory.setLiceName(cat.getLiceName()); fullCategory.setLiceName(cat.getLiceName());
fullCategory.setType(cat.getType()); fullCategory.setType(cat.getType());
fullCategory.setTreeAreClassement(cat.isTreeAreClassement());
fullCategory.setFullClassement(cat.isFullClassement());
fullCategory.setPreset(PresetData.fromModel(cat.getPreset()));
}) })
.call(cat -> Mutiny.fetch(cat.getMatchs()) .call(cat -> Mutiny.fetch(cat.getMatchs())
.map(matchModels -> matchModels.stream().filter(o -> o.getCategory_ord() >= 0) .map(matchModels -> matchModels.stream().filter(o -> o.getCategory_ord() >= 0)
@@ -80,6 +102,8 @@ public class RCategorie {
.call(cat -> treeRepository.list("category = ?1 AND level != 0", cat.getId()) .call(cat -> treeRepository.list("category = ?1 AND level != 0", cat.getId())
.map(treeModels -> treeModels.stream().map(TreeEntity::fromModel).toList()) .map(treeModels -> treeModels.stream().map(TreeEntity::fromModel).toList())
.invoke(fullCategory::setTrees)) .invoke(fullCategory::setTrees))
.call(cat -> cardService.getAll(cat.getCompet())
.invoke(fullCategory::setCards))
.map(__ -> fullCategory); .map(__ -> fullCategory);
} }
@@ -94,20 +118,67 @@ public class RCategorie {
categoryModel.setTree(new ArrayList<>()); categoryModel.setTree(new ArrayList<>());
categoryModel.setType(categorie.type); categoryModel.setType(categorie.type);
categoryModel.setLiceName(categorie.liceName); categoryModel.setLiceName(categorie.liceName);
categoryModel.setTreeAreClassement(categorie.treeAreClassement);
categoryModel.setFullClassement(categorie.fullClassement);
if (categorie.preset() != null)
return catPresetRepository.findById(categorie.preset().getId())
.invoke(categoryModel::setPreset)
.chain(__ -> categoryRepository.create(categoryModel));
return categoryRepository.create(categoryModel); return categoryRepository.create(categoryModel);
}) })
.call(cat -> SSCategorie.sendAddCategory(connection, cat)) .invoke(cat -> SSCategorie.sendAddCategory(connection, cat))
.map(CategoryModel::getId); .map(CategoryModel::getId);
} }
@WSReceiver(code = "createOrReplaceCategory", permission = PermLevel.ADMIN)
public Uni<Long> createOrReplaceCategory(WebSocketConnection connection, JustCategorie categorie) {
return matchRepository.list("category.compet.uuid = ?1 AND category.name = ?2", connection.pathParam("uuid"),
categorie.name)
.chain(existing -> {
if (existing.isEmpty())
return createCategory(connection, categorie);
Map<Long, List<MatchModel>> matchesByCategory = existing.stream()
.filter(m -> m.getCategory() != null)
.collect(Collectors.groupingBy(m -> m.getCategory().getId()));
for (Map.Entry<Long, List<MatchModel>> entry : matchesByCategory.entrySet()) {
Long categoryId = entry.getKey();
List<MatchModel> matches = entry.getValue();
if (matches.stream().noneMatch(m -> !m.getScores().isEmpty() || m.isEnd()))
return Panache.withTransaction(() -> updateCategory(connection, categorie, categoryId)
.call(__ -> treeRepository.delete("category = ?1", categoryId))
.call(__ -> matchRepository.delete("category.id = ?1", categoryId)))
.replaceWith(categoryId);
}
return createCategory(connection, categorie);
});
}
@WSReceiver(code = "updateCategory", permission = PermLevel.ADMIN) @WSReceiver(code = "updateCategory", permission = PermLevel.ADMIN)
public Uni<Void> updateCategory(WebSocketConnection connection, JustCategorie categorie) { public Uni<Void> updateCategory(WebSocketConnection connection, JustCategorie categorie) {
return getById(categorie.id, connection) return updateCategory(connection, categorie, categorie.id);
}
private Uni<Void> updateCategory(WebSocketConnection connection, JustCategorie categorie, Long id) {
return getById(id, connection)
.call(cat -> {
if (categorie.preset() == null) {
cat.setPreset(null);
return Uni.createFrom().item(cat);
} else {
return catPresetRepository.findById(categorie.preset().getId())
.invoke(cat::setPreset);
}
})
.chain(cat -> { .chain(cat -> {
cat.setName(categorie.name); cat.setName(categorie.name);
cat.setLiceName(categorie.liceName); cat.setLiceName(categorie.liceName);
cat.setType(categorie.type); cat.setType(categorie.type);
cat.setTreeAreClassement(categorie.treeAreClassement);
cat.setFullClassement(categorie.fullClassement);
return Panache.withTransaction(() -> categoryRepository.persist(cat)); return Panache.withTransaction(() -> categoryRepository.persist(cat));
}) })
.call(cat -> { .call(cat -> {
@@ -118,9 +189,10 @@ public class RCategorie {
uni = uni.chain(__ -> treeRepository.delete("category = ?1", cat.getId())) uni = uni.chain(__ -> treeRepository.delete("category = ?1", cat.getId()))
.chain(__ -> matchRepository.delete("category = ?1 AND category_ord = -42", cat)); .chain(__ -> matchRepository.delete("category = ?1 AND category_ord = -42", cat));
} }
return uni; Uni<Long> finalUni = uni;
return Panache.withTransaction(() -> finalUni);
}) })
.call(cat -> SSCategorie.sendCategory(connection, cat)) .invoke(cat -> SSCategorie.sendCategory(connection, cat))
.replaceWithVoid(); .replaceWithVoid();
} }
@@ -201,14 +273,169 @@ public class RCategorie {
.call(__ -> treeRepository.flush()) .call(__ -> treeRepository.flush())
.call(cat -> treeRepository.list("category = ?1 AND level != 0", cat.getId()) .call(cat -> treeRepository.list("category = ?1 AND level != 0", cat.getId())
.map(treeModels -> treeModels.stream().map(TreeEntity::fromModel).toList()) .map(treeModels -> treeModels.stream().map(TreeEntity::fromModel).toList())
.chain(trees -> SSCategorie.sendTreeCategory(connection, trees))) .invoke(trees -> SSCategorie.sendTreeCategory(connection, trees)))
.replaceWithVoid(); .replaceWithVoid();
} }
@WSReceiver(code = "deleteCategory", permission = PermLevel.ADMIN)
public Uni<Void> deleteCategory(WebSocketConnection connection, Long id) {
return getById(id, connection)
.call(cat -> Panache.withTransaction(() -> treeRepository.delete("category = ?1", cat.getId())
.call(__ -> matchRepository.delete("category = ?1", cat))))
.chain(cat -> Panache.withTransaction(() -> categoryRepository.delete(cat)))
.invoke(__ -> SSCategorie.sendDelCategory(connection, id))
.replaceWithVoid();
}
@WSReceiver(code = "listPreset", permission = PermLevel.VIEW)
public Uni<List<PresetData>> listPreset(WebSocketConnection connection, Object o) {
return catPresetRepository.list("competition.uuid", connection.pathParam("uuid"))
.map(presets -> presets.stream().map(PresetData::fromModel).toList());
}
@WSReceiver(code = "createClassementMatchs", permission = PermLevel.TABLE)
public Uni<Void> createClassementMatchs(WebSocketConnection connection, Long categoryId) {
return getById(categoryId, connection)
.call(cat -> {
PermLevel perm = PermLevel.valueOf(connection.userData().get(UserData.TypedKey.forString("prem")));
if (perm == PermLevel.TABLE) {
return matchRepository.list("category = ?1 AND category_ord = -42", cat.getId())
.chain(l -> l.stream().anyMatch(MatchModel::isEnd) ?
Uni.createFrom().failure(
new DForbiddenException(trad.t("err.match.termine", connection))) :
Uni.createFrom().voidItem());
}
return Uni.createFrom().voidItem();
})
.call(cat -> treeRepository.list("category = ?1 AND level <= -10", cat.getId())
.map(l -> l.stream().map(o -> o.getMatch().getId()).toList())
.call(__ -> treeRepository.delete("category = ?1 AND level <= -10", cat.getId()))
.call(ids -> matchRepository.delete("id IN ?1", ids)))
.call(cat -> Mutiny.fetch(cat.getTree()))
.call(cat -> {
List<MatchModel> toSave = new ArrayList<>();
if (!cat.getTree().isEmpty()) {
for (TreeModel treeModel : cat.getTree())
cleanTree(treeModel, toSave);
}
return Panache.withTransaction(() -> matchRepository.persist(toSave))
.invoke(__ -> SSMatch.sendMatch(connection,
toSave.stream().map(MatchEntity::fromModel).toList()));
})
.chain(cat -> Mutiny.fetch(cat.getMatchs())
.chain(list -> cardRepository.list("competition = ?1", cat.getCompet())
.map(c -> list.stream().map(m -> new MatchModelExtend(m, c)).toList()))
.map(matchModels -> {
ResultCategoryData out = new ResultCategoryData();
resultService.getArray2(matchModels, null, out);
out.getRankArray().remove('-');
return out;
})
.invoke(Unchecked.consumer(result -> {
if (result.getRankArray().size() != 2) {
throw new DForbiddenException(trad.t("configuration.non.supportee", connection));
}
}))
.chain(result -> {
List<MatchModel> toSave = new ArrayList<>();
List<MatchModel> toCreate = new ArrayList<>();
List<TreeModel> toSaveTree = new ArrayList<>();
int treeSize = 0;
List<TreeModel> lastNode = new ArrayList<>();
Optional<TreeModel> tree = cat.getTree().stream().filter(t -> t.getLevel() > 0)
.min(Comparator.comparing(TreeModel::getLevel));
if (tree.isPresent()) {
tree.get().getChildrenAtDepth(tree.get().death() - 1, 0, lastNode);
treeSize = lastNode.size();
}
Iterator<List<ResultCategoryData.RankArray>> iterator = result.getRankArray().values()
.iterator();
List<ResultCategoryData.RankArray> poule1 = iterator.next();
List<ResultCategoryData.RankArray> poule2 = iterator.next();
int maxToMixFill = Math.min(poule1.size(), poule2.size());
int maxToMixTreeFill = Math.min(treeSize, maxToMixFill);
for (int i = 0; i < maxToMixTreeFill; i++) {
CombModel comb1 = poule1.get(i).getComb();
CombModel comb2 = poule2.get(treeSize - i - 1).getComb();
fillMatchComb(lastNode.get(i).getMatch(), comb1, comb2);
toSave.add(lastNode.get(i).getMatch());
}
if (cat.isFullClassement()) {
for (int i = maxToMixTreeFill; i < maxToMixFill; i++) {
MatchModel match = new MatchModel();
match.setCategory(cat);
match.setCategory_ord(-42);
match.setEnd(false);
CombModel comb1 = poule1.get(i).getComb();
CombModel comb2 = poule2.get(i).getComb();
fillMatchComb(match, comb1, comb2);
toCreate.add(match);
toSaveTree.add(new TreeModel(cat.getId(), -10 - i, match));
}
}
return Panache.withTransaction(() -> matchRepository.persist(toSave)
.call(__ -> matchRepository.create(toCreate)))
.call(__ -> toSaveTree.isEmpty() ? Uni.createFrom().voidItem()
: Panache.withTransaction(() -> treeRepository.persist(toSaveTree)))
.map(__ -> Stream.concat(toSave.stream(), toCreate.stream())
.map(MatchEntity::fromModel).toList());
})
)
.onFailure().invoke(t -> System.out.println("error: " + t.getMessage()))
.invoke(matchEntities -> SSMatch.sendMatch(connection, matchEntities))
.call(__ -> treeRepository.list("category = ?1 AND level != 0", categoryId)
.map(treeModels -> treeModels.stream().map(TreeEntity::fromModel).toList())
.invoke(trees -> SSCategorie.sendTreeCategory(connection, trees)))
.replaceWithVoid();
}
private void cleanTree(TreeModel treeModel, List<MatchModel> toSave) {
MatchModel model = treeModel.getMatch();
if (model != null) {
model.setC1_id(null);
model.setC1_guest(null);
model.setC2_id(null);
model.setC2_guest(null);
model.setEnd(false);
model.setDate(null);
model.getScores().clear();
toSave.add(model);
}
if (treeModel.getLeft() != null)
cleanTree(treeModel.getLeft(), toSave);
if (treeModel.getRight() != null)
cleanTree(treeModel.getRight(), toSave);
}
private void fillMatchComb(MatchModel match, CombModel comb1, CombModel comb2) {
if (comb1 instanceof MembreModel m)
match.setC1_id(m);
else if (comb1 instanceof CompetitionGuestModel g)
match.setC1_guest(g);
if (comb2 instanceof MembreModel m)
match.setC2_id(m);
else if (comb2 instanceof CompetitionGuestModel g)
match.setC2_guest(g);
}
@RegisterForReflection @RegisterForReflection
public record JustCategorie(long id, String name, int type, String liceName) { public record JustCategorie(long id, String name, int type, String liceName, boolean treeAreClassement,
boolean fullClassement, PresetData preset) {
public static JustCategorie from(CategoryModel m) { public static JustCategorie from(CategoryModel m) {
return new JustCategorie(m.getId(), m.getName(), m.getType(), m.getLiceName()); return new JustCategorie(m.getId(), m.getName(), m.getType(), m.getLiceName(), m.isTreeAreClassement(),
m.isFullClassement(), PresetData.fromModel(m.getPreset()));
} }
} }
@@ -223,7 +450,11 @@ public class RCategorie {
String name; String name;
int type; int type;
String liceName; String liceName;
boolean treeAreClassement = false;
boolean fullClassement = false;
PresetData preset;
List<TreeEntity> trees = null; List<TreeEntity> trees = null;
List<MatchEntity> matches; List<MatchEntity> matches;
List<CardModel> cards;
} }
} }

View File

@@ -4,6 +4,7 @@ import fr.titionfire.ffsaf.data.model.*;
import fr.titionfire.ffsaf.data.repository.*; import fr.titionfire.ffsaf.data.repository.*;
import fr.titionfire.ffsaf.domain.entity.MatchEntity; import fr.titionfire.ffsaf.domain.entity.MatchEntity;
import fr.titionfire.ffsaf.domain.entity.TreeEntity; import fr.titionfire.ffsaf.domain.entity.TreeEntity;
import fr.titionfire.ffsaf.domain.service.TradService;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException; import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import fr.titionfire.ffsaf.utils.ScoreEmbeddable; import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
@@ -44,13 +45,19 @@ public class RMatch {
@Inject @Inject
CompetitionGuestRepository competitionGuestRepository; CompetitionGuestRepository competitionGuestRepository;
@Inject
TradService trad;
@Inject
RState rState;
private Uni<MatchModel> getById(long id, WebSocketConnection connection) { private Uni<MatchModel> getById(long id, WebSocketConnection connection) {
return matchRepository.findById(id) return matchRepository.findById(id)
.invoke(Unchecked.consumer(o -> { .invoke(Unchecked.consumer(o -> {
if (o == null) if (o == null)
throw new DNotFoundException("Matche non trouver"); throw new DNotFoundException(trad.t("matche.non.trouver", connection));
if (!o.getCategory().getCompet().getUuid().equals(connection.pathParam("uuid"))) if (!o.getCategory().getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException("Permission denied"); throw new DForbiddenException(trad.t("permission.denied", connection));
})); }));
} }
@@ -78,13 +85,13 @@ public class RMatch {
return categoryRepository.findById(m.categorie) return categoryRepository.findById(m.categorie)
.invoke(Unchecked.consumer(o -> { .invoke(Unchecked.consumer(o -> {
if (o == null) if (o == null)
throw new DNotFoundException("Catégorie non trouver"); throw new DNotFoundException(trad.t("categorie.non.trouver", connection));
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid"))) if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException("Permission denied"); throw new DForbiddenException(trad.t("permission.denied", connection));
})) }))
.chain(categoryModel -> creatMatch(categoryModel, m)) .chain(categoryModel -> creatMatch(categoryModel, m))
.chain(mm -> Panache.withTransaction(() -> matchRepository.create(mm))) .chain(mm -> Panache.withTransaction(() -> matchRepository.create(mm)))
.call(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm))) .invoke(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm)))
.replaceWithVoid(); .replaceWithVoid();
} }
@@ -121,7 +128,7 @@ public class RMatch {
mm.setC2_guest(null); mm.setC2_guest(null);
})) }))
.chain(mm -> Panache.withTransaction(() -> matchRepository.persist(mm))) .chain(mm -> Panache.withTransaction(() -> matchRepository.persist(mm)))
.call(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm))) .invoke(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm)))
.replaceWithVoid(); .replaceWithVoid();
} }
@@ -136,7 +143,7 @@ public class RMatch {
m.getCategory_ord())) m.getCategory_ord()))
.invoke(m -> m.setCategory_ord(order.pos)) .invoke(m -> m.setCategory_ord(order.pos))
.call(m -> Panache.withTransaction(() -> matchRepository.persist(m))) .call(m -> Panache.withTransaction(() -> matchRepository.persist(m)))
.call(mm -> SSMatch.sendMatchOrder(connection, order)) .invoke(mm -> SSMatch.sendMatchOrder(connection, order))
.replaceWithVoid(); .replaceWithVoid();
} }
@@ -163,12 +170,12 @@ public class RMatch {
.call(mm -> { .call(mm -> {
if (mm.isEnd() && mm.win() != old_win && mm.getCategory_ord() == -42) { if (mm.isEnd() && mm.win() != old_win && mm.getCategory_ord() == -42) {
return updateEndAndTree(mm, new ArrayList<>()) return updateEndAndTree(mm, new ArrayList<>())
.call(l -> SSMatch.sendMatch(connection, l)); .invoke(l -> SSMatch.sendMatch(connection, l));
} }
return Uni.createFrom().nullItem(); return Uni.createFrom().nullItem();
}); });
}) })
.call(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm))) .invoke(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm)))
.replaceWithVoid(); .replaceWithVoid();
} }
@@ -188,8 +195,9 @@ public class RMatch {
return Panache.withTransaction(() -> matchRepository.persist(mm)); return Panache.withTransaction(() -> matchRepository.persist(mm));
}) })
.invoke(mm -> toSend.add(MatchEntity.fromModel(mm))) .invoke(mm -> toSend.add(MatchEntity.fromModel(mm)))
.invoke(mm -> rState.setMatchEnd(connection, matchEnd))
.chain(mm -> updateEndAndTree(mm, toSend)) .chain(mm -> updateEndAndTree(mm, toSend))
.call(__ -> SSMatch.sendMatch(connection, toSend)) .invoke(__ -> SSMatch.sendMatch(connection, toSend))
.replaceWithVoid(); .replaceWithVoid();
} }
@@ -277,8 +285,9 @@ public class RMatch {
@WSReceiver(code = "deleteMatch", permission = PermLevel.ADMIN) @WSReceiver(code = "deleteMatch", permission = PermLevel.ADMIN)
public Uni<Void> deleteMatch(WebSocketConnection connection, Long idMatch) { public Uni<Void> deleteMatch(WebSocketConnection connection, Long idMatch) {
return getById(idMatch, connection) return getById(idMatch, connection)
.chain(matchModel -> Panache.withTransaction(() -> matchRepository.delete(matchModel))) .map(__ -> idMatch)
.call(__ -> SSMatch.sendDeleteMatch(connection, idMatch)) .chain(l -> Panache.withTransaction(() -> matchRepository.delete("id = ?1", l)))
.invoke(__ -> SSMatch.sendDeleteMatch(connection, idMatch))
.replaceWithVoid(); .replaceWithVoid();
} }
@@ -288,13 +297,15 @@ public class RMatch {
return categoryRepository.findById(data.categorie) return categoryRepository.findById(data.categorie)
.invoke(Unchecked.consumer(o -> { .invoke(Unchecked.consumer(o -> {
if (o == null) if (o == null)
throw new DNotFoundException("Catégorie non trouver"); throw new DNotFoundException(trad.t("categorie.non.trouver", connection));
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid"))) if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException("Permission denied"); throw new DForbiddenException(trad.t("permission.denied", connection));
})) }))
.call(cm -> matchRepository.delete("id IN ?1 AND category = ?2", data.matchesToRemove, cm) .call(cm -> data.matchesToRemove.isEmpty() ? Uni.createFrom().voidItem() :
.call(__ -> SSMatch.sendDeleteMatch(connection, data.matchesToRemove))) (Panache.withTransaction(
.call(cm -> matchRepository.list("id IN ?1 AND category = ?2", () -> matchRepository.delete("id IN ?1 AND category = ?2", data.matchesToRemove, cm))
.invoke(__ -> SSMatch.sendDeleteMatch(connection, data.matchesToRemove))))
.call(cm -> Panache.withSession(() -> 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)
@@ -304,19 +315,22 @@ public class RMatch {
if (data.matchOrderToUpdate.containsKey(model.getId())) if (data.matchOrderToUpdate.containsKey(model.getId()))
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 -> mm.isEmpty() ? Uni.createFrom().voidItem() :
.invoke(mm -> matches.addAll(mm.stream().map(MatchEntity::fromModel).toList())) Panache.withTransaction(() -> matchRepository.persist(mm)))
.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));
return uni; Uni<List<MatchModel>> finalUni = uni;
return Panache.withSession(() -> finalUni);
} }
) )
.chain(mm -> Panache.withTransaction(() -> matchRepository.create(mm)) .chain(mm -> mm.isEmpty() ? Uni.createFrom().voidItem() :
.invoke(__ -> matches.addAll(mm.stream().map(MatchEntity::fromModel).toList()))) Panache.withTransaction(() -> matchRepository.create(mm))
.call(__ -> SSMatch.sendMatch(connection, matches)) .invoke(__ -> matches.addAll(mm.stream().map(MatchEntity::fromModel).toList())))
.invoke(__ -> SSMatch.sendMatch(connection, matches))
.replaceWithVoid(); .replaceWithVoid();
} }

View File

@@ -0,0 +1,112 @@
package fr.titionfire.ffsaf.ws.recv;
import fr.titionfire.ffsaf.data.model.CardModel;
import fr.titionfire.ffsaf.data.model.CategoryModel;
import fr.titionfire.ffsaf.data.model.MatchModel;
import fr.titionfire.ffsaf.data.repository.CardRepository;
import fr.titionfire.ffsaf.data.repository.CategoryRepository;
import fr.titionfire.ffsaf.data.repository.CompetitionRepository;
import fr.titionfire.ffsaf.data.repository.MatchRepository;
import fr.titionfire.ffsaf.domain.entity.MatchModelExtend;
import fr.titionfire.ffsaf.domain.service.ResultService;
import fr.titionfire.ffsaf.domain.service.TradService;
import fr.titionfire.ffsaf.rest.data.ResultCategoryData;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.ws.PermLevel;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.runtime.annotations.RegisterForReflection;
import io.quarkus.websockets.next.WebSocketConnection;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import org.hibernate.reactive.mutiny.Mutiny;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Stream;
@WithSession
@ApplicationScoped
@RegisterForReflection
public class RPDF {
@Inject
CompetitionRepository competitionRepository;
@Inject
MatchRepository matchRepository;
@Inject
CardRepository cardRepository;
@Inject
CategoryRepository categoryRepository;
@Inject
ResultService resultService;
@Inject
TradService trad;
@Transactional
@WSReceiver(code = "getPodium", permission = PermLevel.VIEW)
public Uni<List<PodiumEntity>> getPodium(WebSocketConnection connection, Object o) {
List<CardModel> cards = new java.util.ArrayList<>();
return cardRepository.list("competition.uuid = ?1", connection.pathParam("uuid"))
.invoke(cards::addAll)
.chain(__ -> matchRepository.list("category.compet.uuid = ?1", connection.pathParam("uuid")))
.chain(matchs -> {
HashMap<CategoryModel, List<MatchModel>> map = new HashMap<>();
for (MatchModel match : matchs) {
if (!map.containsKey(match.getCategory()))
map.put(match.getCategory(), new java.util.ArrayList<>());
map.get(match.getCategory()).add(match);
}
return Multi.createFrom().iterable(map.entrySet())
.onItem().call(entry -> Mutiny.fetch(entry.getKey().getTree()))
.map(entry -> {
ResultCategoryData tmp = new ResultCategoryData();
double cmoy = entry.getValue().stream().flatMap(m -> Stream.of(m.getC1(), m.getC2()))
.filter(c -> c != null && c.getCategorie() != null)
.mapToInt(c -> c.getCategorie().ordinal())
.average().orElse(0);
Categorie categorie_moy = Categorie.values()[(int) Math.ceil(cmoy)];
resultService.getArray2(
entry.getValue().stream().map(m -> new MatchModelExtend(m, cards)).toList(),
null, tmp);
resultService.getClassementArray(entry.getKey(), null, cards, tmp);
String source = "";
if ((entry.getKey().getType() & 2) != 0) {
if (entry.getKey().isTreeAreClassement())
source = trad.t("podium.source.classement", connection);
else
source = trad.t("podium.source.tree", connection);
} else if ((entry.getKey().getType() & 1) != 0)
source = trad.t("podium.source.poule", connection);
return new PodiumEntity(entry.getKey().getName(), source, categorie_moy,
tmp.getClassement());
})
.collect().asList();
});
}
@WSReceiver(code = "getPodiumClub", permission = PermLevel.VIEW)
public Uni<List<ResultService.ClubClassement>> getPodiumClub(WebSocketConnection connection, Object o) {
return resultService.getAllClubArray(connection.pathParam("uuid"), false);
}
@RegisterForReflection
public static record PodiumEntity(String poule_name, String source, Categorie categorie,
List<ResultCategoryData.ClassementData> podium) {
}
}

View File

@@ -6,6 +6,7 @@ import fr.titionfire.ffsaf.ws.PermLevel;
import io.quarkus.hibernate.reactive.panache.common.WithSession; import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import io.quarkus.websockets.next.WebSocketConnection; import io.quarkus.websockets.next.WebSocketConnection;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
@@ -22,16 +23,22 @@ public class RRegister {
@Inject @Inject
CompetitionRepository competitionRepository; CompetitionRepository competitionRepository;
@WSReceiver(code = "getRegister", permission = PermLevel.ADMIN) @WSReceiver(code = "getRegister", permission = PermLevel.TABLE)
public Uni<List<CombEntity>> getRegister(WebSocketConnection connection, Object o) { public Uni<List<CombEntity>> getRegister(WebSocketConnection connection, Object o) {
ArrayList<CombEntity> combEntities = new ArrayList<>();
return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult() return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
.call(cm -> Mutiny.fetch(cm.getInsc())) .call(cm -> Mutiny.fetch(cm.getInsc())
.call(cm -> Mutiny.fetch(cm.getGuests())) .onItem().transformToMulti(Multi.createFrom()::iterable)
.map(cm -> { .call(r -> Mutiny.fetch(r.getCategoriesInscrites()))
ArrayList<CombEntity> combEntities = new ArrayList<>(); .map(r -> CombEntity.fromModel(r).addCategoriesInscrites(r.getCategoriesInscrites()))
combEntities.addAll(cm.getInsc().stream().map(CombEntity::fromModel).toList()); .collect().asList()
combEntities.addAll(cm.getGuests().stream().map(CombEntity::fromModel).toList()); .invoke(combEntities::addAll))
return combEntities; .call(cm -> Mutiny.fetch(cm.getGuests())
}); .onItem().transformToMulti(Multi.createFrom()::iterable)
.call(r -> Mutiny.fetch(r.getCategoriesInscrites()))
.map(r -> CombEntity.fromModel(r).addCategoriesInscrites(r.getCategoriesInscrites()))
.collect().asList()
.invoke(combEntities::addAll))
.replaceWith(combEntities);
} }
} }

View File

@@ -0,0 +1,149 @@
package fr.titionfire.ffsaf.ws.recv;
import fr.titionfire.ffsaf.ws.PermLevel;
import fr.titionfire.ffsaf.ws.send.SSState;
import io.quarkus.runtime.annotations.RegisterForReflection;
import io.quarkus.websockets.next.UserData;
import io.quarkus.websockets.next.WebSocketConnection;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import lombok.Data;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
@ApplicationScoped
@RegisterForReflection
public class RState {
private static final HashMap<WebSocketConnection, TableState> tableStates = new HashMap<>();
@WSReceiver(code = "subscribeToState", permission = PermLevel.VIEW)
public Uni<List<TableState>> sendCurrentScore(WebSocketConnection connection, Boolean subscribe) {
connection.userData().put(UserData.TypedKey.forBoolean("needState"), subscribe);
if (subscribe) {
String uuid = connection.pathParam("uuid");
return Uni.createFrom().item(() ->
tableStates.values().stream().filter(s -> s.getCompetitionUuid().equals(uuid)).toList()
);
}
return Uni.createFrom().nullItem();
}
@WSReceiver(code = "sendState", permission = PermLevel.TABLE)
public Uni<Void> sendState(WebSocketConnection connection, TableState tableState) {
tableState.setCompetitionUuid(connection.pathParam("uuid"));
if (tableStates.containsKey(connection))
tableState.setId(tableStates.get(connection).getId());
if (tableState.getChronoState().isRunning() && tableState.getChronoState().state == 0)
tableState.setState(MatchState.IN_PROGRESS);
tableStates.put(connection, tableState);
SSState.sendStateFull(connection, tableState);
return Uni.createFrom().voidItem();
}
@WSReceiver(code = "sendSelectCategory", permission = PermLevel.TABLE)
public Uni<Void> sendSelectCategory(WebSocketConnection connection, Long catId) {
TableState tableState = tableStates.get(connection);
if (tableState != null) {
tableState.setSelectedCategory(catId);
tableState.setState(MatchState.NOT_STARTED);
SSState.sendStateFull(connection, tableState);
}
return Uni.createFrom().voidItem();
}
@WSReceiver(code = "sendSelectMatch", permission = PermLevel.TABLE)
public Uni<Void> sendSelectMatch(WebSocketConnection connection, Long matchId) {
TableState tableState = tableStates.get(connection);
if (tableState != null) {
tableState.setSelectedMatch(matchId);
tableState.setState(MatchState.NOT_STARTED);
SSState.sendStateFull(connection, tableState);
}
return Uni.createFrom().voidItem();
}
@WSReceiver(code = "sendCurentChrono", permission = PermLevel.TABLE)
public Uni<Void> sendCurentChrono(WebSocketConnection connection, ChronoState chronoState) {
TableState tableState = tableStates.get(connection);
if (tableState != null) {
tableState.setChronoState(chronoState);
if (chronoState.isRunning())
tableState.setState(MatchState.IN_PROGRESS);
SSState.sendStateFull(connection, tableState);
}
return Uni.createFrom().voidItem();
}
@WSReceiver(code = "sendLicenceName", permission = PermLevel.TABLE)
public Uni<Void> sendCurrentScore(WebSocketConnection connection, String name) {
TableState tableState = tableStates.get(connection);
if (tableState != null) {
tableState.setLiceName(name);
SSState.sendStateFull(connection, tableState);
}
return Uni.createFrom().voidItem();
}
@WSReceiver(code = "sendCurrentScore", permission = PermLevel.TABLE)
public Uni<Void> sendCurrentScore(WebSocketConnection connection, ScoreState scoreState) {
TableState tableState = tableStates.get(connection);
if (tableState != null) {
tableState.setScoreState(scoreState);
SSState.sendStateFull(connection, tableState);
}
return Uni.createFrom().voidItem();
}
public void removeConnection(WebSocketConnection connection) {
if (tableStates.containsKey(connection)) {
SSState.sendRmStateFull(connection, tableStates.get(connection).getId());
tableStates.remove(connection);
}
}
public void setMatchEnd(WebSocketConnection connection, RMatch.MatchEnd matchEnd) {
if (tableStates.containsKey(connection)) {
TableState tableState = tableStates.get(connection);
if (matchEnd.end())
tableState.setState(MatchState.ENDED);
else
tableState.setState(MatchState.IN_PROGRESS);
SSState.sendStateFull(connection, tableState);
}
}
@RegisterForReflection
public record ChronoState(long time, long startTime, long configTime, long configPause, int state) {
public boolean isRunning() {
return startTime != 0 || state != 0;
}
}
@RegisterForReflection
public record ScoreState(int scoreRouge, int scoreBleu) {
}
@Data
@RegisterForReflection
public static class TableState {
UUID id = UUID.randomUUID();
String competitionUuid;
Long selectedCategory;
Long selectedMatch;
ChronoState chronoState;
ScoreState scoreState;
String liceName = "???";
MatchState state = MatchState.NOT_STARTED;
}
public enum MatchState {
NOT_STARTED,
IN_PROGRESS,
ENDED
}
}

View File

@@ -0,0 +1,137 @@
package fr.titionfire.ffsaf.ws.recv;
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
import fr.titionfire.ffsaf.data.model.RegisterModel;
import fr.titionfire.ffsaf.data.repository.CompetitionGuestRepository;
import fr.titionfire.ffsaf.data.repository.CompetitionRepository;
import fr.titionfire.ffsaf.data.repository.RegisterRepository;
import fr.titionfire.ffsaf.domain.entity.CombEntity;
import fr.titionfire.ffsaf.domain.service.TradService;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.Genre;
import fr.titionfire.ffsaf.utils.Pair;
import fr.titionfire.ffsaf.ws.PermLevel;
import fr.titionfire.ffsaf.ws.send.SSRegister;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.runtime.annotations.RegisterForReflection;
import io.quarkus.websockets.next.WebSocketConnection;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@WithSession
@ApplicationScoped
@RegisterForReflection
public class RTeam {
@Inject
TradService trad;
@Inject
CompetitionRepository competitionRepository;
@Inject
RegisterRepository registerRepository;
@Inject
CompetitionGuestRepository competitionGuestRepository;
@WSReceiver(code = "setTeam", permission = PermLevel.ADMIN)
public Uni<CombEntity> setTeam(WebSocketConnection connection, TeamData data) {
return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
.chain(cm -> registerRepository.list("membre.id IN ?1 AND competition = ?2",
data.members.stream().filter(id -> id >= 0).toList(), cm)
.chain(l -> competitionGuestRepository.list("id IN ?1",
data.members.stream().filter(id -> id < 0).map(i -> i * -1).toList())
.map(l2 -> new Pair<>(l, l2)))
.chain(pair ->
competitionGuestRepository.find("fname = ?1 AND lname = ?2 AND competition = ?3",
data.name, "__team", cm).firstResult()
.chain(team -> {
if (pair.getKey().isEmpty() && pair.getValue().isEmpty()) {
if (team != null) {
CompetitionGuestModel finalTeam1 = team;
SSRegister.sendRegisterRemove(connection, finalTeam1.getId() * -1);
return Panache.withTransaction(
() -> competitionGuestRepository.delete(finalTeam1))
.replaceWith((CombEntity) null);
} else
return Uni.createFrom().item((CombEntity) null);
}
if (team == null) {
// Create new team
team = new CompetitionGuestModel();
team.setFname(data.name);
team.setLname("__team");
team.setCompetition(cm);
team.setClub("Team");
team.setGenre(Genre.NA);
} else {
team.getComb().clear();
team.getGuest().clear();
}
team.setCategorie(Stream.concat(
pair.getKey().stream().map(RegisterModel::getCategorie2),
pair.getValue().stream().map(CompetitionGuestModel::getCategorie))
.map(Enum::ordinal)
.max(Integer::compareTo)
.map(i -> Categorie.values()[i]).orElse(Categorie.SENIOR1));
List<Float> s = Stream.concat(
pair.getKey().stream().map(RegisterModel::getWeight),
pair.getValue().stream().map(CompetitionGuestModel::getWeight))
.filter(Objects::nonNull).toList();
if (s.isEmpty()) {
team.setWeight(null);
} else if (s.size() == 1) {
team.setWeight(s.get(0));
} else {
team.setWeight((float) s.stream().mapToDouble(Float::doubleValue)
.average()
.orElse(0));
}
team.setCountry(Stream.concat(
pair.getKey().stream().map(m -> m.getMembre().getCountry()),
pair.getValue().stream().map(CompetitionGuestModel::getCountry))
.filter(Objects::nonNull)
.map(String::toUpperCase)
.collect(Collectors.groupingBy(
e -> e, // Classer par élément
Collectors.counting() // Compter les occurrences
))
.entrySet()
.stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse("FR"));
team.getComb().addAll(
pair.getKey().stream().map(RegisterModel::getMembre).toList());
team.getGuest().addAll(pair.getValue());
CompetitionGuestModel finalTeam = team;
return Panache.withTransaction(
() -> competitionGuestRepository.persistAndFlush(finalTeam))
.map(CombEntity::fromModel);
}))
)
.invoke(combEntity -> {
if (combEntity != null)
SSRegister.sendRegister(connection, combEntity);
});
}
@RegisterForReflection
public record TeamData(List<Long> members, String name) {
}
}

View File

@@ -3,6 +3,7 @@ package fr.titionfire.ffsaf.ws.send;
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel; import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
import fr.titionfire.ffsaf.data.model.RegisterModel; import fr.titionfire.ffsaf.data.model.RegisterModel;
import fr.titionfire.ffsaf.domain.entity.CombEntity; import fr.titionfire.ffsaf.domain.entity.CombEntity;
import fr.titionfire.ffsaf.domain.service.CardService;
import fr.titionfire.ffsaf.net2.MessageType; import fr.titionfire.ffsaf.net2.MessageType;
import fr.titionfire.ffsaf.ws.CompetitionWS; import fr.titionfire.ffsaf.ws.CompetitionWS;
import fr.titionfire.ffsaf.ws.MessageOut; import fr.titionfire.ffsaf.ws.MessageOut;
@@ -13,6 +14,7 @@ import io.quarkus.websockets.next.UserData;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import org.hibernate.reactive.mutiny.Mutiny;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@@ -25,22 +27,54 @@ public class SRegister {
@Inject @Inject
OpenConnections connections; OpenConnections connections;
@Inject
CardService cardService;
public Uni<Void> sendRegister(String uuid, RegisterModel registerModel) { public Uni<Void> sendRegister(String uuid, RegisterModel registerModel) {
return send(uuid, "sendRegister", CombEntity.fromModel(registerModel)); return Mutiny.fetch(registerModel.getCategoriesInscrites()).chain(o ->
send(uuid, "sendRegister", CombEntity.fromModel(registerModel).addCategoriesInscrites(o))
.call(__ -> registerModel.getClub2() == null ? Uni.createFrom().voidItem() :
cardService.addTeamCartToNewComb(registerModel.getMembre().getId(),
registerModel.getClub2().getClubId(), registerModel.getClub2().getName(),
registerModel.getCompetition())
.chain(cardModels -> send(uuid, "sendCards", cardModels))));
}
public Uni<Void> sendRegisterNoFetch(String uuid, RegisterModel registerModel) {
return send(uuid, "sendRegister",
CombEntity.fromModel(registerModel).addCategoriesInscrites(registerModel.getCategoriesInscrites()))
.call(__ -> registerModel.getClub2() == null ? Uni.createFrom().voidItem() :
cardService.addTeamCartToNewComb(registerModel.getMembre().getId(),
registerModel.getClub2().getClubId(), registerModel.getClub2().getName(),
registerModel.getCompetition())
.chain(cardModels -> send(uuid, "sendCards", cardModels)));
} }
public Uni<Void> sendRegister(String uuid, CompetitionGuestModel model) { public Uni<Void> sendRegister(String uuid, CompetitionGuestModel model) {
return send(uuid, "sendRegister", CombEntity.fromModel(model)); return Mutiny.fetch(model.getCategoriesInscrites()).chain(o ->
send(uuid, "sendRegister", CombEntity.fromModel(model).addCategoriesInscrites(o))
.call(__ -> cardService.addTeamCartToNewComb(model.getId() * -1,
null, model.getClub(), model.getCompetition())
.chain(cardModels -> send(uuid, "sendCards", cardModels))));
}
public Uni<Void> sendRegisterNoFetch(String uuid, CompetitionGuestModel model) {
return send(uuid, "sendRegister",
CombEntity.fromModel(model).addCategoriesInscrites(model.getCategoriesInscrites()))
.call(__ -> cardService.addTeamCartToNewComb(model.getId() * -1,
null, model.getClub(), model.getCompetition())
.chain(cardModels -> send(uuid, "sendCards", cardModels)));
} }
public Uni<Void> sendRegisterRemove(String uuid, Long combId) { public Uni<Void> sendRegisterRemove(String uuid, Long combId) {
return send(uuid, "sendRegisterRemove", combId); return send(uuid, "sendRegisterRemove", combId)
.call(__ -> cardService.rmTeamCardFromComb(combId, uuid));
} }
public Uni<Void> send(String uuid, String code, Object data) { public Uni<Void> send(String uuid, String code, Object data) {
List<Uni<Void>> queue = connections.findByEndpointId(CompetitionWS.class.getCanonicalName()).stream() List<Uni<Void>> queue = connections.findByEndpointId(CompetitionWS.class.getCanonicalName()).stream()
.filter(c -> c.pathParam("uuid").equals(uuid) && PermLevel.valueOf( .filter(c -> c.pathParam("uuid").equals(uuid) && PermLevel.valueOf(
c.userData().get(UserData.TypedKey.forString("prem"))).ordinal() >= PermLevel.ADMIN.ordinal()) c.userData().get(UserData.TypedKey.forString("prem"))).ordinal() >= PermLevel.TABLE.ordinal())
.map(c -> c.sendText( .map(c -> c.sendText(
new MessageOut(UUID.randomUUID(), code, MessageType.NOTIFY, data))) new MessageOut(UUID.randomUUID(), code, MessageType.NOTIFY, data)))
.toList(); .toList();

View File

@@ -0,0 +1,27 @@
package fr.titionfire.ffsaf.ws.send;
import fr.titionfire.ffsaf.data.model.CardModel;
import fr.titionfire.ffsaf.ws.CompetitionWS;
import fr.titionfire.ffsaf.ws.recv.RCard;
import io.quarkus.websockets.next.WebSocketConnection;
import java.util.List;
public class SSCard {
public static void sendCards(WebSocketConnection connection, List<CardModel> cardModel) {
CompetitionWS.sendNotifyToOtherEditor(connection, "sendCards", cardModel);
}
public static void sendRmCards(WebSocketConnection connection, List<Long> ids) {
CompetitionWS.sendNotifyToOtherEditor(connection, "rmCards", ids);
}
public static void sendTeamCard(WebSocketConnection connection, RCard.SendTeamCards teamCards) {
CompetitionWS.sendNotifyToOtherEditor(connection, "sendTeamCard", teamCards);
}
public static void rmTeamCard(WebSocketConnection connection, RCard.SendTeamCards teamCards) {
CompetitionWS.sendNotifyToOtherEditor(connection, "rmTeamCard", teamCards);
}
}

Some files were not shown because too many files have changed in this diff Show More