198 Commits

Author SHA1 Message Date
f968d5d03b Merge pull request 'don't notify cancelled trips' (#713) from dont-notify-cancelled-trips into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m46s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 8m14s
Reviewed-on: #713
2024-08-27 09:25:16 +02:00
a52ee97a80 don't notify cancelled trips
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-08-27 09:24:21 +02:00
0b46cbf8db Merge pull request 'allow retroactively adding logbook entrie' (#711) from allow-retro-logbookentry into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m52s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 8m12s
Reviewed-on: #711
2024-08-23 12:26:45 +02:00
be6d3229a4 allow retroactively adding logbook entrie
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-08-23 12:26:08 +02:00
fdd9c3bdff Merge pull request 'allow vorstand to see fees' (#709) from fix-permission into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m50s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 8m21s
Reviewed-on: #709
2024-08-22 20:55:22 +02:00
ae6c129fd3 allow vorstand to see fees
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-08-22 20:54:27 +02:00
396aa204a4 Merge pull request 'rsync was a bad idea' (#707) from reduce-deployment-time into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m51s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 8m18s
Reviewed-on: #707
2024-08-21 17:40:35 +02:00
bbe4949203 rsync was a bad idea
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-08-21 17:39:43 +02:00
94130f9230 Merge pull request 'use rsync instead of scp' (#705) from reduce-deployment-time into main
Some checks failed
CI/CD Pipeline / test (push) Successful in 10m52s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #705
2024-08-21 17:09:04 +02:00
010627c91d use rsync instead of scp
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-08-21 17:07:44 +02:00
36276e5415 verify, that boat is not on water on adding log entry; Fixes #625 (#697)
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #697
2024-08-21 17:05:41 +02:00
4bb0e54635 Merge pull request 'update' (#703) from update into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m19s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m50s
Reviewed-on: #703
2024-08-21 16:35:46 +02:00
a518023892 Merge branch 'main' into staging
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-08-21 16:34:13 +02:00
3f06e91e24 Merge pull request 'fix price' (#701) from fix-price into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #701
2024-08-21 16:29:00 +02:00
2b4345ba77 fix price
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-08-21 16:28:13 +02:00
412c45a9df Merge pull request 'care-about-einschreibgebuehr' (#699) from care-about-einschreibgebuehr into main
Some checks are pending
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 10m23s
Reviewed-on: #699
2024-08-21 16:17:52 +02:00
d971c1504c clippy
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-08-21 16:16:07 +02:00
cf9b79e56e care about einschreibgebuehr
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-08-21 16:14:54 +02:00
5d01d18e70 Merge pull request 'nag, if a long logentry is entered w/o trip_type; Fixes #448' (#695) from trip-type-nag into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m55s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m52s
Reviewed-on: #695
2024-08-19 14:36:59 +02:00
1e96a2d6e1 Merge pull request 'trip-type-nag' (#694) from trip-type-nag into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m5s
CI/CD Pipeline / deploy-staging (push) Successful in 8m6s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #694
2024-08-19 14:36:53 +02:00
ff81ab0246 Merge pull request 'notify-on-always-show-events' (#693) from notify-on-always-show-events into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #693
2024-08-19 14:14:21 +02:00
202e128c98 Merge pull request 'notify-on-always-show-events' (#692) from notify-on-always-show-events into staging
Some checks are pending
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 9m6s
Reviewed-on: #692
2024-08-19 14:14:14 +02:00
ecb347c204 nag, if a long logentry is entered w/o trip_type; Fixes #448
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m6s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-08-19 14:06:00 +02:00
2bc426be52 Merge pull request 'allow to edit users; Fixes #688' (#689) from allow-user-edit-role into main
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #689
2024-08-19 14:04:39 +02:00
0a130709c7 Merge pull request 'allow-user-edit-role' (#690) from allow-user-edit-role into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #690
2024-08-19 14:04:35 +02:00
6e8a5927a6 create notification if a new event with 'always_show' is updated
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m22s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-08-19 13:50:08 +02:00
a31bacb3e1 create notification if a new event with 'always_show' is entered 2024-08-19 13:39:22 +02:00
93c8316543 allow to edit users; Fixes #688
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m25s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-08-19 13:23:08 +02:00
6e72e2a753 Merge pull request 'board-scheckbook' (#687) from board-scheckbook into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m56s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m20s
Reviewed-on: #687
2024-08-19 12:13:05 +02:00
c847c3300f Merge pull request 'board-scheckbook' (#686) from board-scheckbook into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m0s
CI/CD Pipeline / deploy-staging (push) Successful in 6m40s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #686
2024-08-19 12:12:59 +02:00
116c7523d2 styling
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m21s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-08-19 11:57:51 +02:00
eb15421d08 allow move from schnuppern to scheckbuch
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-08-19 11:53:28 +02:00
818cf0d40b Merge branch 'main' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt
Some checks are pending
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 9m13s
2024-08-19 11:27:39 +02:00
ed9d93410e allow vorstand to add scheckbücher
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-08-19 11:27:10 +02:00
c162e0a66f Merge pull request 'kassier-role' (#685) from kassier-role into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m13s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m32s
Reviewed-on: #685
2024-08-19 11:24:10 +02:00
e965d33a7b Merge pull request 'kassier-role' (#684) from kassier-role into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m3s
CI/CD Pipeline / deploy-staging (push) Successful in 6m45s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #684
2024-08-19 11:24:07 +02:00
3d77a2325c allow kassier to change payment stuff
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m9s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-08-19 10:57:02 +02:00
afb6af8ece only one '+' role necessary + introduction of 'kassier' role
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-08-19 10:51:50 +02:00
799e94a50f switch to macro for special user
Some checks failed
CI/CD Pipeline / test (push) Failing after 15m6s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-08-19 10:34:37 +02:00
c41dc0853a Merge pull request 'maybe unpublishing a post works now?' (#683) from fix into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m3s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m33s
Reviewed-on: #683
2024-08-19 09:55:46 +02:00
74edcfa119 Merge pull request 'fix' (#682) from fix into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m11s
CI/CD Pipeline / deploy-staging (push) Successful in 6m44s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #682
2024-08-19 09:55:45 +02:00
cc7bd3a416 maybe unpublishing a post works now?
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-08-19 09:55:01 +02:00
bfee85a963 Merge pull request 'send-blogpost-notification' (#680) from send-blogpost-notification into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m1s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m21s
Reviewed-on: #680
2024-08-18 22:24:14 +02:00
f55f45d960 Merge pull request 'send-blogpost-notification' (#681) from send-blogpost-notification into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m8s
CI/CD Pipeline / deploy-staging (push) Successful in 6m56s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #681
2024-08-18 22:24:06 +02:00
c68593a67d proper docs; Fixes #645
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-08-18 22:23:08 +02:00
20da86f69e delete notification again, if article is unpublished
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-08-18 22:21:36 +02:00
8588e1f71b Merge pull request 'send-blogpost-notification' (#679) from send-blogpost-notification into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m38s
CI/CD Pipeline / deploy-staging (push) Successful in 7m54s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #679
2024-08-18 21:58:31 +02:00
8efb3aea2c push
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-08-18 21:57:20 +02:00
83aa9bc84c allow for article_titles 2024-08-18 21:46:11 +02:00
6171bb0f85 comment wordpress changes 2024-08-18 21:37:53 +02:00
e040764902 Merge pull request 'first draft of sending blog post notifications' (#678) from send-blogpost-notification into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m12s
CI/CD Pipeline / deploy-staging (push) Successful in 6m54s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #678
2024-08-18 21:33:36 +02:00
eeab4c167b switch to new pw 2024-08-18 21:32:34 +02:00
25161fc8e9 first draft of sending blog post notifications
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-08-18 21:30:14 +02:00
dea53d8396 Merge pull request 'default-dest-table' (#677) from default-dest-table into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m9s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m42s
Reviewed-on: #677
2024-08-18 20:53:10 +02:00
80ac131fb2 Merge pull request 'default-dest-table' (#676) from default-dest-table into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m0s
CI/CD Pipeline / deploy-staging (push) Successful in 6m30s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #676
2024-08-18 20:53:06 +02:00
8e65a6540d fix tests
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m13s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-08-18 20:43:45 +02:00
d7e5731753 order by most used destination
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-08-18 20:34:55 +02:00
1a4d5ac569 create own default_destination table to remove clutter; Fixes #646
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-08-18 20:21:59 +02:00
668fc5e295 Merge pull request 'Keep selected rowers during boat change, Fixes #48' (#675) from keep-rowers-on-boatchange into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m58s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m37s
Reviewed-on: #675
2024-08-15 15:51:12 +02:00
4f9778eccf Merge pull request 'keep-rowers-on-boatchange' (#674) from keep-rowers-on-boatchange into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m9s
CI/CD Pipeline / deploy-staging (push) Successful in 6m42s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #674
2024-08-15 15:51:08 +02:00
09d4c5d958 Keep selected rowers during boat change, Fixes #48
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m40s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-08-15 15:42:06 +02:00
11dd978135 Merge pull request 'fix' (#673) from quick-add-people-from-planned-trip into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m58s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m50s
Reviewed-on: #673
2024-08-14 07:36:44 +02:00
d7d0a3fedd Merge pull request 'fix' (#672) from quick-add-people-from-planned-trip into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m6s
CI/CD Pipeline / deploy-staging (push) Successful in 6m57s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #672
2024-08-14 07:36:40 +02:00
e4333a05d7 fix
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-08-14 07:35:46 +02:00
f687e18195 Merge pull request 'be able to auto populate people from planned trip' (#671) from quick-add-people-from-planned-trip into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m54s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m30s
Reviewed-on: #671
2024-08-13 23:11:00 +02:00
a4f72d746c Merge pull request 'quick-add-people-from-planned-trip' (#670) from quick-add-people-from-planned-trip into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m2s
CI/CD Pipeline / deploy-staging (push) Successful in 6m50s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #670
2024-08-13 23:10:53 +02:00
e55f380c4f be able to auto populate people from planned trip
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-08-13 23:02:34 +02:00
bb502a4561 Merge pull request 'fix typo' (#669) from typo into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m9s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m38s
Reviewed-on: #669
2024-08-13 15:38:07 +02:00
1340639f91 Merge pull request 'typo' (#668) from typo into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m29s
CI/CD Pipeline / deploy-staging (push) Successful in 6m44s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #668
2024-08-13 15:37:59 +02:00
ce28c95853 fix typo
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-08-13 15:37:23 +02:00
2c3f69a562 Merge pull request 'allow admins to delete logbook entries' (#666) from allow-admin-to-delete-logbook-entries into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m16s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 19m44s
Reviewed-on: #666
2024-08-12 20:56:11 +02:00
0bf7094770 Merge pull request 'allow-admin-to-delete-logbook-entries' (#665) from allow-admin-to-delete-logbook-entries into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 19m9s
CI/CD Pipeline / deploy-staging (push) Successful in 20m15s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #665
2024-08-12 20:56:06 +02:00
a75c892cfb allow admins to delete logbook entries
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-08-12 20:55:31 +02:00
f71ab634d7 Merge pull request 'update people who are responsible for fingerprint access, reduce bus factor' (#664) from update-fingerprint-responsible-people into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m5s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m19s
Reviewed-on: #664
2024-07-30 23:29:32 +02:00
0c770f6ddc Merge pull request 'update-fingerprint-responsible-people' (#663) from update-fingerprint-responsible-people into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m52s
CI/CD Pipeline / deploy-staging (push) Successful in 7m27s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #663
2024-07-30 23:29:27 +02:00
6b71449183 update people who are responsible for fingerprint access, reduce bus factor
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-07-30 23:28:52 +02:00
d59b3f4345 Merge pull request 'push' (#662) from add-notification into main
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #662
2024-07-30 23:27:16 +02:00
7e41cd3f73 Merge pull request 'add-notification' (#661) from add-notification into staging
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #661
2024-07-30 23:27:11 +02:00
2a8c339dcd push
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-07-30 23:24:46 +02:00
0dd10e1dd6 Merge pull request 'extend filter' (#660) from add-filter into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m18s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m30s
Reviewed-on: #660
2024-07-29 14:27:22 +02:00
2d2e44126a Merge pull request 'add-filter' (#659) from add-filter into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m21s
CI/CD Pipeline / deploy-staging (push) Successful in 7m3s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #659
2024-07-29 14:27:01 +02:00
def8028446 extend filter
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-07-29 14:26:26 +02:00
db318c23cd Merge pull request 'push' (#658) from fix into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m47s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m17s
Reviewed-on: #658
2024-07-28 07:44:42 +02:00
4555391dd3 Merge pull request 'push' (#657) from fix into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m35s
CI/CD Pipeline / deploy-staging (push) Successful in 7m28s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #657
2024-07-28 07:44:37 +02:00
23aa6aa0f8 push
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-07-28 07:43:56 +02:00
a682d1e6ce Merge pull request 'ext person can also be shipmaster (and not cox)' (#656) from fix into main
Some checks failed
CI/CD Pipeline / test (push) Failing after 1m48s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #656
2024-07-28 07:38:06 +02:00
8aca437eb3 Merge pull request 'fix' (#655) from fix into staging
Some checks failed
CI/CD Pipeline / test (push) Failing after 1m43s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #655
2024-07-28 07:37:57 +02:00
cd1bf12e68 ext person can also be shipmaster (and not cox)
Some checks failed
CI/CD Pipeline / test (push) Failing after 1m41s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-07-28 07:36:57 +02:00
5f7591f52a Merge pull request 'allow external cox; Fix #650' (#654) from external-cox into main
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #654
2024-07-28 07:31:02 +02:00
127d9784ad Merge pull request 'external-cox' (#653) from external-cox into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #653
2024-07-28 07:30:59 +02:00
bf4ea502d3 allow external cox; Fix #650
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-07-28 07:29:44 +02:00
c13dfdaa77 Merge pull request 'fix spacing in roles' (#652) from fix-spacing into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #652
2024-07-28 07:14:29 +02:00
26aa222bc6 Merge pull request 'fix-spacing' (#651) from fix-spacing into staging
Some checks are pending
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 10m51s
Reviewed-on: #651
2024-07-28 07:14:07 +02:00
0bc9e11b9a fix spacing in roles
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-07-28 07:13:21 +02:00
2fdcab9030 Merge pull request 'allow instand logbook add' (#649) from fix-instand-add into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m28s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 19m49s
Reviewed-on: #649
2024-07-27 20:49:30 +02:00
7689a39ac5 Merge pull request 'allow instand logbook add' (#648) from fix-instand-add into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m26s
CI/CD Pipeline / deploy-staging (push) Successful in 6m43s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #648
2024-07-27 20:49:22 +02:00
b43682ac39 allow instand logbook add
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-07-27 20:48:32 +02:00
8d7a1c707d Merge pull request 'Merge pull request 'don't allow to finalize a logbook entry more than once' (#644) from only-allow-finalizing-logbook-once into main' (#647) from fix-instand-add into staging
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #647
2024-07-27 20:47:00 +02:00
958dda9f52 Merge pull request 'don't allow to finalize a logbook entry more than once' (#644) from only-allow-finalizing-logbook-once into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #644
2024-07-26 10:34:57 +02:00
1eea8c9662 Merge pull request 'only-allow-finalizing-logbook-once' (#643) from only-allow-finalizing-logbook-once into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m2s
CI/CD Pipeline / deploy-staging (push) Successful in 5m35s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #643
2024-07-26 10:34:51 +02:00
b4b922222c don't allow to finalize a logbook entry more than once
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-07-26 10:34:14 +02:00
84e76e8d65 Merge pull request 'one more error fix :-)' (#642) from fix into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m26s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m31s
Reviewed-on: #642
2024-07-24 09:25:19 +02:00
bdfcc6bc0a Merge pull request 'one more error fix :-)' (#641) from fix into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m2s
CI/CD Pipeline / deploy-staging (push) Successful in 5m37s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #641
2024-07-24 09:25:10 +02:00
afa88b9529 one more error fix :-)
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-07-24 09:24:26 +02:00
4229a4e021 Merge pull request 'use proper role name' (#640) from fix into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m30s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m4s
Reviewed-on: #640
2024-07-24 08:46:40 +02:00
6cd555298d Merge pull request 'fix' (#639) from fix into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m39s
CI/CD Pipeline / deploy-staging (push) Successful in 5m34s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #639
2024-07-24 08:46:34 +02:00
f6207e2994 use proper role name
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-07-24 08:45:52 +02:00
4da996251a Merge pull request 'better logs' (#637) from update-logbook-entries into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m43s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m13s
Reviewed-on: #637
2024-07-23 15:15:16 +02:00
c44c0d8505 Merge pull request 'better logs' (#636) from update-logbook-entries into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m43s
CI/CD Pipeline / deploy-staging (push) Successful in 5m44s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #636
2024-07-23 15:14:50 +02:00
aa9568f326 better logs
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-07-23 15:13:36 +02:00
1db09cd8ac Merge pull request 'update logbook entries' (#634) from update-logbook-entries into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #634
2024-07-23 15:04:52 +02:00
59ef93d6fa Merge pull request 'update-logbook-entries' (#633) from update-logbook-entries into staging
Some checks are pending
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 9m40s
Reviewed-on: #633
2024-07-23 15:04:47 +02:00
4a3ee5b551 update logbook entries
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-07-23 14:57:09 +02:00
c73b3e94c3 Merge pull request 'new text to make clear who's responsible for fee payment' (#632) from new-text into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m44s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m56s
Reviewed-on: #632
2024-07-23 08:59:05 +02:00
4969a0d90a Merge pull request 'new-text' (#631) from new-text into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m39s
CI/CD Pipeline / deploy-staging (push) Successful in 5m40s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #631
2024-07-23 08:58:59 +02:00
3efcd99bbc new text to make clear who's responsible for fee payment
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-07-23 08:57:43 +02:00
4f0f509ad6 Merge pull request 'show halfprice for member fees if entry_year == current_year && start_date >= 1.7. Fixes #616' (#629) from halfprice-fee into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m16s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m33s
Reviewed-on: #629
2024-07-22 21:59:36 +02:00
8112f1ed2a Merge pull request 'fix empty but non-null date entries' (#630) from halfprice-fee into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m11s
CI/CD Pipeline / deploy-staging (push) Successful in 6m21s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #630
2024-07-22 21:57:30 +02:00
b1252e8d5c fix empty but non-null date entries
Some checks failed
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-07-22 21:56:47 +02:00
9cb9cfe2a1 Merge pull request 'show halfprice for member fees if entry_year == current_year && start_date >= 1.7. Fixes #616' (#628) from halfprice-fee into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m20s
CI/CD Pipeline / deploy-staging (push) Successful in 6m23s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #628
2024-07-22 21:36:05 +02:00
a62fd116ea show halfprice for member fees if entry_year == current_year && start_date >= 1.7. Fixes #616
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-07-22 21:35:07 +02:00
622bc700f3 Merge pull request '[TASK] quick restructure of user screen' (#607) from restructure-user into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m57s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m59s
Reviewed-on: #607
2024-07-16 17:39:05 +02:00
2540a3dc7c Merge pull request 'format tera files' (#627) from restructure-user into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m5s
CI/CD Pipeline / deploy-staging (push) Successful in 5m49s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #627
2024-07-16 17:38:48 +02:00
0e5fd25e61 format tera files
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m42s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-07-16 16:38:12 +01:00
72b86d4dad Merge pull request 'restructure-user' (#626) from restructure-user into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #626
2024-07-16 17:32:38 +02:00
16fbeea81b [BUGFIX] only-event.png
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m25s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-07-16 14:38:27 +02:00
bd6fbe772e [BUGFIX] vorstand-no-admin.png
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-07-16 14:32:40 +02:00
647970e1fc Merge pull request 'fix-steering-only-boat-logentry' (#624) from fix-steering-only-boat-logentry into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m18s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m33s
Reviewed-on: #624
2024-07-13 21:16:05 +02:00
1e1c1bb6d9 Merge pull request 'fix-steering-only-boat-logentry' (#623) from fix-steering-only-boat-logentry into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m5s
CI/CD Pipeline / deploy-staging (push) Successful in 6m14s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #623
2024-07-13 21:14:28 +02:00
088fe98995 fix test
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m1s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-07-13 20:03:10 +01:00
4237fafdff fix error, where log entries can't be added with boats with only steering
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-07-13 19:58:13 +01:00
6b24008c17 Merge pull request 'more robust data fetching' (#618) from more-robust-data-fetching into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m40s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m10s
Reviewed-on: #618
2024-07-10 09:45:15 +02:00
4fbd3c7717 Merge pull request 'more robust data fetching' (#617) from more-robust-data-fetching into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 17m20s
CI/CD Pipeline / deploy-staging (push) Successful in 18m57s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #617
2024-07-10 09:45:01 +02:00
ce8a095b31 more robust data fetching
All checks were successful
CI/CD Pipeline / test (push) Successful in 18m28s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-07-10 09:44:38 +02:00
6c191cf59e Merge pull request 'fix term' (#615) from fix-term into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m54s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m36s
Reviewed-on: #615
2024-06-27 17:11:15 +02:00
b0698e70a4 Merge pull request 'fix term' (#614) from fix-term into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m26s
CI/CD Pipeline / deploy-staging (push) Successful in 6m22s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #614
2024-06-27 17:10:53 +02:00
6f7283f754 fix term
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m17s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-06-27 17:10:15 +02:00
323f721fc0 Merge pull request 'order logbook by arrival' (#612) from order-logbook-by-arrival into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m21s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 17m57s
Reviewed-on: #612
2024-06-27 08:52:22 +02:00
d0bcf1f384 Merge pull request 'order logbook by arrival' (#611) from order-logbook-by-arrival into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m26s
CI/CD Pipeline / deploy-staging (push) Successful in 18m9s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #611
2024-06-27 08:52:17 +02:00
bd7cd0020e order logbook by arrival
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-06-27 08:51:44 +02:00
22bfe48d18 Merge pull request 'update deps' (#610) from update-deps into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #610
2024-06-27 07:59:27 +02:00
c379a6ca79 Merge pull request 'update deps' (#609) from update-deps into staging
Some checks are pending
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 20m12s
Reviewed-on: #609
2024-06-27 07:59:15 +02:00
3543ffe9e1 update deps
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-06-27 07:58:30 +02:00
1d6770f11b Merge pull request 'minor visual improvements' (#608) from restructure-user into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m58s
CI/CD Pipeline / deploy-staging (push) Successful in 8m39s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #608
2024-06-24 19:57:08 +02:00
1ad6509568 minor visual improvements
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m6s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-06-24 19:56:35 +02:00
705f2ddc52 Merge pull request 'restructure-user' (#606) from restructure-user into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m12s
CI/CD Pipeline / deploy-staging (push) Successful in 8m30s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #606
2024-06-24 17:46:13 +02:00
dba1e08c5d [TASK] quick restructure of user screen
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m26s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-06-24 16:58:19 +02:00
1dc0c9c0e1 Merge pull request 'update iban' (#605) from update-iban into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m52s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 8m28s
Reviewed-on: #605
2024-06-24 15:03:54 +02:00
45004567ed Merge pull request 'update-iban' (#604) from update-iban into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m44s
CI/CD Pipeline / deploy-staging (push) Successful in 8m4s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #604
2024-06-24 15:03:47 +02:00
3dff956544 update iban
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-06-24 15:02:55 +02:00
79f8efc34b Merge pull request 'allow vorstand to send mail + notifications' (#603) from allow-vorstand-to-send-things into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m5s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m27s
Reviewed-on: #603
2024-06-21 11:25:27 +02:00
5c31fac230 Merge pull request 'allow vorstand to send mail + notifications' (#602) from allow-vorstand-to-send-things into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m12s
CI/CD Pipeline / deploy-staging (push) Successful in 6m2s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #602
2024-06-21 11:25:09 +02:00
3e983e05f9 allow vorstand to send mail + notifications
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-06-21 11:24:14 +02:00
8f91cc4e88 Merge pull request 'allow vorstand to send mail + notifications' (#601) from allow-vorstand-to-send-things into main
Some checks failed
CI/CD Pipeline / test (push) Failing after 1m36s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #601
2024-06-21 11:15:37 +02:00
c55f9743aa Merge pull request 'allow-vorstand-to-send-things' (#600) from allow-vorstand-to-send-things into staging
Some checks failed
CI/CD Pipeline / test (push) Failing after 1m40s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #600
2024-06-21 11:15:30 +02:00
76290a64ae allow vorstand to send mail + notifications
Some checks failed
CI/CD Pipeline / test (push) Failing after 1m50s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-06-21 11:14:48 +02:00
4b48fbaa82 Merge pull request 'update deps' (#599) from update-deps into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m39s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 18m11s
Reviewed-on: #599
2024-06-19 13:44:08 +02:00
d25cd491d0 Merge pull request 'update deps' (#598) from update-deps into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 18m4s
CI/CD Pipeline / deploy-staging (push) Successful in 18m15s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #598
2024-06-19 13:44:07 +02:00
def8affb5f update deps
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-06-19 13:43:04 +02:00
dfa7be9928 Merge pull request 'fix migration' (#597) from fix-migration into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m13s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m26s
Reviewed-on: #597
2024-06-16 20:15:58 +02:00
03a467270d Merge pull request 'fix migration' (#596) from fix-migration into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m38s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #596
2024-06-16 20:15:54 +02:00
1c04462c30 Merge pull request 'add more life to the mails :-)' (#595) from more-sympatic-welcome-mail into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m11s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m40s
Reviewed-on: #595
2024-06-15 18:28:24 +02:00
7af53203f8 Merge pull request 'more-sympatic-welcome-mail' (#594) from more-sympatic-welcome-mail into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m18s
CI/CD Pipeline / deploy-staging (push) Successful in 6m32s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #594
2024-06-15 18:28:09 +02:00
b2393eb6ec mb is too serious
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-06-15 18:27:09 +02:00
a5e82851ba fix missing word
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m41s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-06-10 22:23:26 +02:00
80725e223b add more life to the mails :-)
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-06-10 22:17:41 +02:00
70be6726db Merge pull request 'add-wifi-pw-new-members' (#592) from add-wifi-pw-new-members into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m35s
CI/CD Pipeline / deploy-staging (push) Successful in 5m44s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #592
2024-06-10 22:08:10 +02:00
08fc324cc6 Merge pull request 'require-necessary-fields' (#590) from require-necessary-fields into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m51s
CI/CD Pipeline / deploy-staging (push) Successful in 5m45s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #590
2024-06-10 20:59:38 +02:00
df007524ed Merge pull request 'merged :-)' (#589) from trailer-reservation into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m1s
CI/CD Pipeline / deploy-staging (push) Successful in 6m41s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #589
2024-06-10 20:00:07 +02:00
5720767af3 Merge pull request 'add trailer reservation funcitonality; Fixes #443' (#587) from trailer-reservation into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #587
2024-06-10 19:58:13 +02:00
1215bdbd84 Merge pull request 'delte-trip-btn' (#585) from delte-trip-btn into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m5s
CI/CD Pipeline / deploy-staging (push) Successful in 5m37s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #585
2024-06-10 19:08:17 +02:00
734490efe7 Merge pull request 'use proper hydro license; add fluctuation' (#583) from proper-hydro into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m18s
CI/CD Pipeline / deploy-staging (push) Successful in 5m35s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #583
2024-06-10 15:49:43 +02:00
980fcc0c0c Merge pull request 'thousand-km-trips' (#581) from thousand-km-trips into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m34s
CI/CD Pipeline / deploy-staging (push) Successful in 6m15s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #581
2024-06-10 15:12:57 +02:00
84f23e6e55 Merge pull request 'fix boat select' (#579) from fix-boat-select into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m42s
CI/CD Pipeline / deploy-staging (push) Successful in 5m22s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #579
2024-06-10 11:01:39 +02:00
36193e3a64 Merge pull request 'cleaner cal' (#577) from cleaner-cal into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m1s
CI/CD Pipeline / deploy-staging (push) Successful in 5m28s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #577
2024-06-06 17:22:13 +02:00
c6e3458588 Merge pull request 'add-badge' (#575) from add-badge into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m55s
CI/CD Pipeline / deploy-staging (push) Successful in 18m13s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #575
2024-06-06 10:48:30 +02:00
a0528c1c65 Merge pull request 'nicer-cal' (#573) from nicer-cal into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m22s
CI/CD Pipeline / deploy-staging (push) Successful in 7m19s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #573
2024-06-06 07:11:32 +02:00
5e4d708884 Merge pull request 'update-deps' (#571) from update-deps into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m55s
CI/CD Pipeline / deploy-staging (push) Successful in 17m58s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #571
2024-06-05 15:07:04 +02:00
6e41758104 Merge pull request 'allow-event-triptype-update' (#569) from allow-event-triptype-update into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m30s
CI/CD Pipeline / deploy-staging (push) Successful in 6m16s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #569
2024-06-04 08:32:54 +02:00
c916381fb0 Merge pull request 'fix-spaces' (#567) from fix-spaces into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m59s
CI/CD Pipeline / deploy-staging (push) Successful in 6m43s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #567
2024-06-02 14:53:06 +02:00
7d44204533 Merge pull request 'remove debug println; better phrasing' (#565) from better-text into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m34s
CI/CD Pipeline / deploy-staging (push) Successful in 6m10s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #565
2024-05-30 18:53:35 +02:00
145892104b Merge pull request 'try' (#563) from better-text into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m53s
CI/CD Pipeline / deploy-staging (push) Successful in 5m43s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #563
2024-05-30 11:34:29 +02:00
6c0f0e6b04 Merge pull request 'better-text' (#562) from better-text into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m57s
CI/CD Pipeline / deploy-staging (push) Successful in 5m38s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #562
2024-05-30 11:12:03 +02:00
e004d81ca1 Merge pull request 'fix error' (#559) from fix-user-find-bug into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m14s
CI/CD Pipeline / deploy-staging (push) Successful in 7m28s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #559
2024-05-28 15:04:26 +02:00
41b5aff329 Merge pull request 'migrated-db' (#557) from migrated-db into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m18s
CI/CD Pipeline / deploy-staging (push) Successful in 7m44s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #557
2024-05-28 11:27:41 +02:00
76c8456380 Merge pull request 'rename role to manage_events' (#555) from reanme-to-event into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m43s
CI/CD Pipeline / deploy-staging (push) Successful in 7m26s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #555
2024-05-28 10:56:04 +02:00
95f43a73cf Merge pull request 'reanme-to-event' (#553) from reanme-to-event into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m26s
CI/CD Pipeline / deploy-staging (push) Successful in 7m11s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #553
2024-05-28 10:06:48 +02:00
3d340bf803 Merge pull request 'case-insensitive-auth' (#549) from case-insensitive-auth into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m27s
CI/CD Pipeline / deploy-staging (push) Successful in 6m54s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #549
2024-05-27 08:33:03 +02:00
ae096ad602 Merge pull request 'better spacing' (#547) from fix-spacing into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m7s
CI/CD Pipeline / deploy-staging (push) Successful in 8m21s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #547
2024-05-26 18:47:51 +02:00
25d8b1ea7c Merge pull request 'fix-spacing' (#545) from fix-spacing into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m18s
CI/CD Pipeline / deploy-staging (push) Successful in 7m50s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #545
2024-05-26 14:18:07 +02:00
410cd05acc Merge pull request 'type' (#543) from type into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m25s
CI/CD Pipeline / deploy-staging (push) Successful in 7m52s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #543
2024-05-25 18:52:45 +02:00
972811c2cf Merge pull request 'filter-logs' (#539) from filter-logs into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m8s
CI/CD Pipeline / deploy-staging (push) Successful in 7m35s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #539
2024-05-22 23:42:06 +02:00
e22d2d718e Merge pull request 'fix-footer' (#537) from fix-footer into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m23s
CI/CD Pipeline / deploy-staging (push) Successful in 7m54s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #537
2024-05-22 22:51:39 +02:00
b55f122f1d Merge pull request 'sanity-check-timing; fixes #488' (#535) from sanity-check-timing into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #535
2024-05-22 22:41:31 +02:00
d27489d714 Merge pull request 'automate-schnupper-mails' (#533) from automate-schnupper-mails into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m39s
CI/CD Pipeline / deploy-staging (push) Successful in 5m2s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #533
2024-05-22 08:51:39 +02:00
c340d1a916 Merge pull request 'reason-for-canceled-event' (#531) from reason-for-canceled-event into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m29s
CI/CD Pipeline / deploy-staging (push) Successful in 5m29s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #531
2024-05-22 08:13:55 +02:00
e7732b9e96 Merge pull request 'fix-ci' (#528) from fix-ci into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m55s
CI/CD Pipeline / deploy-staging (push) Successful in 7m20s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #528
2024-05-22 00:24:22 +02:00
97dc9308bc Merge pull request 'clippy' (#526) from clippy into staging
Some checks failed
CI/CD Pipeline / test (push) Failing after 1m2s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #526
2024-05-22 00:18:35 +02:00
46 changed files with 1529 additions and 792 deletions

195
Cargo.lock generated
View File

@ -175,7 +175,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.68",
]
[[package]]
@ -186,7 +186,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.68",
]
[[package]]
@ -221,9 +221,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "backtrace"
version = "0.3.72"
version = "0.3.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11"
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
dependencies = [
"addr2line",
"cc",
@ -266,9 +266,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
dependencies = [
"serde",
]
@ -309,9 +309,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "bytemuck"
version = "1.16.0"
version = "1.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5"
checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e"
[[package]]
name = "byteorder"
@ -327,9 +327,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
[[package]]
name = "cc"
version = "1.0.98"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d"
[[package]]
name = "cfg-if"
@ -620,11 +620,11 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
"syn 2.0.66",
"syn 2.0.68",
]
[[package]]
@ -647,9 +647,9 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
name = "either"
version = "1.12.0"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
dependencies = [
"serde",
]
@ -785,7 +785,7 @@ checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
dependencies = [
"futures-core",
"futures-sink",
"spin 0.9.8",
"spin",
]
[[package]]
@ -894,7 +894,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.68",
]
[[package]]
@ -992,8 +992,8 @@ dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata 0.4.6",
"regex-syntax 0.8.3",
"regex-automata 0.4.7",
"regex-syntax 0.8.4",
]
[[package]]
@ -1002,7 +1002,7 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"ignore",
"walkdir",
]
@ -1139,9 +1139,9 @@ dependencies = [
[[package]]
name = "httparse"
version = "1.8.0"
version = "1.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
[[package]]
name = "httpdate"
@ -1237,7 +1237,7 @@ dependencies = [
"globset",
"log",
"memchr",
"regex-automata 0.4.6",
"regex-automata 0.4.7",
"same-file",
"walkdir",
"winapi-util",
@ -1306,15 +1306,6 @@ version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.13.0"
@ -1372,11 +1363,11 @@ dependencies = [
[[package]]
name = "lazy_static"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
dependencies = [
"spin 0.5.2",
"spin",
]
[[package]]
@ -1485,9 +1476,9 @@ dependencies = [
[[package]]
name = "memchr"
version = "2.7.2"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "mime"
@ -1503,9 +1494,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.7.3"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
dependencies = [
"adler",
]
@ -1535,7 +1526,7 @@ dependencies = [
"httparse",
"memchr",
"mime",
"spin 0.9.8",
"spin",
"tokio",
"tokio-util",
"version_check",
@ -1583,7 +1574,7 @@ version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"crossbeam-channel",
"filetime",
"fsevent-sys",
@ -1671,9 +1662,9 @@ dependencies = [
[[package]]
name = "object"
version = "0.35.0"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434"
dependencies = [
"memchr",
]
@ -1696,7 +1687,7 @@ version = "0.10.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"cfg-if",
"foreign-types",
"libc",
@ -1713,7 +1704,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.68",
]
[[package]]
@ -1768,7 +1759,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.5.1",
"redox_syscall 0.5.2",
"smallvec",
"windows-targets 0.52.5",
]
@ -1819,7 +1810,7 @@ dependencies = [
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
"syn 2.0.66",
"syn 2.0.68",
]
[[package]]
@ -1868,7 +1859,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.68",
]
[[package]]
@ -1985,9 +1976,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.85"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
@ -2000,7 +1991,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.68",
"version_check",
"yansi",
]
@ -2076,11 +2067,11 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.1"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
]
[[package]]
@ -2100,19 +2091,19 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.68",
]
[[package]]
name = "regex"
version = "1.10.4"
version = "1.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata 0.4.6",
"regex-syntax 0.8.3",
"regex-automata 0.4.7",
"regex-syntax 0.8.4",
]
[[package]]
@ -2126,13 +2117,13 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.6"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.8.3",
"regex-syntax 0.8.4",
]
[[package]]
@ -2143,9 +2134,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.8.3"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "ring"
@ -2157,7 +2148,7 @@ dependencies = [
"cfg-if",
"getrandom",
"libc",
"spin 0.9.8",
"spin",
"untrusted",
"windows-sys 0.52.0",
]
@ -2211,7 +2202,7 @@ dependencies = [
"proc-macro2",
"quote",
"rocket_http",
"syn 2.0.66",
"syn 2.0.68",
"unicode-xid",
"version_check",
]
@ -2267,7 +2258,7 @@ dependencies = [
"env_logger",
"futures",
"ics",
"itertools 0.13.0",
"itertools",
"job_scheduler_ng",
"lettre",
"log",
@ -2280,6 +2271,7 @@ dependencies = [
"sqlx",
"tera",
"ureq",
"urlencoding",
]
[[package]]
@ -2314,7 +2306,7 @@ version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"errno",
"libc",
"linux-raw-sys",
@ -2440,7 +2432,7 @@ version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"core-foundation",
"core-foundation-sys",
"libc",
@ -2474,14 +2466,14 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.68",
]
[[package]]
name = "serde_json"
version = "1.0.117"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
dependencies = [
"itoa",
"ryu",
@ -2588,12 +2580,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spin"
version = "0.9.8"
@ -2615,11 +2601,10 @@ dependencies = [
[[package]]
name = "sqlformat"
version = "0.2.3"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c"
checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f"
dependencies = [
"itertools 0.12.1",
"nom",
"unicode_categories",
]
@ -2728,7 +2713,7 @@ checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
dependencies = [
"atoi",
"base64 0.21.7",
"bitflags 2.5.0",
"bitflags 2.6.0",
"byteorder",
"bytes",
"chrono",
@ -2772,7 +2757,7 @@ checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
dependencies = [
"atoi",
"base64 0.21.7",
"bitflags 2.5.0",
"bitflags 2.6.0",
"byteorder",
"chrono",
"crc",
@ -2873,9 +2858,9 @@ dependencies = [
[[package]]
name = "subtle"
version = "2.5.0"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
@ -2890,9 +2875,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.66"
version = "2.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
dependencies = [
"proc-macro2",
"quote",
@ -2950,7 +2935,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.68",
]
[[package]]
@ -2996,9 +2981,9 @@ dependencies = [
[[package]]
name = "tinyvec"
version = "1.6.0"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82"
dependencies = [
"tinyvec_macros",
]
@ -3035,7 +3020,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.68",
]
[[package]]
@ -3122,7 +3107,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.68",
]
[[package]]
@ -3328,14 +3313,14 @@ dependencies = [
"serde",
"serde_json",
"url",
"webpki-roots 0.26.2",
"webpki-roots 0.26.3",
]
[[package]]
name = "url"
version = "2.5.0"
version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
dependencies = [
"form_urlencoded",
"idna",
@ -3350,15 +3335,15 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "utf8parse"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.8.0"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439"
dependencies = [
"getrandom",
]
@ -3433,7 +3418,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.68",
"wasm-bindgen-shared",
]
@ -3455,7 +3440,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.68",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -3474,9 +3459,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "webpki-roots"
version = "0.26.2"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c452ad30530b54a4d8e71952716a212b08efd0f3562baa66c29a618b07da7c3"
checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd"
dependencies = [
"rustls-pki-types",
]
@ -3691,9 +3676,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "winnow"
version = "0.6.11"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c52728401e1dc672a56e81e593e912aa54c78f40246869f78359a2bf24d29d"
checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1"
dependencies = [
"memchr",
]
@ -3724,7 +3709,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.68",
]
[[package]]

View File

@ -28,6 +28,7 @@ itertools = "0.13"
job_scheduler_ng = "2.0"
ureq = { version = "2.9", features = ["json"] }
regex = "1.10"
urlencoding = "2.1"
[target.'cfg(not(windows))'.dependencies]
openssl = { version = "0.10", features = [ "vendored" ] }

View File

@ -2,6 +2,7 @@
secret_key = "/NtVGizglEoyoxBLzsRDWTy4oAG1qDw4J4O+CWJSv+fypD7W9sam8hUY4j90EZsbZk8wEradS5zBoWtWKi3k8w=="
rss_key = "rss-key-for-ci"
limits = { file = "10 MiB", data-form = "10 MiB"}
smtp_pw = "8kIjlLH79Ky6D3jQ"
smtp_pw = "8kIjlLH79Ky6D3j"
usage_log_path = "./usage.txt"
openweathermap_key = "c8dab8f91b5b815d76e9879cbaecd8d5"
wordpress_key = "pw-to-allow-sending-notifications"

2
fd
View File

@ -1,5 +1,5 @@
#!/bin/bash
scp read@128.140.64.118:/home/rowing/db.sqlite db.sqlite
scp root@128.140.64.118:/home/rowing/db.sqlite db.sqlite
#sqlite3 db.sqlite < seeds.sql

View File

@ -8,7 +8,7 @@ export interface choiceMap {
declare var loggedin_user_id: string;
let choiceObjects: choiceMap = {};
let boat_in_ottensheim = true;
let boat_reserved_today= true;
let boat_reserved_today = true;
document.addEventListener("DOMContentLoaded", function () {
changeTheme();
@ -103,7 +103,11 @@ function setTheme(theme: string, setLocalStorage = true) {
function setCurrentdate(input: HTMLInputElement) {
if (input) {
const now = new Date();
const formattedDateTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}T${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`;
const formattedDateTime = `${now.getFullYear()}-${String(
now.getMonth() + 1
).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}T${String(
now.getHours()
).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`;
input.value = formattedDateTime;
}
@ -139,29 +143,33 @@ function selectBoatChange() {
boatSelect.addEventListener(
"addItem",
function (e) {
const event = e as ChoiceBoatEvent;
boat_reserved_today = event.detail.customProperties.boat_reserved_today;
if (boat_reserved_today){
alert(event.detail.label.trim()+' wurde heute reserviert. Bitte kontrolliere, dass du die Reservierung nicht störst.');
}
if (boat_reserved_today) {
alert(
event.detail.label.trim() +
" wurde heute reserviert. Bitte kontrolliere, dass du die Reservierung nicht störst."
);
}
boat_in_ottensheim = event.detail.customProperties.boat_in_ottensheim;
const amount_seats = event.detail.customProperties.amount_seats;
setMaxAmountRowers("newrower", amount_seats);
let only_steering = <HTMLSelectElement>document.querySelector('#shipmaster_only_steering');
if (event.detail.customProperties.default_handoperated) {
only_steering.setAttribute('checked', 'true');
}else {
only_steering.removeAttribute('checked');
}
let only_steering = <HTMLSelectElement>(
document.querySelector("#shipmaster_only_steering")
);
if (event.detail.customProperties.default_handoperated) {
only_steering.setAttribute("checked", "true");
} else {
only_steering.removeAttribute("checked");
}
if (event.detail.customProperties.convert_handoperated_possible) {
only_steering.removeAttribute('disabled');
}else {
only_steering.setAttribute('disabled', 'disabled');
}
if (event.detail.customProperties.convert_handoperated_possible) {
only_steering.removeAttribute("readonly");
} else {
only_steering.setAttribute("readonly", "readonly");
}
const destination = <HTMLSelectElement>(
document.querySelector("#destination")
@ -170,22 +178,35 @@ function selectBoatChange() {
if (event.detail.customProperties.owner) {
choiceObjects["newrower"].setChoiceByValue(
event.detail.customProperties.owner.toString(),
event.detail.customProperties.owner.toString()
);
if(event.detail.value === '36') {
/** custom code for Etsch */
choiceObjects["newrower"].setChoiceByValue("81");
if (event.detail.value === "36") {
/** custom code for Etsch */
choiceObjects["newrower"].setChoiceByValue("81");
}
}else if (typeof loggedin_user_id !== 'undefined'){
choiceObjects["newrower"].setChoiceByValue(loggedin_user_id);
}
} else if (typeof loggedin_user_id !== "undefined") {
const currentSelection = choiceObjects["newrower"].getValue();
let selectedItemsCount: number;
if (Array.isArray(currentSelection)) {
selectedItemsCount = currentSelection.length;
} else {
selectedItemsCount = currentSelection !== undefined ? 1 : 0;
}
if (selectedItemsCount == 0) {
choiceObjects["newrower"].setChoiceByValue(loggedin_user_id);
}
}
const inputElement = document.getElementById(
"departure",
"departure"
) as HTMLInputElement;
const now = new Date();
const formattedDateTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}T${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`;
const formattedDateTime = `${now.getFullYear()}-${String(
now.getMonth() + 1
).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}T${String(
now.getHours()
).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`;
inputElement.value = formattedDateTime;
@ -194,7 +215,7 @@ function selectBoatChange() {
);
destinput.dispatchEvent(new Event("input"));
},
false,
false
);
choiceObjects[boatSelect.id] = boatChoice;
@ -222,16 +243,16 @@ function reloadPage() {
function setMaxAmountRowers(name: string, rowers: number) {
if (choiceObjects[name]) {
choiceObjects[name].removeActiveItems(-1);
//let curSelection = choiceObjects[name].getValue(true);
//let amount_to_delete = (<any>curSelection).length - rowers;
//choiceObjects[name].removeActiveItems(-1);
let curSelection = choiceObjects[name].getValue(true);
let amount_to_delete = (<any>curSelection).length - rowers;
//if (amount_to_delete > 0){
// let to_delete = (<any>curSelection).slice(-amount_to_delete);
// for (let del of to_delete) {
// choiceObjects[name].removeActiveItemsByValue(del);
// }
//}
if (amount_to_delete > 0) {
let to_delete = (<any>curSelection).slice(-amount_to_delete);
for (let del of to_delete) {
choiceObjects[name].removeActiveItemsByValue(del);
}
}
let input = <HTMLElement>document.querySelector("#" + name);
if (input) {
@ -239,24 +260,24 @@ function setMaxAmountRowers(name: string, rowers: number) {
if (rowers === 0) {
choiceObjects[name].disable();
input.parentElement?.parentElement?.parentElement?.classList.add(
"hidden",
"hidden"
);
input.parentElement?.parentElement?.parentElement?.classList.add(
"md:block",
"md:block"
);
input.parentElement?.parentElement?.parentElement?.classList.add(
"opacity-50",
"opacity-50"
);
} else {
choiceObjects[name].enable();
input.parentElement?.parentElement?.parentElement?.classList.remove(
"hidden",
"hidden"
);
input.parentElement?.parentElement?.parentElement?.classList.remove(
"md:block",
"md:block"
);
input.parentElement?.parentElement?.parentElement?.classList.remove(
"opacity-50",
"opacity-50"
);
}
}
@ -293,7 +314,7 @@ function setMaxAmountRowers(name: string, rowers: number) {
function initBoatActions() {
const boatSelects = document.querySelectorAll(
'.boats-js[data-onclick="true"]',
'.boats-js[data-onclick="true"]'
);
if (boatSelects) {
Array.prototype.forEach.call(boatSelects, (select: HTMLInputElement) => {
@ -366,7 +387,7 @@ function initNewChoice(select: HTMLInputElement) {
steering_person.setAttribute("required", "required");
}
const choice = new Choices(select, {
searchFields: ['label', 'value', 'customProperties.searchableText'],
searchFields: ["label", "value", "customProperties.searchableText"],
removeItemButton: true,
loadingText: "Wird geladen...",
noResultsText: "Keine Ergebnisse gefunden",
@ -449,7 +470,7 @@ function initNewChoice(select: HTMLInputElement) {
steeringSelect.add(new Option(name, user_id));
}
},
false,
false
);
select.addEventListener(
@ -478,7 +499,7 @@ function initNewChoice(select: HTMLInputElement) {
}
}
},
false,
false
);
choiceObjects[select.id] = choice;
@ -508,7 +529,7 @@ function initToggle() {
}
sessionStorage.setItem(
"tripsFilter",
JSON.stringify(Array.from(filterMap.entries())),
JSON.stringify(Array.from(filterMap.entries()))
);
}
resetFilteredElements();
@ -535,7 +556,7 @@ function initToggle() {
} else {
sessionStorage.setItem(
"tripsFilter",
JSON.stringify(Array.from(filterObject.entries())),
JSON.stringify(Array.from(filterObject.entries()))
);
}
}
@ -547,14 +568,14 @@ function resetFilteredElements() {
hiddenElements,
(hiddenElement: HTMLButtonElement) => {
hiddenElement.classList.remove("hidden");
},
}
);
}
}
function triggerFilterAction(activeFilter: any) {
const activeBtn = document.querySelector(
'button[data-action="' + activeFilter + '"]',
'button[data-action="' + activeFilter + '"]'
);
if (activeBtn) {
activeBtn.setAttribute("aria-pressed", "true");
@ -654,7 +675,7 @@ function initSidebar() {
sidebar.toggle();
});
}
},
}
);
}
}
@ -751,14 +772,15 @@ function addRelationMagic(bodyElement: HTMLElement) {
dataList.options,
function (option) {
return option.value === field.value;
},
}
);
if (option && option.value !== ""){
// Get distance
const distance = option.getAttribute("distance");
if (distance && relatedField.value === "") relatedField.value = distance;
}
if (option && option.value !== "") {
// Get distance
const distance = option.getAttribute("distance");
if (distance && relatedField.value === "")
relatedField.value = distance;
}
}
});
}

View File

@ -102,6 +102,28 @@ test("Cox can start and finish trip", async ({ page }, testInfo) => {
await expect(page.locator('body')).toContainText('(cox2)');
await expect(page.locator('body')).toContainText('Ottensheim (25 km)');
await expect(page.locator('body')).toContainText('Ruderer: cox2, rower2');
//Ausloggen...
await page.getByRole('banner').getByRole('link', { name: 'Logbuch' }).click();
await page.getByRole('link', { name: 'Ausloggen' }).click();
// Login as admin
await page.getByPlaceholder("Name").click();
await page.getByPlaceholder("Name").fill("main");
await page.getByPlaceholder("Name").press("Tab");
await page.getByPlaceholder("Passwort").fill("admin");
await page.getByPlaceholder("Passwort").press("Enter");
await page.goto("/log/show");
await page.getByText('(cox2)').click();
page.once("dialog", (dialog) => {
dialog.accept().catch(() => {});
});
await page.getByRole('link', { name: 'Löschen' }).click();
//Ausloggen...
await page.getByRole('banner').getByRole('link', { name: 'Logbuch' }).click();
await page.getByRole('link', { name: 'Ausloggen' }).click();
});
test("Kiosk can start and cancel trip", async ({ page }, testInfo) => {
@ -189,4 +211,173 @@ test("Kiosk can start and finish trip", async ({ page }, testInfo) => {
await expect(page.locator('body')).toContainText('(cox2)');
await expect(page.locator('body')).toContainText('Ottensheim (25 km)');
await expect(page.locator('body')).toContainText('Ruderer: cox2, rower2');
//Ausloggen...
await page.context().clearCookies();
await page.goto("/auth");
// Login as admin
await page.getByPlaceholder("Name").click();
await page.getByPlaceholder("Name").fill("main");
await page.getByPlaceholder("Name").press("Tab");
await page.getByPlaceholder("Passwort").fill("admin");
await page.getByPlaceholder("Passwort").press("Enter");
await page.goto("/log/show");
await page.getByText('(cox2)').click();
page.once("dialog", (dialog) => {
dialog.accept().catch(() => {});
});
await page.getByRole('link', { name: 'Löschen' }).click();
//Ausloggen...
await page.getByRole('banner').getByRole('link', { name: 'Logbuch' }).click();
await page.getByRole('link', { name: 'Ausloggen' }).click();
});
test("Cox can start and finish trip with cox steering only", async ({ page }, testInfo) => {
await page.goto("/auth");
await page.getByPlaceholder("Name").click();
await page.getByPlaceholder("Name").fill("cox2");
await page.getByPlaceholder("Name").press("Tab");
await page.getByPlaceholder("Passwort").fill("cox");
await page.getByPlaceholder("Passwort").press("Enter");
await page.goto("/");
await page.getByRole("link", { name: "Ausfahrt eintragen" }).click();
if (testInfo.project.name.includes("Mobile")) {
// No left boat selector on mobile views
await page.getByText('-- Wähle ein Boot aus ---').nth(1).click();
await page.getByRole("option", { name: "cox_only_steering_boat" }).click();
} else {
await page.getByText('2+', { exact: true }).click();
await page.getByText("cox_only_steering_boat", { exact: true }).click();
}
// Trip starts 2 hours ago
const datetimeSelector = '#departure';
const currentValue = await page.$eval(datetimeSelector, el => el.value);
const currentDate = new Date(currentValue);
currentDate.setMinutes(currentDate.getMinutes());
currentDate.setHours(currentDate.getHours() - new Date().getTimezoneOffset()/60 - 2);
const newDatetime = currentDate.toISOString().slice(0, 16);
await page.$eval(datetimeSelector, (el, value) => el.value = value, newDatetime);
await expect(page.locator("#shipmaster-newrowerjs")).toContainText("cox");
await expect(page.locator("#steering_person-newrowerjs")).toContainText(
"cox",
);
await page.getByRole("button", { name: "Ausfahrt eintragen" }).click();
await expect(page.locator("body")).toContainText(
"Ausfahrt erfolgreich hinzugefügt",
);
await expect(page.locator("body")).toContainText("cox_only_steering_boat");
await page.goto("/log");
await page.locator("div:nth-child(2) > .border-0").click();
await page.getByRole("combobox", { name: "Destination" }).click();
await page.getByRole("combobox", { name: "Destination" }).fill("Ottensheim");
await page.getByRole("button", { name: "Ausfahrt beenden" }).click();
await expect(page.locator("body")).toContainText(
"Ausfahrt korrekt eingetragen",
);
await page.goto('/log/show');
await expect(page.locator('body')).toContainText('cox_only_steering_boat');
await expect(page.locator('body')).toContainText('(cox2 - handgesteuert)');
await expect(page.locator('body')).toContainText('Ottensheim (25 km)');
//Ausloggen...
await page.getByRole('banner').getByRole('link', { name: 'Logbuch' }).click();
await page.getByRole('link', { name: 'Ausloggen' }).click();
// Login as admin
await page.getByPlaceholder("Name").click();
await page.getByPlaceholder("Name").fill("main");
await page.getByPlaceholder("Name").press("Tab");
await page.getByPlaceholder("Passwort").fill("admin");
await page.getByPlaceholder("Passwort").press("Enter");
await page.goto("/log/show");
await page.getByText('(cox2 - handgesteuert)').click();
page.once("dialog", (dialog) => {
dialog.accept().catch(() => {});
});
await page.getByRole('link', { name: 'Löschen' }).click();
//Ausloggen...
await page.getByRole('banner').getByRole('link', { name: 'Logbuch' }).click();
await page.getByRole('link', { name: 'Ausloggen' }).click();
});
test("Kiosk can start and finish trip in one stop", async ({ page }, testInfo) => {
await page.goto("/log/kiosk/ekrv2019/Linz");
if (testInfo.project.name.includes("Mobile")) {
// No left boat selector on mobile views
await page.getByText('-- Wähle ein Boot aus ---').nth(1).click();
await page.getByRole("option", { name: "Joe" }).click();
} else {
await page.getByText('2x', { exact: true }).click();
await page.getByText("Joe", { exact: true }).click();
}
await page.getByPlaceholder("Ruderer auswählen").click();
await page.getByRole("option", { name: "rower2" }).click();
await page.getByRole("option", { name: "cox2" }).click();
await expect(page.getByRole("listbox")).toContainText(
"Nur 2 Ruderer können hinzugefügt werden",
);
// Trip starts 2 hours ago
const datetimeSelector = '#departure';
const currentValue = await page.$eval(datetimeSelector, el => el.value);
const currentDate = new Date(currentValue);
currentDate.setMinutes(currentDate.getMinutes());
currentDate.setHours(currentDate.getHours() - new Date().getTimezoneOffset()/60 - 2);
const newDatetime = currentDate.toISOString().slice(0, 16);
await page.$eval(datetimeSelector, (el, value) => el.value = value, newDatetime);
await page.getByLabel('Ankunftszeit').click();
await page.locator('#destination').fill('a');
await page.getByLabel('Distanz').fill('1');
await expect(page.locator("#shipmaster-newrowerjs")).toContainText("cox");
await expect(page.locator("#steering_person-newrowerjs")).toContainText(
"rower2 cox",
);
await page.getByRole("button", { name: "Ausfahrt eintragen" }).click();
await expect(page.locator("body")).toContainText(
"Ausfahrt erfolgreich hinzugefügt",
);
await page.getByRole('link', { name: 'Logbuch' }).click();
await expect(page.locator('body')).toContainText('Joe');
await expect(page.locator('body')).toContainText('(cox2)');
await expect(page.locator('body')).toContainText('a (1 km)');
await expect(page.locator('body')).toContainText('Ruderer: cox2, rower2');
//Ausloggen...
await page.context().clearCookies();
await page.goto("/auth");
// Login as admin
await page.getByPlaceholder("Name").click();
await page.getByPlaceholder("Name").fill("main");
await page.getByPlaceholder("Name").press("Tab");
await page.getByPlaceholder("Passwort").fill("admin");
await page.getByPlaceholder("Passwort").press("Enter");
await page.goto("/log/show");
await page.getByText('(cox2)').click();
page.once("dialog", (dialog) => {
dialog.accept().catch(() => {});
});
await page.getByRole('link', { name: 'Löschen' }).click();
//Ausloggen...
await page.getByRole('banner').getByRole('link', { name: 'Logbuch' }).click();
await page.getByRole('link', { name: 'Ausloggen' }).click();
});

View File

@ -213,3 +213,9 @@ CREATE TABLE IF NOT EXISTS "trailer_reservation" (
"created_at" datetime not null default CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS "distance" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"destination" text NOT NULL,
"distance_in_km" integer NOT NULL
);

View File

@ -1,73 +0,0 @@
# Wordpress auth
Add the following code to `wp-content/themes/bravada/functions.php`:
```
function rot_auth( $user, $username, $password ){
// Make sure a username and password are present for us to work with
if($username == '' || $password == '') return;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://app.rudernlinz.at/wikiauth');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "name=$username&password=$password");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Execute the cURL session and get the response
$response = curl_exec($ch);
// Check for cURL errors
if(curl_errno($ch)){
$user = new WP_Error( 'denied', __('Curl error: ' . curl_error($ch)) );
}
// Close the cURL session
curl_close($ch);
if (strpos($response, 'SUCC') !== false) {
$user = get_user_by('login', $username);
if (!$user) {
// User does not exist, create a new one
$userdata = array(
'user_email' => $username,
'user_login' => $username,
'first_name' => $username,
'last_name' => ''
);
$new_user_id = wp_insert_user($userdata);
if (!is_wp_error($new_user_id)) {
// Load the new user info
$user = new WP_User($new_user_id);
// Set role based on username
if ($username == 'Philipp Hofer' || $username == 'Marie Birner') {
$user->set_role('administrator');
} else {
$user->set_role('editor');
}
} else {
// Handle error in user creation
return $new_user_id;
}
} else {
}
} else {
$user = new WP_Error( 'denied', __("Falscher Benutzername/Passwort. Verwendest du deine Accountdaten vom Ruderassistenten?") );
}
return $user;
}
// Comment this line if you wish to fall back on WordPress authentication
// Useful for times when the external service is offline
remove_action('authenticate', 'wp_authenticate_username_password', 20);
add_filter( 'authenticate', 'rot_auth', 10, 3 );
```

View File

@ -9,6 +9,9 @@ INSERT INTO "role" (name) VALUES ('paid');
INSERT INTO "role" (name) VALUES ('Vorstand');
INSERT INTO "role" (name) VALUES ('Bootsführer');
INSERT INTO "role" (name) VALUES ('schnupperant');
INSERT INTO "role" (name) VALUES ('kassier');
INSERT INTO "role" (name) VALUES ('schriftfuehrer');
INSERT INTO "role" (name) VALUES ('no-einschreibgebuehr');
INSERT INTO "user" (name, pw) VALUES('admin', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$4P4NCw4Ukhv80/eQYTsarHhnw61JuL1KMx/L9dm82YM');
INSERT INTO "user_role" (user_id, role_id) VALUES(1,1);
INSERT INTO "user_role" (user_id, role_id) VALUES(1,2);
@ -35,7 +38,12 @@ INSERT INTO "user_role" (user_id, role_id) VALUES(8,5);
INSERT INTO "user_role" (user_id, role_id) VALUES(8,7);
INSERT INTO "user" (name, pw) VALUES('Vorstandsmitglied', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$jWKzDmI0jqT2dqINFt6/1NjVF4Dx15n07PL1ZMBmFsY');
INSERT INTO "user_role" (user_id, role_id) VALUES(9,5);
INSERT INTO "user_role" (user_id, role_id) VALUES(9,9);
INSERT INTO "user" (name, pw) VALUES('main', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$4P4NCw4Ukhv80/eQYTsarHhnw61JuL1KMx/L9dm82YM');
INSERT INTO "user_role" (user_id, role_id) VALUES(10,1);
INSERT INTO "user_role" (user_id, role_id) VALUES(10,2);
INSERT INTO "user_role" (user_id, role_id) VALUES(10,5);
INSERT INTO "user_role" (user_id, role_id) VALUES(10,6);
INSERT INTO "user_role" (user_id, role_id) VALUES(10,9);
INSERT INTO "trip_details" (planned_starting_time, max_people, day, notes) VALUES('10:00', 2, '1970-01-01', 'trip_details for a planned event');
INSERT INTO "planned_event" (name, planned_amount_cox, trip_details_id) VALUES('test-planned-event', 2, 1);
@ -55,6 +63,7 @@ INSERT INTO "boat" (name, amount_seats, location_id) VALUES ('Kaputtes Boot :-('
INSERT INTO "boat" (name, amount_seats, location_id) VALUES ('Sehr kaputtes Boot :-((', 7, 1);
INSERT INTO "boat" (name, amount_seats, location_id) VALUES ('Ottensheim Boot', 7, 2);
INSERT INTO "boat" (name, amount_seats, location_id, owner) VALUES ('second_private_boat_from_rower', 1, 1, 2);
INSERT INTO "boat" (name, amount_seats, location_id, default_shipmaster_only_steering) VALUES ('cox_only_steering_boat', 3, 1, true);
INSERT INTO "logbook_type" (name) VALUES ('Wanderfahrt');
INSERT INTO "logbook_type" (name) VALUES ('Regatta');
INSERT INTO "logbook" (boat_id, shipmaster,steering_person, shipmaster_only_steering, departure) VALUES (2, 2, 2, false, strftime('%Y', 'now') || '-12-24 10:00');
@ -66,3 +75,5 @@ INSERT INTO "boat_damage" (boat_id, desc, user_id_created, created_at, lock_boat
INSERT INTO "notification" (user_id, message, category) VALUES (1, 'This is a test notification', 'test-cat');
INSERT INTO "trailer" (name) VALUES('Großer Hänger');
INSERT INTO "trailer" (name) VALUES('Kleiner Hänger');
insert into distance(destination, distance_in_km) values('Ottensheim', 25);

View File

@ -1,5 +1,6 @@
use std::ops::DerefMut;
use chrono::NaiveDateTime;
use itertools::Itertools;
use rocket::serde::{Deserialize, Serialize};
use rocket::FromForm;
@ -391,6 +392,39 @@ ORDER BY amount_seats DESC
.await
.ok()
}
pub async fn on_water_between(
&self,
db: &mut Transaction<'_, Sqlite>,
dep: NaiveDateTime,
arr: NaiveDateTime,
) -> bool {
let dep = dep.format("%Y-%m-%dT%H:%M").to_string();
let arr = arr.format("%Y-%m-%dT%H:%M").to_string();
sqlx::query!(
"SELECT COUNT(*) AS overlap_count
FROM logbook
WHERE boat_id = ?
AND (
(departure <= ? AND arrival >= ?) -- Existing entry covers the entire new period
OR (departure >= ? AND departure < ?) -- Existing entry starts during the new period
OR (arrival > ? AND arrival <= ?) -- Existing entry ends during the new period
);",
self.id,
arr,
arr,
dep,
dep,
dep,
arr
)
.fetch_one(db.deref_mut())
.await
.unwrap()
.overlap_count
> 0
}
}
#[cfg(test)]

33
src/model/distance.rs Normal file
View File

@ -0,0 +1,33 @@
use serde::Serialize;
use sqlx::{FromRow, SqlitePool};
#[derive(FromRow, Serialize, Clone, Debug)]
pub struct Distance {
pub id: i64,
pub destination: String,
pub distance_in_km: i64,
}
impl Distance {
/// Return all default `distance`s, ordered by usage in logbook entries
pub async fn all(db: &SqlitePool) -> Vec<Self> {
sqlx::query_as!(
Self,
"SELECT
d.id,
d.destination,
d.distance_in_km
FROM
distance d
LEFT JOIN
logbook l ON d.destination = l.destination AND d.distance_in_km = l.distance_in_km
GROUP BY
d.id, d.destination, d.distance_in_km
ORDER BY
COUNT(l.id) DESC, d.destination ASC;"
)
.fetch_all(db)
.await
.unwrap()
}
}

View File

@ -8,7 +8,10 @@ use ics::{
use serde::Serialize;
use sqlx::{FromRow, Row, SqlitePool};
use super::{notification::Notification, tripdetails::TripDetails, triptype::TripType, user::User};
use super::{
notification::Notification, role::Role, tripdetails::TripDetails, triptype::TripType,
user::User,
};
#[derive(Serialize, Clone, FromRow, Debug, PartialEq)]
pub struct Event {
@ -213,12 +216,35 @@ WHERE trip_details.id=?
.ok()
}
async fn advertise(db: &SqlitePool, day: &str, planned_starting_time: &str, name: &str) {
let donau = Role::find_by_name(db, "Donau Linz").await.unwrap();
Notification::create_for_role(
db,
&donau,
&format!("Am {} um {} wurde ein neues Event angelegt: {} Wir freuen uns wenn du dabei mitmachst, die Anmeldung ist ab sofort offen :-)", day, planned_starting_time, name),
"Neues Event",
Some(&format!("/planned#{day}")),
None,
)
.await;
}
pub async fn create(
db: &SqlitePool,
name: &str,
planned_amount_cox: i32,
trip_details: &TripDetails,
) {
if trip_details.always_show {
Self::advertise(
db,
&trip_details.day,
&trip_details.planned_starting_time,
name,
)
.await;
}
sqlx::query!(
"INSERT INTO planned_event(name, planned_amount_cox, trip_details_id) VALUES(?, ?, ?)",
name,
@ -258,6 +284,16 @@ WHERE trip_details.id=?
.await
.unwrap(); //Okay, as planned_event can only be created with proper DB backing
if !tripdetails.always_show && update.always_show {
Self::advertise(
db,
&tripdetails.day,
&tripdetails.planned_starting_time,
update.name,
)
.await;
}
if update.max_people == 0 && !was_already_cancelled {
let coxes = Registration::all_cox(db, self.id).await;
for user in coxes {

View File

@ -1,6 +1,6 @@
use std::ops::DerefMut;
use chrono::{Datelike, Local, NaiveDateTime};
use chrono::{Datelike, Duration, Local, NaiveDateTime};
use rocket::FromForm;
use serde::Serialize;
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
@ -60,6 +60,22 @@ pub struct LogToFinalize {
pub rowers: Vec<i64>,
}
#[derive(FromForm, Debug, Clone)]
pub struct LogToUpdate {
pub id: i64,
pub boat_id: i64,
pub shipmaster: i64,
pub steering_person: i64,
pub shipmaster_only_steering: bool,
pub departure: String,
pub arrival: Option<String>,
pub destination: Option<String>,
pub distance_in_km: Option<i64>,
pub comments: Option<String>,
pub logtype: Option<i64>,
pub rowers: Vec<i64>,
}
impl TryFrom<LogToAdd> for LogToFinalize {
type Error = String;
@ -94,6 +110,25 @@ pub struct LogbookWithBoatAndRowers {
pub rowers: Vec<User>,
}
impl LogbookWithBoatAndRowers {
pub(crate) async fn from(db: &SqlitePool, log: Logbook) -> Self {
Self {
rowers: Rower::for_log(db, &log).await,
boat: Boat::find_by_id(db, log.boat_id as i32).await.unwrap(),
shipmaster_user: User::find_by_id(db, log.shipmaster as i32).await.unwrap(),
steering_user: User::find_by_id(db, log.steering_person as i32)
.await
.unwrap(),
logbook: log,
}
}
}
#[derive(Debug, PartialEq)]
pub enum LogbookAdminUpdateError {
NotAllowed,
}
#[derive(Debug, PartialEq)]
pub enum LogbookUpdateError {
NotYourEntry,
@ -105,6 +140,9 @@ pub enum LogbookUpdateError {
UserNotAllowedToUseBoat,
OnlyAllowedToEndTripsEndingToday,
TooFast(i64, i64),
AlreadyFinalized,
ExternalSteeringPersonMustSteerOrShipmaster,
BoatAlreadyOnWater,
}
#[derive(Debug, PartialEq)]
@ -129,6 +167,8 @@ pub enum LogbookCreateError {
OnlyAllowedToEndTripsEndingToday,
CantChangeHandoperatableStatusForThisBoat,
TooFast(i64, i64),
AlreadyFinalized,
ExternalSteeringPersonMustSteerOrShipmaster,
}
impl From<LogbookUpdateError> for LogbookCreateError {
@ -153,6 +193,11 @@ impl From<LogbookUpdateError> for LogbookCreateError {
LogbookCreateError::OnlyAllowedToEndTripsEndingToday
}
LogbookUpdateError::TooFast(km, min) => LogbookCreateError::TooFast(km, min),
LogbookUpdateError::AlreadyFinalized => LogbookCreateError::AlreadyFinalized,
LogbookUpdateError::ExternalSteeringPersonMustSteerOrShipmaster => {
LogbookCreateError::ExternalSteeringPersonMustSteerOrShipmaster
}
LogbookUpdateError::BoatAlreadyOnWater => LogbookCreateError::BoatAlreadyOnWater,
}
}
}
@ -172,7 +217,7 @@ impl Logbook {
.await
.ok()
}
pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
sqlx::query_as!(
Self,
"
@ -219,15 +264,7 @@ ORDER BY departure DESC
let mut ret = Vec::new();
for log in logs {
ret.push(LogbookWithBoatAndRowers {
rowers: Rower::for_log(db, &log).await,
boat: Boat::find_by_id(db, log.boat_id as i32).await.unwrap(),
shipmaster_user: User::find_by_id(db, log.shipmaster as i32).await.unwrap(),
steering_user: User::find_by_id(db, log.steering_person as i32)
.await
.unwrap(),
logbook: log,
});
ret.push(LogbookWithBoatAndRowers::from(db, log).await);
}
ret
}
@ -242,7 +279,7 @@ ORDER BY departure DESC
FROM logbook
JOIN rower ON logbook.id = rower.logbook_id
WHERE arrival is not null AND rower_id = {}
ORDER BY departure DESC
ORDER BY arrival DESC
", user.id)
)
.fetch_all(db)
@ -251,15 +288,7 @@ ORDER BY departure DESC
let mut ret = Vec::new();
for log in logs {
ret.push(LogbookWithBoatAndRowers {
rowers: Rower::for_log(db, &log).await,
boat: Boat::find_by_id(db, log.boat_id as i32).await.unwrap(),
shipmaster_user: User::find_by_id(db, log.shipmaster as i32).await.unwrap(),
steering_user: User::find_by_id(db, log.steering_person as i32)
.await
.unwrap(),
logbook: log,
});
ret.push(LogbookWithBoatAndRowers::from(db, log).await);
}
ret
}
@ -275,7 +304,7 @@ ORDER BY departure DESC
SELECT id, boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km, comments, logtype
FROM logbook
WHERE arrival is not null AND arrival LIKE '{}-%'
ORDER BY departure DESC
ORDER BY arrival DESC
", year)
)
.fetch_all(db)
@ -284,15 +313,7 @@ ORDER BY departure DESC
let mut ret = Vec::new();
for log in logs {
ret.push(LogbookWithBoatAndRowers {
rowers: Rower::for_log(db, &log).await,
boat: Boat::find_by_id(db, log.boat_id as i32).await.unwrap(),
shipmaster_user: User::find_by_id(db, log.shipmaster as i32).await.unwrap(),
steering_user: User::find_by_id(db, log.steering_person as i32)
.await
.unwrap(),
logbook: log,
});
ret.push(LogbookWithBoatAndRowers::from(db, log).await);
}
ret
}
@ -329,13 +350,12 @@ ORDER BY departure DESC
let mut tx = db.begin().await.unwrap();
let inserted_row = sqlx::query!(
"INSERT INTO logbook(boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km, comments, logtype) VALUES (?,?,?,?,?,?,?,?,?,?) RETURNING id",
"INSERT INTO logbook(boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, destination, distance_in_km, comments, logtype) VALUES (?,?,?,?,?,?,?,?,?) RETURNING id",
log.boat_id,
log.shipmaster,
log.steering_person,
log.shipmaster_only_steering,
log.departure,
log.arrival,
log.destination,
log.distance_in_km,
log.comments,
@ -391,6 +411,18 @@ ORDER BY departure DESC
if user.on_water(db).await {
return Err(LogbookCreateError::RowerAlreadyOnWater(Box::new(user)));
}
if user.name == "Externe Steuerperson" {
if let (Some(steering_id), Some(shipmaster_id)) =
(log.steering_person, log.shipmaster)
{
if steering_id != user.id && shipmaster_id != user.id {
return Err(
LogbookCreateError::ExternalSteeringPersonMustSteerOrShipmaster,
);
}
}
}
}
if !boat.shipmaster_allowed(db, created_by_user).await {
@ -437,23 +469,33 @@ ORDER BY departure DESC
Ok(ret)
}
pub async fn distances(db: &SqlitePool) -> Vec<(String, i64)> {
let result = sqlx::query!("SELECT destination, distance_in_km FROM logbook WHERE id IN (SELECT MIN(id) FROM logbook GROUP BY destination) AND destination IS NOT NULL AND distance_in_km IS NOT NULL;")
.fetch_all(db)
.await
.unwrap();
pub async fn update(
&self,
db: &SqlitePool,
data: LogToUpdate,
user: &User,
) -> Result<(), LogbookAdminUpdateError> {
if !user.has_role(db, "Vorstand").await {
return Err(LogbookAdminUpdateError::NotAllowed);
}
result
.into_iter()
.filter_map(|r| {
if let (Some(destination), Some(distance_in_km)) = (r.destination, r.distance_in_km)
{
Some((destination, distance_in_km))
} else {
None
}
})
.collect()
sqlx::query!(
"UPDATE logbook SET boat_id=?, shipmaster=?, steering_person=?, shipmaster_only_steering=?, departure=?, arrival=?, destination=?, distance_in_km=?, comments=?, logtype=? WHERE id=?",
data.boat_id,
data.shipmaster,
data.steering_person,
data.shipmaster_only_steering,
data.departure,
data.arrival,
data.destination,
data.distance_in_km,
data.comments,
data.logtype,
self.id
)
.execute(db)
.await.unwrap();
Ok(())
}
async fn remove_rowers(&self, db: &mut Transaction<'_, Sqlite>) {
@ -496,6 +538,10 @@ ORDER BY departure DESC
return Err(LogbookUpdateError::NotYourEntry);
}
if self.arrival.is_some() {
return Err(LogbookUpdateError::AlreadyFinalized);
}
let boat = Boat::find_by_id_tx(db, self.boat_id as i32).await.unwrap(); //ok
if boat.amount_seats == 1 {
@ -529,6 +575,12 @@ ORDER BY departure DESC
return Err(LogbookUpdateError::ArrivalNotAfterDeparture);
}
if !boat.external {
if boat.on_water_between(db, dep, arr).await {
return Err(LogbookUpdateError::BoatAlreadyOnWater);
};
}
let duration_in_mins = (arr.and_utc().timestamp() - dep.and_utc().timestamp()) / 60;
// Not possible to row < 1 min / 500 m = < 2 min / km
let possible_distance_km = duration_in_mins / 2;
@ -542,8 +594,14 @@ ORDER BY departure DESC
let today = Local::now().date_naive();
let day_diff = today - arr.date();
let day_diff = day_diff.num_days();
if day_diff >= 7 && !user.has_role_tx(db, "admin").await {
return Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday);
if day_diff >= 7 {
if !user.has_role_tx(db, "admin").await
&& !user
.has_role_tx(db, "allow-retroactive-logbookentries")
.await
{
return Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday);
}
}
if day_diff < 0 && !user.has_role_tx(db, "admin").await {
return Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday);
@ -553,6 +611,19 @@ ORDER BY departure DESC
self.remove_rowers(db).await;
for rower in &log.rowers {
let user = User::find_by_id_tx(db, *rower as i32).await.unwrap();
if user.name == "Externe Steuerperson" {
if let (Some(steering_id), Some(shipmaster_id)) =
(log.steering_person, log.shipmaster)
{
if steering_id != user.id && shipmaster_id != user.id {
return Err(
LogbookUpdateError::ExternalSteeringPersonMustSteerOrShipmaster,
);
}
}
}
Rower::create(db, self.id, *rower)
.await
.map_err(|e| LogbookUpdateError::RowerCreateError(*rower, e.to_string()))?;
@ -623,15 +694,57 @@ ORDER BY departure DESC
pub async fn delete(&self, db: &SqlitePool, user: &User) -> Result<(), LogbookDeleteError> {
Log::create(db, format!("{} deleted trip: {self:?}", user.name)).await;
if user.has_role(db, "admin").await
|| user.has_role(db, "Vorstand").await
|| user.id == self.shipmaster
{
sqlx::query!("DELETE FROM logbook WHERE id=?", self.id)
.execute(db)
.await
.unwrap(); //Okay, because we can only create a Logbook of a valid id
return Ok(());
if self.arrival.is_none() {
if user.has_role(db, "admin").await
|| user.has_role(db, "Vorstand").await
|| user.id == self.shipmaster
{
let now = Local::now().naive_local();
let difference = now - self.departure;
if difference > Duration::hours(1) {
let vorstand = Role::find_by_name(db, "Vorstand").await.unwrap();
let logbook = LogbookWithBoatAndRowers::from(db, self.clone()).await;
let mut msg = format!("{} hat folgenden Logbuch-Eintrag jetzt gelöscht, welcher bereits vor über einer Stunde begonnen wurde: Schiffsführer: {}, Steuerperson: {}, Abfahrt: {}", user.name, logbook.steering_user.name, logbook.steering_user.name, logbook.logbook.departure.format("%Y-%m-%d %H:%M"));
if let Some(destination) = logbook.logbook.destination {
msg.push_str(&format!(", Ziel: {}", destination));
} else {
msg.push_str(", kein Ziel eingegeben");
}
msg.push_str(", Ruderer: ");
let mut it = logbook.rowers.clone().into_iter().peekable();
while let Some(rower) = it.next() {
msg.push_str(&rower.name);
if it.peek().is_some() {
msg.push_str(" + ");
}
}
Notification::create_for_role(
db,
&vorstand,
&msg,
"Ungewöhnliches Verhalten",
None,
None,
)
.await;
}
sqlx::query!("DELETE FROM logbook WHERE id=?", self.id)
.execute(db)
.await
.unwrap(); //Okay, because we can only create a Logbook of a valid id
return Ok(());
}
} else {
// Only admins can delete completed logbook entries
if user.has_role(db, "admin").await {
sqlx::query!("DELETE FROM logbook WHERE id=?", self.id)
.execute(db)
.await
.unwrap(); //Okay, because we can only create a Logbook of a valid id
return Ok(());
}
}
Err(LogbookDeleteError::NotYourEntry)
}
@ -953,21 +1066,6 @@ mod test {
assert_eq!(res, Err(LogbookCreateError::TooManyRowers(1, 2)));
}
#[sqlx::test]
fn test_distances() {
let pool = testdb!();
let res = Logbook::distances(&pool).await;
assert_eq!(
res,
vec![
("Ottensheim".into(), 25 as i64),
("Ottensheim + Regattastrecke".into(), 29 as i64),
]
);
}
#[sqlx::test]
fn test_succ_home() {
let pool = testdb!();

View File

@ -182,7 +182,7 @@ dein Vereinsbeitrag für das aktuelle Jahr beträgt {}€",
fees.name
))
}
content.push_str("\nBitte überweise diesen auf folgendes Konto: IBAN: AT13 1200 0804 1300 1200. Auf https://app.rudernlinz.at/planned findest du einen QR Code, den du mit deiner Bankapp scannen kannst um alle Eingaben bereits ausgefüllt zu haben.\n\n\
content.push_str("\nBitte überweise diesen auf folgendes Konto: IBAN: AT58 2032 0321 0072 9256. Auf https://app.rudernlinz.at/planned findest du einen QR Code, den du mit deiner Bankapp scannen kannst um alle Eingaben bereits ausgefüllt zu haben.\n\n\
Falls die Berechnung nicht stimmt (korrekte Preise findest du unter https://rudernlinz.at/unser-verein/gebuhren/) melde dich bitte bei it@rudernlinz.at. @Studenten: Bitte die aktuelle Studienbestätigung an it@rudernlinz.at schicken.\n\n\
Wenn du die Vereinsgebühren schon bezahlt hast, kannst du diese Mail einfach ignorieren.\n\n
Beste Grüße\n\
@ -293,7 +293,7 @@ Dein Vereinsbeitrag für das aktuelle Jahr beträgt {}€",
Gemäß § 7 Abs. 3 lit. c unseres Status behalten wir uns vor, bei ausbleibender Zahlung die Mitgliedschaft zu beenden. Dies möchten wir vermeiden und hoffen auf deine Unterstützung.\n\n\
Bei Fragen oder Problemen stehen wir gerne zur Verfügung.
Bankverbindung: IBAN: AT13 1200 0804 1300 1200 (Unter https://app.rudernlinz.at/planned findest du einen QR Code, den du mit deiner Bankapp scannen kannst um alle Eingaben bereits ausgefüllt zu haben.)
Bankverbindung: IBAN: AT58 2032 0321 0072 9256 (Unter https://app.rudernlinz.at/planned findest du einen QR Code, den du mit deiner Bankapp scannen kannst um alle Eingaben bereits ausgefüllt zu haben.)
Mit freundlichen Grüßen,\n\
Der Vorstand");

View File

@ -14,6 +14,7 @@ pub mod boat;
pub mod boatdamage;
pub mod boathouse;
pub mod boatreservation;
pub mod distance;
pub mod event;
pub mod family;
pub mod location;

View File

@ -179,6 +179,14 @@ ORDER BY read_at DESC, created_at DESC;
.await
.unwrap();
}
pub(crate) async fn delete_by_link(db: &sqlx::Pool<Sqlite>, link: &str) {
let link = Some(link);
sqlx::query!("DELETE FROM notification WHERE link=?", link)
.execute(db)
.await
.unwrap();
}
}
#[cfg(test)]

View File

@ -1,4 +1,4 @@
use chrono::NaiveDate;
use chrono::{Local, NaiveDate};
use serde::Serialize;
use sqlx::SqlitePool;
@ -77,23 +77,30 @@ impl Trip {
.await;
if same_starting_datetime.len() > 1 {
for notify in same_starting_datetime {
if notify.id != trip_details.id {
// notify everyone except oneself
if let Some(trip) = Trip::find_by_trip_details(db, notify.id).await {
let user = User::find_by_id(db, trip.cox_id as i32).await.unwrap();
Notification::create(
db,
&user,
&format!(
"{} hat eine Ausfahrt zur selben Zeit ({} um {}) wie du erstellt",
cox.user.name, trip.day, trip.planned_starting_time
),
"Neue Ausfahrt zur selben Zeit",
None,
None,
)
.await;
}
// don't notify oneself
if notify.id == trip_details.id {
continue;
}
// don't notify people who have cancelled their trip
if notify.cancelled(db) {
continue;
}
if let Some(trip) = Trip::find_by_trip_details(db, notify.id).await {
let user = User::find_by_id(db, trip.cox_id as i32).await.unwrap();
Notification::create(
db,
&user,
&format!(
"{} hat eine Ausfahrt zur selben Zeit ({} um {}) wie du erstellt",
cox.user.name, trip.day, trip.planned_starting_time
),
"Neue Ausfahrt zur selben Zeit",
None,
None,
)
.await;
}
}
}
@ -164,6 +171,11 @@ WHERE trip.id=?
}
}
pub async fn get_for_today(db: &SqlitePool) -> Vec<TripWithUserAndType> {
let today = Local::now().date_naive();
Self::get_for_day(db, today).await
}
pub async fn get_for_day(db: &SqlitePool, day: NaiveDate) -> Vec<TripWithUserAndType> {
let day = format!("{day}");
let trips = sqlx::query_as!(

View File

@ -77,6 +77,10 @@ WHERE day = ? AND planned_starting_time = ?
.await.unwrap()
}
pub fn cancelled(&self, db: &SqlitePool) -> bool {
self.max_people == 0
}
/// This function is called when a person registers to a trip or when the cox changes the
/// amount of free places.
pub async fn check_free_spaces(&self, db: &SqlitePool) {
@ -85,7 +89,7 @@ WHERE day = ? AND planned_starting_time = ?
return;
}
if self.max_people == 0 {
if self.cancelled(db) {
// Cox cancelled event, thus it's probably bad weather. Don't bother with sending
// notifications
return;
@ -213,7 +217,7 @@ ORDER BY day;",
pub(crate) async fn user_allowed_to_change(&self, db: &SqlitePool, user: &User) -> bool {
if self.belongs_to_event(db).await {
user.has_role(db, "planned_event").await
user.has_role(db, "manage_events").await
} else {
self.user_is_cox(db, user).await != CoxAtTrip::No
}

View File

@ -29,6 +29,7 @@ const REGULAR: i32 = 22000;
const UNTERSTUETZEND: i32 = 2500;
const FOERDERND: i32 = 8500;
pub const SCHECKBUCH: i32 = 3000;
const EINSCHREIBGEBUEHR: i32 = 3000;
#[derive(FromRow, Serialize, Deserialize, Clone, Debug, Eq, Hash, PartialEq)]
pub struct User {
@ -273,15 +274,15 @@ ASKÖ Ruderverein Donau Linz", self.name, SCHECKBUCH/100),
herzlich willkommen im ASKÖ Ruderverein Donau Linz! Wir freuen uns sehr, dich als neues Mitglied in unserem Verein begrüßen zu dürfen.
Um dir den Einstieg zu erleichtern, findest du in unserem Handbuch alle wichtigen Informationen über unseren Verein: https://rudernlinz.at/book. Bei weiteren Fragen stehen dir die Adressen info@rudernlinz.at und it@rudernlinz.at jederzeit zur Verfügung.
Um dir den Einstieg zu erleichtern, findest du in unserem Handbuch alle wichtigen Informationen über unseren Verein: https://rudernlinz.at/book. Bei weiteren Fragen stehen dir die Adressen info@rudernlinz.at (für allgemeine Fragen) und it@rudernlinz.at (bei technischen Fragen) jederzeit zur Verfügung.
Du kannst auch gerne unserer Signal-Gruppe beitreten, um auf dem Laufenden zu bleiben und dich mit anderen Mitgliedern auszutauschen: https://signal.group/#CjQKICFrq6zSsRHxrucS3jEcQn6lknEXacAykwwLV3vNLKxPEhA17jxz7cpjfu3JZokLq1TH
Für die Organisation unserer Ausfahrten nutzen wir app.rudernlinz.at. Logge dich einfach mit deinem Namen ('{0}' ohne Anführungszeichen) ein, beim ersten Mal kannst du das Passwortfeld leer lassen. Unter 'Geplante Ausfahrten' kannst du dich jederzeit zu den Ausfahrten anmelden.
Beim nächsten Treffen im Verein, erinnere mich (Philipp Hofer) bitte daran, deinen Fingerabdruck zu registrieren, damit du eigenständig Zugang zum Bootshaus erhältst.
Beim nächsten Treffen im Verein, erinnere jemand vom Vorstand (https://rudernlinz.at/unser-verein/vorstand/) bitte daran, deinen Fingerabdruck zu registrieren, damit du Zugang zum Bootshaus erhältst.
Außerdem haben wir im Bootshaus ein WLAN für Vereinsmitglieder 'ASKÖ Ruderverein Donau Linz'. Das Passwort dafür lautet 'donau1921' (ohne Anführungszeichen). Bitte gib das Passwort an keine vereinsfremden Personen weiter.
Damit du dich noch mehr verbunden fühlst (:-)), haben wir im Bootshaus ein WLAN für Vereinsmitglieder 'ASKÖ Ruderverein Donau Linz' eingerichtet. Das Passwort dafür lautet 'donau1921' (ohne Anführungszeichen). Bitte gib das Passwort an keine vereinsfremden Personen weiter.
Wir freuen uns darauf, dich bald am Wasser zu sehen und gemeinsam tolle Erfahrungen zu sammeln!
@ -361,17 +362,49 @@ ASKÖ Ruderverein Donau Linz", self.name),
);
}
if let Some(member_since_date) = &self.member_since_date {
if let Ok(member_since_date) = NaiveDate::parse_from_str(member_since_date, "%Y-%m-%d")
{
if member_since_date.year() == Local::now().year()
&& !self.has_role(db, "no-einschreibgebuehr").await
{
fee.add("Einschreibgebühr".into(), EINSCHREIBGEBUEHR);
}
}
}
let halfprice = if let Some(member_since_date) = &self.member_since_date {
if let Ok(member_since_date) = NaiveDate::parse_from_str(member_since_date, "%Y-%m-%d")
{
let halfprice_startdate =
NaiveDate::from_ymd_opt(Local::now().year(), 7, 1).unwrap();
member_since_date >= halfprice_startdate
} else {
false
}
} else {
false
};
if self.has_role(db, "Unterstützend").await {
fee.add("Unterstützendes Mitglied".into(), UNTERSTUETZEND);
} else if self.has_role(db, "Förderndes Mitglied").await {
fee.add("Förderndes Mitglied".into(), FOERDERND);
} else if Family::find_by_opt_id(db, self.family_id).await.is_none() {
if self.has_role(db, "Student").await || self.has_role(db, "Schüler").await {
fee.add("Schüler/Student".into(), STUDENT_OR_PUPIL);
if halfprice {
fee.add("Schüler/Student (Halbpreis)".into(), STUDENT_OR_PUPIL / 2);
} else {
fee.add("Schüler/Student".into(), STUDENT_OR_PUPIL);
}
} else if self.has_role(db, "Ehrenmitglied").await {
fee.add("Ehrenmitglied".into(), 0);
} else {
fee.add("Mitgliedsbeitrag".into(), REGULAR);
if halfprice {
fee.add("Mitgliedsbeitrag (Halbpreis)".into(), REGULAR / 2);
} else {
fee.add("Mitgliedsbeitrag".into(), REGULAR);
}
}
}
@ -627,6 +660,14 @@ ORDER BY last_access DESC
.is_ok()
}
pub async fn create_with_mail(db: &SqlitePool, name: &str, mail: &str) -> bool {
let name = name.trim();
sqlx::query!("INSERT INTO USER(name, mail) VALUES (?, ?)", name, mail)
.execute(db)
.await
.is_ok()
}
pub async fn update(&self, db: &SqlitePool, data: UserEditForm<'_>) {
let mut family_id = data.family_id;
@ -714,6 +755,7 @@ ORDER BY last_access DESC
if ![
"n-sageder",
"p-hofer",
"marie-birner",
"daniel-kortschak",
"rudernlinz",
"m-birner",
@ -896,266 +938,83 @@ impl<'r> FromRequest<'r> for User {
}
}
pub struct TechUser {
pub(crate) user: User,
}
/// Creates a struct named $name. Allows to be created from a user, if one of the specified $roles are active for the user.
macro_rules! special_user {
($name:ident, $($role:tt)*) => {
#[derive(Debug)]
pub struct $name {
pub(crate) user: User,
}
impl Deref for TechUser {
type Target = User;
impl Deref for $name {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.user
}
}
fn deref(&self) -> &Self::Target {
&self.user
}
}
impl $name {
pub fn into_inner(self) -> User {
self.user
}
}
#[async_trait]
impl<'r> FromRequest<'r> for TechUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "tech").await {
Outcome::Success(TechUser { user })
} else {
Outcome::Error((Status::Forbidden, LoginError::NotACox))
#[async_trait]
impl<'r> FromRequest<'r> for $name {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if special_user!(@check_roles user, db, $($role)*) {
Outcome::Success($name { user })
} else {
Outcome::Forward(Status::Forbidden)
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
pub struct CoxUser {
pub(crate) user: User,
}
impl Deref for CoxUser {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.user
}
}
impl CoxUser {
pub async fn new(db: &SqlitePool, user: User) -> Option<Self> {
if user.has_role(db, "cox").await {
Some(CoxUser { user })
} else {
None
}
}
}
#[async_trait]
impl<'r> FromRequest<'r> for CoxUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "cox").await {
Outcome::Success(CoxUser { user })
impl $name {
pub async fn new(db: &SqlitePool, user: User) -> Option<Self> {
if special_user!(@check_roles user, db, $($role)*) {
Some($name { user })
} else {
Outcome::Error((Status::Forbidden, LoginError::NotACox))
None
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AdminUser {
pub(crate) user: User,
}
#[async_trait]
impl<'r> FromRequest<'r> for AdminUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "admin").await {
Outcome::Success(AdminUser { user })
} else {
Outcome::Forward(Status::Forbidden)
};
(@check_roles $user:ident, $db:ident, $(+$role:expr),* $(,-$neg_role:expr)*) => {
{
let mut has_positive_role = false;
$(
if $user.has_role($db, $role).await {
has_positive_role = true;
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
)*
has_positive_role
$(
&& !$user.has_role($db, $neg_role).await
)*
}
}
};
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AllowedForPlannedTripsUser(pub(crate) User);
special_user!(TechUser, +"tech");
special_user!(CoxUser, +"cox");
special_user!(AdminUser, +"admin");
special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch");
special_user!(DonauLinzUser, +"Donau Linz", -"Unterstützend", -"Förderndes Mitglied");
special_user!(SchnupperBetreuerUser, +"schnupper-betreuer");
special_user!(VorstandUser, +"Vorstand");
special_user!(EventUser, +"manage_events");
special_user!(AllowedToEditPaymentStatusUser, +"kassier", +"admin");
special_user!(ManageUserUser, +"admin", +"schriftfuehrer");
#[async_trait]
impl<'r> FromRequest<'r> for AllowedForPlannedTripsUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "Donau Linz").await | user.has_role(db, "scheckbuch").await {
Outcome::Success(AllowedForPlannedTripsUser(user))
} else {
Outcome::Error((Status::Forbidden, LoginError::NotACox))
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
impl From<AllowedForPlannedTripsUser> for User {
fn from(val: AllowedForPlannedTripsUser) -> Self {
val.0
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct DonauLinzUser(pub(crate) User);
impl From<DonauLinzUser> for User {
fn from(val: DonauLinzUser) -> Self {
val.0
}
}
impl Deref for DonauLinzUser {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[async_trait]
impl<'r> FromRequest<'r> for DonauLinzUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "Donau Linz").await
&& !user.has_role(db, "Unterstützend").await
&& !user.has_role(db, "Förderndes Mitglied").await
{
Outcome::Success(DonauLinzUser(user))
} else {
Outcome::Error((Status::Forbidden, LoginError::NotACox))
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SchnupperBetreuerUser(pub(crate) User);
impl From<SchnupperBetreuerUser> for User {
fn from(val: SchnupperBetreuerUser) -> Self {
val.0
}
}
impl Deref for SchnupperBetreuerUser {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[async_trait]
impl<'r> FromRequest<'r> for SchnupperBetreuerUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "schnupper-betreuer").await {
Outcome::Success(SchnupperBetreuerUser(user))
} else {
Outcome::Forward(Status::Forbidden)
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct VorstandUser(pub(crate) User);
impl From<VorstandUser> for User {
fn from(val: VorstandUser) -> Self {
val.0
}
}
impl Deref for VorstandUser {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[async_trait]
impl<'r> FromRequest<'r> for VorstandUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "Vorstand").await {
Outcome::Success(VorstandUser(user))
} else {
Outcome::Forward(Status::Forbidden)
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct EventUser(pub(crate) User);
impl From<EventUser> for User {
fn from(val: EventUser) -> Self {
val.0
}
}
impl Deref for EventUser {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(FromRow, Serialize, Deserialize, Clone, Debug)]
pub struct UserWithRolesAndMembershipPdf {
#[serde(flatten)]
@ -1199,25 +1058,6 @@ impl UserWithMembershipPdf {
}
}
#[async_trait]
impl<'r> FromRequest<'r> for EventUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "manage_events").await {
Outcome::Success(EventUser(user))
} else {
Outcome::Error((Status::Forbidden, LoginError::NotACox))
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
#[cfg(test)]
mod test {
use std::collections::HashMap;
@ -1266,7 +1106,7 @@ mod test {
fn test_cox() {
let pool = testdb!();
let res = User::cox(&pool).await;
assert_eq!(res.len(), 3);
assert_eq!(res.len(), 4);
}
#[sqlx::test]

View File

@ -14,8 +14,12 @@ pub fn schedule(db: &SqlitePool, config: &Config) {
let openweathermap_key = config.openweathermap_key.clone();
tokio::task::spawn(async {
waterlevel::update(&db).await.unwrap();
weather::update(&db, &openweathermap_key).await.unwrap();
if let Err(e) = waterlevel::update(&db).await {
log::error!("Water level update error: {e}, trying again next time");
}
if let Err(e) = weather::update(&db, &openweathermap_key).await {
log::error!("Weather update error: {e}, trying again next time");
}
let mut sched = JobScheduler::new();
@ -26,10 +30,12 @@ pub fn schedule(db: &SqlitePool, config: &Config) {
// nicer one's rust (stable) support async closures
task::block_in_place(|| {
tokio::runtime::Handle::current().block_on(async {
waterlevel::update(&db_clone).await.unwrap();
weather::update(&db_clone, &openweathermap_key)
.await
.unwrap();
if let Err(e) = waterlevel::update(&db_clone).await {
log::error!("Water level update error: {e}, trying again next time");
}
if let Err(e) = weather::update(&db_clone, &openweathermap_key).await {
log::error!("Weather update error: {e}, trying again next time");
}
});
});
}));

View File

@ -9,14 +9,14 @@ use sqlx::SqlitePool;
use crate::model::log::Log;
use crate::model::mail::Mail;
use crate::model::role::Role;
use crate::model::user::AdminUser;
use crate::model::user::UserWithDetails;
use crate::model::user::{AdminUser, VorstandUser};
use crate::tera::Config;
#[get("/mail")]
async fn index(
db: &State<SqlitePool>,
admin: AdminUser,
admin: VorstandUser,
flash: Option<FlashMessage<'_>>,
) -> Template {
let mut context = Context::new();
@ -65,7 +65,7 @@ async fn update(
db: &State<SqlitePool>,
data: Form<MailToSend<'_>>,
config: &State<Config>,
admin: AdminUser,
admin: VorstandUser,
) -> Flash<Redirect> {
let d = data.into_inner();
Log::create(db, format!("{admin:?} trying to send this mail: {d:?}")).await;

View File

@ -2,7 +2,7 @@ use crate::model::{
log::Log,
notification::Notification,
role::Role,
user::{AdminUser, User, UserWithDetails},
user::{User, UserWithDetails, VorstandUser},
};
use itertools::Itertools;
use rocket::{
@ -18,7 +18,7 @@ use sqlx::SqlitePool;
#[get("/notification")]
async fn index(
db: &State<SqlitePool>,
user: AdminUser,
user: VorstandUser,
flash: Option<FlashMessage<'_>>,
) -> Template {
let mut context = Context::new();
@ -62,7 +62,7 @@ pub struct NotificationToSendUser {
async fn send_group(
db: &State<SqlitePool>,
data: Form<NotificationToSendGroup>,
admin: AdminUser,
admin: VorstandUser,
) -> Flash<Redirect> {
let d = data.into_inner();
Log::create(
@ -89,7 +89,7 @@ async fn send_group(
async fn send_user(
db: &State<SqlitePool>,
data: Form<NotificationToSendUser>,
admin: AdminUser,
admin: VorstandUser,
) -> Flash<Redirect> {
let d = data.into_inner();
Log::create(

View File

@ -29,7 +29,7 @@ async fn index(
context.insert("schnupperanten", &users);
context.insert(
"loggedin_user",
&UserWithDetails::from_user(user.into(), db).await,
&UserWithDetails::from_user(user.user, db).await,
);
Template::render("admin/schnupper/index", context.into_json())

View File

@ -7,13 +7,14 @@ use crate::{
logbook::Logbook,
role::Role,
user::{
AdminUser, User, UserWithDetails, UserWithMembershipPdf, UserWithRolesAndMembershipPdf,
VorstandUser,
AdminUser, AllowedToEditPaymentStatusUser, ManageUserUser, SchnupperBetreuerUser, User,
UserWithDetails, UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser,
},
},
tera::Config,
};
use futures::future::join_all;
use lettre::Address;
use rocket::{
form::Form,
fs::TempFile,
@ -54,8 +55,8 @@ async fn index(
.map(|u| async move { UserWithRolesAndMembershipPdf::from_user(db, u).await })
.collect();
let user: User = user.into();
let allowed_to_edit = user.has_role(db, "admin").await;
let user: User = user.into_inner();
let allowed_to_edit = ManageUserUser::new(db, user.clone()).await.is_some();
let users: Vec<UserWithRolesAndMembershipPdf> = join_all(user_futures).await;
@ -89,7 +90,7 @@ async fn index_admin(
let users: Vec<UserWithRolesAndMembershipPdf> = join_all(user_futures).await;
let user: User = user.user;
let allowed_to_edit = user.has_role(db, "admin").await;
let allowed_to_edit = ManageUserUser::new(db, user.clone()).await.is_some();
let roles = Role::all(db).await;
let families = Family::all_with_members(db).await;
@ -110,7 +111,7 @@ async fn index_admin(
#[get("/user/fees")]
async fn fees(
db: &State<SqlitePool>,
admin: VorstandUser,
user: VorstandUser,
flash: Option<FlashMessage<'_>>,
) -> Template {
let mut context = Context::new();
@ -130,7 +131,7 @@ async fn fees(
}
context.insert(
"loggedin_user",
&UserWithDetails::from_user(admin.into(), db).await,
&UserWithDetails::from_user(user.into_inner(), db).await,
);
Template::render("admin/user/fees", context.into_json())
@ -161,7 +162,7 @@ async fn scheckbuch(
}
context.insert(
"loggedin_user",
&UserWithDetails::from_user(user.into(), db).await,
&UserWithDetails::from_user(user.into_inner(), db).await,
);
Template::render("admin/user/scheckbuch", context.into_json())
@ -170,7 +171,7 @@ async fn scheckbuch(
#[get("/user/fees/paid?<user_ids>")]
async fn fees_paid(
db: &State<SqlitePool>,
admin: AdminUser,
calling_user: AllowedToEditPaymentStatusUser,
user_ids: Vec<i32>,
referer: Referer,
) -> Flash<Redirect> {
@ -181,7 +182,10 @@ async fn fees_paid(
if user.has_role(db, "paid").await {
Log::create(
db,
format!("{} set fees NOT paid for '{}'", admin.user.name, user.name),
format!(
"{} set fees NOT paid for '{}'",
calling_user.user.name, user.name
),
)
.await;
user.remove_role(db, &Role::find_by_name(db, "paid").await.unwrap())
@ -189,7 +193,10 @@ async fn fees_paid(
} else {
Log::create(
db,
format!("{} set fees paid for '{}'", admin.user.name, user.name),
format!(
"{} set fees paid for '{}'",
calling_user.user.name, user.name
),
)
.await;
user.add_role(db, &Role::find_by_name(db, "paid").await.unwrap())
@ -208,7 +215,7 @@ async fn fees_paid(
#[get("/user/<user>/send-welcome-mail")]
async fn send_welcome_mail(
db: &State<SqlitePool>,
_admin: AdminUser,
_admin: ManageUserUser,
config: &State<Config>,
user: i32,
) -> Flash<Redirect> {
@ -226,7 +233,7 @@ async fn send_welcome_mail(
}
#[get("/user/<user>/reset-pw")]
async fn resetpw(db: &State<SqlitePool>, admin: AdminUser, user: i32) -> Flash<Redirect> {
async fn resetpw(db: &State<SqlitePool>, admin: ManageUserUser, user: i32) -> Flash<Redirect> {
let user = User::find_by_id(db, user).await;
match user {
Some(user) => {
@ -246,7 +253,7 @@ async fn resetpw(db: &State<SqlitePool>, admin: AdminUser, user: i32) -> Flash<R
}
#[get("/user/<user>/delete")]
async fn delete(db: &State<SqlitePool>, admin: AdminUser, user: i32) -> Flash<Redirect> {
async fn delete(db: &State<SqlitePool>, admin: ManageUserUser, user: i32) -> Flash<Redirect> {
let user = User::find_by_id(db, user).await;
Log::create(db, format!("{} deleted user: {user:?}", admin.user.name)).await;
match user {
@ -283,7 +290,7 @@ pub struct UserEditForm<'a> {
async fn update(
db: &State<SqlitePool>,
data: Form<UserEditForm<'_>>,
admin: AdminUser,
admin: ManageUserUser,
) -> Flash<Redirect> {
let user = User::find_by_id(db, data.id).await;
Log::create(
@ -306,7 +313,7 @@ async fn update(
#[get("/user/<user>/membership")]
async fn download_membership_pdf(
db: &State<SqlitePool>,
admin: AdminUser,
admin: ManageUserUser,
user: i32,
) -> (ContentType, Vec<u8>) {
let user = User::find_by_id(db, user).await.unwrap();
@ -332,7 +339,7 @@ struct UserAddForm<'r> {
async fn create(
db: &State<SqlitePool>,
data: Form<UserAddForm<'_>>,
admin: AdminUser,
admin: ManageUserUser,
) -> Flash<Redirect> {
if User::create(db, data.name).await {
Log::create(
@ -349,6 +356,104 @@ async fn create(
}
}
#[derive(FromForm, Debug)]
struct UserAddScheckbuchForm<'r> {
name: &'r str,
mail: &'r str,
}
#[post("/user/new/scheckbuch", data = "<data>")]
async fn create_scheckbuch(
db: &State<SqlitePool>,
data: Form<UserAddScheckbuchForm<'_>>,
admin: VorstandUser,
config: &State<Config>,
) -> Flash<Redirect> {
// 1. Check mail adress
let mail = data.mail.trim();
if mail.parse::<Address>().is_err() {
return Flash::error(
Redirect::to("/admin/user/scheckbuch"),
format!("Keine gültige Mailadresse"),
);
}
// 2. Check name
let name = data.name.trim();
if User::find_by_name(db, name).await.is_some() {
return Flash::error(
Redirect::to("/admin/user/scheckbuch"),
format!(
"Kann kein Scheckbuch erstellen, der Name wird bereits von einem User verwendet"
),
);
}
// 3. Create user
User::create_with_mail(db, name, mail).await;
let user = User::find_by_name(db, name).await.unwrap();
// 4. Add 'scheckbuch' role
let scheckbuch = Role::find_by_name(db, "scheckbuch").await.unwrap();
user.add_role(db, &scheckbuch).await;
// 4. Send welcome mail (+ notification)
user.send_welcome_email(db, &config.smtp_pw).await.unwrap();
Log::create(
db,
format!("{} created new scheckbuch: {data:?}", admin.name),
)
.await;
Flash::success(Redirect::to("/admin/user/scheckbuch"), &format!("Scheckbuch erfolgreich erstellt. Eine E-Mail in der alles erklärt wird, wurde an {mail} verschickt."))
}
#[get("/user/move/schnupperant/<id>/to/scheckbuch")]
async fn schnupper_to_scheckbuch(
db: &State<SqlitePool>,
id: i32,
admin: SchnupperBetreuerUser,
config: &State<Config>,
) -> Flash<Redirect> {
let Some(user) = User::find_by_id(db, id).await else {
return Flash::error(
Redirect::to("/admin/schnupper"),
format!("user id not found"),
);
};
if !user.has_role(db, "schnupperant").await {
return Flash::error(
Redirect::to("/admin/schnupper"),
format!("kein schnupperant..."),
);
}
let schnupperant = Role::find_by_name(db, "schnupperant").await.unwrap();
let paid = Role::find_by_name(db, "paid").await.unwrap();
user.remove_role(db, &schnupperant).await;
user.remove_role(db, &paid).await;
let scheckbuch = Role::find_by_name(db, "scheckbuch").await.unwrap();
user.add_role(db, &scheckbuch).await;
if let Some(no_einschreibgebuehr) = Role::find_by_name(db, "no-einschreibgebuehr").await {
user.add_role(db, &no_einschreibgebuehr).await;
}
user.send_welcome_email(db, &config.smtp_pw).await.unwrap();
Log::create(
db,
format!(
"{} created new scheckbuch (from schnupperant): {}",
admin.name, user.name
),
)
.await;
Flash::success(Redirect::to("/admin/schnupper"), &format!("Scheckbuch erfolgreich erstellt. Eine E-Mail in der alles erklärt wird, wurde an {} verschickt.", user.mail.unwrap()))
}
pub fn routes() -> Vec<Route> {
routes![
index,
@ -356,6 +461,8 @@ pub fn routes() -> Vec<Route> {
resetpw,
update,
create,
create_scheckbuch,
schnupper_to_scheckbuch,
delete,
fees,
fees_paid,

View File

@ -39,7 +39,7 @@ async fn index(
context.insert(
"loggedin_user",
&UserWithDetails::from_user(admin.into(), db).await,
&UserWithDetails::from_user(admin.into_inner(), db).await,
);
Template::render("board/boathouse", context.into_json())

View File

@ -59,7 +59,7 @@ async fn index(
context.insert("boats", &boats);
context.insert(
"loggedin_user",
&UserWithDetails::from_user(user.into(), db).await,
&UserWithDetails::from_user(user.into_inner(), db).await,
);
Template::render("boatdamages", context.into_json())
@ -78,7 +78,7 @@ async fn create<'r>(
data: Form<FormBoatDamageToAdd<'r>>,
user: DonauLinzUser,
) -> Flash<Redirect> {
let user: User = user.into();
let user: User = user.into_inner();
let boatdamage_to_add = BoatDamageToAdd {
boat_id: data.boat_id,
desc: data.desc,

View File

@ -75,7 +75,7 @@ async fn index(
context.insert("user", &User::all(db).await);
context.insert(
"loggedin_user",
&UserWithDetails::from_user(user.into(), db).await,
&UserWithDetails::from_user(user.into_inner(), db).await,
);
Template::render("boatreservations", context.into_json())
@ -97,7 +97,7 @@ async fn create<'r>(
data: Form<FormBoatReservationToAdd<'r>>,
user: DonauLinzUser,
) -> Flash<Redirect> {
let user_applicant: User = user.into();
let user_applicant: User = user.into_inner();
let boat = Boat::find_by_id(db, data.boat_id as i32).await.unwrap();
let boatreservation_to_add = BoatReservationToAdd {
boat: &boat,

View File

@ -18,13 +18,15 @@ use tera::Context;
use crate::model::{
boat::Boat,
boatreservation::BoatReservation,
distance::Distance,
log::Log,
logbook::{
LogToAdd, LogToFinalize, Logbook, LogbookCreateError, LogbookDeleteError,
LogbookUpdateError,
LogToAdd, LogToFinalize, LogToUpdate, Logbook, LogbookAdminUpdateError, LogbookCreateError,
LogbookDeleteError, LogbookUpdateError,
},
logtype::LogType,
user::{AdminUser, DonauLinzUser, User, UserWithDetails},
trip::Trip,
user::{AdminUser, DonauLinzUser, User, UserWithDetails, VorstandUser},
};
pub struct KioskCookie(());
@ -68,11 +70,13 @@ async fn index(
)
.await;
users.retain(|u| {
u.roles.contains(&"Donau Linz".into()) || u.roles.contains(&"scheckbuch".into())
u.roles.contains(&"Donau Linz".into())
|| u.roles.contains(&"scheckbuch".into())
|| u.user.name == "Externe Steuerperson"
});
let logtypes = LogType::all(db).await;
let distances = Logbook::distances(db).await;
let distances = Distance::all(db).await;
let on_water = Logbook::on_water(db).await;
@ -82,6 +86,7 @@ async fn index(
}
context.insert("boats", &boats);
context.insert("planned_trips", &Trip::get_for_today(db).await);
context.insert(
"reservations",
&BoatReservation::all_future_with_groups(db).await,
@ -91,7 +96,7 @@ async fn index(
context.insert("logtypes", &logtypes);
context.insert(
"loggedin_user",
&UserWithDetails::from_user(user.into(), db).await,
&UserWithDetails::from_user(user.into_inner(), db).await,
);
context.insert("on_water", &on_water);
context.insert("distances", &distances);
@ -105,7 +110,7 @@ async fn show(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
Template::render(
"log.completed",
context!(logs, loggedin_user: &UserWithDetails::from_user(user.into(), db).await),
context!(logs, loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await),
)
}
@ -176,7 +181,7 @@ async fn kiosk(
});
let logtypes = LogType::all(db).await;
let distances = Logbook::distances(db).await;
let distances = Distance::all(db).await;
let on_water = Logbook::on_water(db).await;
@ -185,6 +190,7 @@ async fn kiosk(
context.insert("flash", &msg.into_inner());
}
context.insert("planned_trips", &Trip::get_for_today(db).await);
context.insert("boats", &boats);
context.insert(
"reservations",
@ -228,6 +234,8 @@ async fn create_logbook(
Err(LogbookCreateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), "Nur Ausfahrten, die in der letzten Woche enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten Philipp (Tel. nr. siehe Signal oder it@rudernlinz.at)."),
Err(LogbookCreateError::CantChangeHandoperatableStatusForThisBoat) => Flash::error(Redirect::to("/log"), "Handsteuer-Status dieses Boots kann nicht verändert werden."),
Err(LogbookCreateError::TooFast(km, min)) => Flash::error(Redirect::to("/log"), format!("KM zu groß für die eingegebene Dauer ({km} km in {min} Minuten). Bitte überprüfe deine Start- und Endzeit und versuche es erneut.")),
Err(LogbookCreateError::AlreadyFinalized) => Flash::error(Redirect::to("/log"), "Logbucheintrag wurde bereits abgeschlossen."),
Err(LogbookCreateError::ExternalSteeringPersonMustSteerOrShipmaster) => Flash::error(Redirect::to("/log"), "Wenn du eine 'Externe Steuerperson' hinzufügst, muss diese steuern oder Schiffsführer sein!"),
}
}
@ -279,13 +287,49 @@ async fn create_kiosk(
)
.await;
create_logbook(db, data, &DonauLinzUser(creator)).await //TODO: fixme
create_logbook(db, data, &DonauLinzUser::new(db, creator).await.unwrap()).await
//TODO: fixme
}
#[post("/update", data = "<data>")]
async fn update(
db: &State<SqlitePool>,
data: Form<LogToUpdate>,
user: VorstandUser,
) -> Flash<Redirect> {
let data = data.into_inner();
let Some(logbook) = Logbook::find_by_id(db, data.id).await else {
return Flash::error(Redirect::to("/log"), &format!("Logbucheintrag kann nicht bearbeitet werden, da es einen Logbuch-Eintrag mit ID={} nicht gibt", data.id));
};
match logbook.update(db, data.clone(), &user.user).await {
Ok(()) => {
Log::create(
db,
format!(
"User {} updated log entry={:?} to {:?}",
&user.name, logbook, data
),
)
.await;
Flash::success(
Redirect::to("/log/show"),
"Logbucheintrag erfolgreich bearbeitet".to_string(),
)
}
Err(LogbookAdminUpdateError::NotAllowed) => Flash::error(
Redirect::to("/log/show"),
"Du hast keine Erlaubnis, diesen Logbucheintrag zu bearbeiten!".to_string(),
),
}
}
async fn home_logbook(
db: &SqlitePool,
data: Form<LogToFinalize>,
logbook_id: i32,
logbook_id: i64,
user: &DonauLinzUser,
) -> Flash<Redirect> {
let logbook: Option<Logbook> = Logbook::find_by_id(db, logbook_id).await;
@ -301,6 +345,9 @@ async fn home_logbook(
Err(LogbookUpdateError::TooManyRowers(expected, actual)) => Flash::error(Redirect::to("/log"), format!("Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)")),
Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), "Nur Ausfahrten, die heute enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten Philipp (Tel. nr. siehe Signal oder it@rudernlinz.at)."),
Err(LogbookUpdateError::TooFast(km, min)) => Flash::error(Redirect::to("/log"), format!("KM zu groß für die eingegebene Dauer ({km} km in {min} Minuten). Bitte überprüfe deine Start- und Endzeit und versuche es erneut.")),
Err(LogbookUpdateError::AlreadyFinalized) => Flash::error(Redirect::to("/log"), "Logbucheintrag wurde bereits abgeschlossen."),
Err(LogbookUpdateError::ExternalSteeringPersonMustSteerOrShipmaster) => Flash::error(Redirect::to("/log"), "Wenn du eine 'Externe Steuerperson' hinzufügst, muss diese steuern oder Schiffsführer sein!"),
Err(LogbookUpdateError::BoatAlreadyOnWater) => Flash::error(Redirect::to("/log"), "Das Boot war in diesem Zeitraum schon am Wasser. Bitte überprüfe das Datum und die Zeit."),
Err(e) => Flash::error(
Redirect::to("/log"),
format!("Eintrag {logbook_id} konnte nicht abgesendet werden (Fehler: {e:?})!"),
@ -312,7 +359,7 @@ async fn home_logbook(
async fn home_kiosk(
db: &State<SqlitePool>,
data: Form<LogToFinalize>,
logbook_id: i32,
logbook_id: i64,
_kiosk: KioskCookie,
) -> Flash<Redirect> {
let logbook = Logbook::find_by_id(db, logbook_id).await.unwrap(); //TODO: fixme
@ -327,11 +374,14 @@ async fn home_kiosk(
db,
data,
logbook_id,
&DonauLinzUser(
&DonauLinzUser::new(
db,
User::find_by_id(db, logbook.shipmaster as i32)
.await
.unwrap(),
), //TODO: fixme
)
.await
.unwrap(),
)
.await
}
@ -340,7 +390,7 @@ async fn home_kiosk(
async fn home(
db: &State<SqlitePool>,
data: Form<LogToFinalize>,
logbook_id: i32,
logbook_id: i64,
user: DonauLinzUser,
) -> Flash<Redirect> {
Log::create(
@ -356,9 +406,14 @@ async fn home(
}
#[get("/<logbook_id>/delete", rank = 2)]
async fn delete(db: &State<SqlitePool>, logbook_id: i32, user: DonauLinzUser) -> Flash<Redirect> {
async fn delete(db: &State<SqlitePool>, logbook_id: i64, user: DonauLinzUser) -> Flash<Redirect> {
let logbook = Logbook::find_by_id(db, logbook_id).await;
if let Some(logbook) = logbook {
let redirect = if logbook.arrival.is_some() {
"/log/show"
} else {
"/log"
};
Log::create(
db,
format!("User {} tries to delete log entry {logbook_id}", &user.name),
@ -366,11 +421,11 @@ async fn delete(db: &State<SqlitePool>, logbook_id: i32, user: DonauLinzUser) ->
.await;
match logbook.delete(db, &user).await {
Ok(_) => Flash::success(
Redirect::to("/log"),
format!("Eintrag {} gelöscht!", logbook_id),
Redirect::to(redirect),
format!("Eintrag {} von {} gelöscht!", logbook_id, user.name),
),
Err(LogbookDeleteError::NotYourEntry) => Flash::error(
Redirect::to("/log"),
Redirect::to(redirect),
"Du hast nicht die Berechtigung, den Eintrag zu löschen!",
),
}
@ -385,7 +440,7 @@ async fn delete(db: &State<SqlitePool>, logbook_id: i32, user: DonauLinzUser) ->
#[get("/<logbook_id>/delete")]
async fn delete_kiosk(
db: &State<SqlitePool>,
logbook_id: i32,
logbook_id: i64,
_kiosk: KioskCookie,
) -> Flash<Redirect> {
let logbook = Logbook::find_by_id(db, logbook_id).await;
@ -425,7 +480,8 @@ pub fn routes() -> Vec<Route> {
show_kiosk,
show_for_year,
delete,
delete_kiosk
delete_kiosk,
update
]
}

View File

@ -118,6 +118,60 @@ fn unauthorized_error(req: &Request) -> Redirect {
Redirect::to("/auth")
}
#[derive(FromForm, Debug)]
struct NewBlogpostForm<'r> {
article_url: &'r str,
article_title: &'r str,
pw: &'r str,
}
#[post("/", data = "<blogpost>")]
async fn new_blogpost(
db: &State<SqlitePool>,
blogpost: Form<NewBlogpostForm<'_>>,
config: &State<Config>,
) -> String {
if blogpost.pw == &config.wordpress_key {
let member = Role::find_by_name(&db, "Donau Linz").await.unwrap();
Notification::create_for_role(
db,
&member,
&urlencoding::decode(blogpost.article_title).expect("UTF-8"),
&format!("Neuer Blogpost"),
Some(blogpost.article_url),
None,
)
.await;
"ACK".into()
} else {
"WRONG pw".into()
}
}
#[derive(FromForm, Debug)]
struct BlogpostUnpublishedForm<'r> {
article_url: &'r str,
pw: &'r str,
}
#[post("/", data = "<blogpost>")]
async fn blogpost_unpublished(
db: &State<SqlitePool>,
blogpost: Form<BlogpostUnpublishedForm<'_>>,
config: &State<Config>,
) -> String {
if blogpost.pw == &config.wordpress_key {
Notification::delete_by_link(
&db,
&urlencoding::decode(blogpost.article_url).expect("UTF-8"),
)
.await;
"ACK".into()
} else {
"WRONG pw".into()
}
}
#[catch(403)] //forbidden
fn forbidden_error() -> Flash<Redirect> {
Flash::error(Redirect::to("/"), "Keine Berechtigung für diese Aktion. Wenn du der Meinung bist, dass du das machen darfst, melde dich bitte bei it@rudernlinz.at.")
@ -187,6 +241,7 @@ pub struct Config {
smtp_pw: String,
usage_log_path: String,
pub openweathermap_key: String,
wordpress_key: String,
}
pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
@ -194,6 +249,8 @@ pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
.mount("/", routes![index, steering, impressum])
.mount("/auth", auth::routes())
.mount("/wikiauth", routes![wikiauth])
.mount("/new-blogpost", routes![new_blogpost])
.mount("/blogpost-unpublished", routes![blogpost_unpublished])
.mount("/log", log::routes())
.mount("/planned", planned::routes())
.mount("/ergo", ergo::routes())

View File

@ -22,7 +22,7 @@ async fn index(
user: AllowedForPlannedTripsUser,
flash: Option<FlashMessage<'_>>,
) -> Template {
let user: User = user.into();
let user: User = user.into_inner();
let mut context = Context::new();
@ -50,7 +50,7 @@ async fn join(
user: AllowedForPlannedTripsUser,
user_note: Option<String>,
) -> Flash<Redirect> {
let user: User = user.into();
let user: User = user.into_inner();
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
return Flash::error(Redirect::to("/"), "Trip_details do not exist.");
@ -113,7 +113,7 @@ async fn remove_guest(
user: AllowedForPlannedTripsUser,
name: String,
) -> Flash<Redirect> {
let user: User = user.into();
let user: User = user.into_inner();
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
return Flash::error(Redirect::to("/planned"), "TripDetailsId does not exist");
@ -160,7 +160,7 @@ async fn remove(
trip_details_id: i64,
user: AllowedForPlannedTripsUser,
) -> Flash<Redirect> {
let user: User = user.into();
let user: User = user.into_inner();
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
return Flash::error(Redirect::to("/planned"), "TripDetailsId does not exist");

View File

@ -16,7 +16,7 @@ async fn index_boat(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
Template::render(
"stat.boats",
context!(loggedin_user: &UserWithDetails::from_user(user.into(), db).await, stat, kiosk),
context!(loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await, stat, kiosk),
)
}
@ -38,7 +38,7 @@ async fn index(db: &State<SqlitePool>, user: DonauLinzUser, year: Option<i32>) -
Template::render(
"stat.people",
context!(loggedin_user: &UserWithDetails::from_user(user.into(), db).await, stat, personal, kiosk, guest_km, club_km),
context!(loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await, stat, personal, kiosk, guest_km, club_km),
)
}

View File

@ -59,7 +59,7 @@ async fn index(
context.insert("user", &User::all(db).await);
context.insert(
"loggedin_user",
&UserWithDetails::from_user(user.into(), db).await,
&UserWithDetails::from_user(user.into_inner(), db).await,
);
Template::render("trailerreservations", context.into_json())
@ -81,7 +81,7 @@ async fn create<'r>(
data: Form<FormTrailerReservationToAdd<'r>>,
user: DonauLinzUser,
) -> Flash<Redirect> {
let user_applicant: User = user.into();
let user_applicant: User = user.into_inner();
let trailer = Trailer::find_by_id(db, data.trailer_id as i32)
.await
.unwrap();

View File

@ -12,7 +12,13 @@
{% for user in schnupperanten %}
<li class="py-1"
{% if "paid" in user.roles %}style="background-color: green;"{% endif %}>
{{ user.name }} ({{ user.mail }} | {{ user.notes }})
{{ user.name }} ({{ user.mail }}
{%- if user.notes %} | {{ user.notes }}
{% endif -%}
)
<a class="btn btn-primary"
href="/admin/user/move/schnupperant/{{ user.id }}/to/scheckbuch"
onclick="return confirm('Willst du wirklich ein Scheckbuch erstellen? Die Person erhält ein Mail mit allen Infos.')">Zu Scheckbuch umwandeln</a>
</li>
{% endfor %}
</ol>

View File

@ -32,7 +32,7 @@
{% if not loop.last %}+{% endif %}
{% endfor %}
</div>
{% if "admin" in loggedin_user.roles %}
{% if "admin" in loggedin_user.roles or "kassier" in loggedin_user.roles %}
<a href="/admin/user/fees/paid?{{ fee.user_ids }}">Zahlungsstatus ändern</a>
{% endif %}
</div>

View File

@ -36,37 +36,53 @@
placeholder="Suchen nach (Name, [yes|no]-role:<name>, has-[no-]membership-pdf)" />
</div>
<!-- END filterBar -->
<div class="bg-primary-100 dark:bg-primary-950 p-3 rounded-b-md grid gap-4">
<div id="filter-result-js"
class="text-primary-950 dark:text-white text-right"></div>
{% for user in users %}
<div data-filterable="true"
data-filter="{{ user.name }} {% for role in roles %} {% if role.name in user.roles %} yes-role:{{ role.name }} {% else %} no-role:{{ role.name }} {% endif %} role-{{ role }} {% endfor %} {% if user.membership_pdf %}has-membership-pdf{% else %}has-no-membership-pdf{% endif %} ">
<div id="filter-result-js" class="search-result"></div>
{% for user in users %}
<div data-filterable="true"
data-filter="{{ user.name }} {% for role in roles %} {% if role.name in user.roles %} yes-role:{{ role.name }} {% else %} no-role:{{ role.name }} {% endif %} role-{{ role }} {% endfor %} {% if user.membership_pdf %}has-membership-pdf{% else %}has-no-membership-pdf{% endif %}"
class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative">
<details class="block dark:text-white w-full">
<summary>
<span class="text-black dark:text-white cursor-pointer">
<span class="font-bold">
{{ user.name }}
{% if not user.last_access and "admin" in loggedin_user.roles and user.mail %}
<form action="/admin/user"
method="post"
enctype="multipart/form-data"
class="inline">
&bullet; <a class="font-normal text-primary-600 dark:text-primary-200 hover:text-primary-900 dark:hover:text-primary-300 underline"
href="/admin/user/{{ user.id }}/send-welcome-mail"
onclick="return confirm('Willst du wirklich das Willkommensmail an {{ user.name }} ausschicken?');">Willkommensmail verschicken</a>
</form>
{% endif %}
{% if user.last_access %}&bullet; ⏳&nbsp;{{ user.last_access | date }}{% endif %}
</span>
<small class="block text-gray-600 dark:text-gray-100">
{% for role in user.roles -%}
{{ role }}
{%- if not loop.last %},
{% endif -%}
{% endfor %}
</small>
</span>
</summary>
<form action="/admin/user"
method="post"
enctype="multipart/form-data"
class="bg-white dark:bg-primary-900 p-3 rounded-md w-full">
<div class="w-full grid gap-3">
class="w-full mt-2">
{% if user.pw %}
<a class="block my-1 font-normal text-[#f43f5e] dark:text-primary-200 hover:text-primary-900 dark:hover:text-primary-300 underline"
href="/admin/user/{{ user.id }}/reset-pw"
onclick="return confirm('Willst du wirklich das Passwort zurücksetzen?');">Passwort zurücksetzen</a>
{% endif %}
<div class="w-full grid gap-3 mt-3">
<input type="hidden" name="id" value="{{ user.id }}" />
<div class="font-bold mb-1 text-black dark:text-white">
{{ user.name }}
{% if user.last_access %}
(last access:
{{ user.last_access | date }})
{% endif %}
{% if user.pw %}
<a class="block mt-1 font-normal text-primary-600 dark:text-primary-200 hover:text-primary-900 dark:hover:text-primary-300 underline"
href="/admin/user/{{ user.id }}/reset-pw">Passwort zurücksetzen</a>
{% endif %}
{% if not user.last_access and "admin" in loggedin_user.roles %}
<a class="block mt-1 font-normal text-primary-600 dark:text-primary-200 hover:text-primary-900 dark:hover:text-primary-300 underline"
href="/admin/user/{{ user.id }}/send-welcome-mail">Willkommensmail verschicken</a>
{% endif %}
</div>
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-3">
{% for role in roles %}
{{ macros::checkbox(label=role.name, name="roles[" ~ role.id ~ "]", id=loop.index , checked=role.name in user.roles, disabled=allowed_to_edit == false) }}
{% endfor %}
<hr class="sm:col-span-2 lg:col-span-4 my-3" />
{% if user.membership_pdf %}
<a href="/admin/user/{{ user.id }}/membership"
class="text-black dark:text-white">Beitrittserklärung herunterladen</a>
@ -100,8 +116,8 @@
</div>
{% endif %}
</form>
</div>
{% endfor %}
</div>
</details>
</div>
{% endfor %}
</div>
{% endblock content %}

View File

@ -4,6 +4,34 @@
{% block content %}
<div class="max-w-screen-lg w-full bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
<h1 class="h1">Scheckbücher</h1>
<form action="/admin/user/new/scheckbuch"
method="post"
class="mt-4 bg-primary-900 rounded-md text-white px-3 pb-3 pt-2 sm:flex items-end justify-between">
<div class="w-full">
<h2 class="text-md font-bold mb-2 uppercase tracking-wide">Neues Scheckbuch hinzufügen</h2>
<div class="grid md:grid-cols-3">
<div>
<label for="name" class="sr-only">Name</label>
<input type="text"
name="name"
class="relative block rounded-md border-0 py-1.5 px-2 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-primary-600 sm:text-sm sm:leading-6 mb-2 md:mb-0"
placeholder="Name" />
</div>
<div>
<label for="name" class="sr-only">Mail</label>
<input type="mail"
name="mail"
class="relative block rounded-md border-0 py-1.5 px-2 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-primary-600 sm:text-sm sm:leading-6 mb-2 md:mb-0"
placeholder="Mail" />
</div>
</div>
</div>
<div class="text-right">
<input value="Hinzufügen"
type="submit"
class="w-28 mt-2 sm:mt-0 rounded-md bg-primary-500 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer" />
</div>
</form>
<!-- START filterBar -->
<div class="search-wrapper">
<label for="name" class="sr-only">Suche</label>
@ -31,7 +59,7 @@
<li>{{ log::show_old(log=trip, state="completed", only_ones=false, index=loop.index) }}</li>
{% endfor %}
</div>
{% if "admin" in loggedin_user.roles %}
{% if "admin" in loggedin_user.roles or "kassier" in loggedin_user.roles %}
<a href="/admin/user/fees/paid?user_ids[]={{ user.id }}">Zahlungsstatus ändern</a>
{% endif %}
</div>

View File

@ -29,7 +29,7 @@
Folgende Daten werden verarbeitet:
<ul>
<li>
Server-Log Files: IP-Adresse, Adresse der besuchten Seite, Browseragent, Datum und Uhrzeit. Wir nutzen diese Daten nicht und geben Sie in der Regel nicht weiter, können jedoch nicht ausschließen, dass diese Daten beim Vorliegen von rechtswidrigem Verhalten eingesehen werden.
Server-Log Files: IP-Adresse, Adresse der besuchten Seite, Browseragent, Datum und Uhrzeit. Wir nutzen diese Daten nicht und geben sie in der Regel nicht weiter, können jedoch nicht ausschließen, dass diese Daten beim Vorliegen von rechtswidrigem Verhalten eingesehen werden.
</li>
<li>
Cookie: Diese Website verwendet nur einen Cookie (loggedin_user), der verschlüsselte Informationen über den Login-Status speichert. Weitere Cookies werden nicht verwendet.

View File

@ -36,7 +36,7 @@
<div class="col-span-4 md:col-span-1">
<div class="text-sm text-gray-600 dark:text-gray-100">Bootssteuerung</div>
<div class="h-10 flex items-center">
{{ macros::checkbox(label='handgesteuert', name='shipmaster_only_steering', disabled=true) }}
{{ macros::checkbox(label='handgesteuert', name='shipmaster_only_steering', readonly=true) }}
</div>
</div>
{{ log::rower_select(id="newrower", selected=[], class="col-span-4", init=true) }}
@ -53,9 +53,13 @@
id="destination"
name="destination"
value=""
data-relation="distance_in_km" />
data-relation="distance_in_km"
autocomplete="off" />
<datalist id="destinations">
{% for distance in distances %}<option value="{{ distance.0 }}" distance="{{ distance.1 }}" />{% endfor %}
{% for distance in distances %}
<option value="{{ distance.destination }}"
distance="{{ distance.distance_in_km }}" />
{% endfor %}
</datalist>
</div>
<div class="relative col-span-2">
@ -169,83 +173,114 @@
</div>
</div>
{% endmacro show %}
{% macro show_old(log, state, allowed_to_close=false, index) %}
{% macro show_old(log, state, allowed_to_close=false, allowed_to_edit=false, index) %}
<div class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative"
data-filterable="true"
data-filter="{{ log.boat.name }} {% for rower in log.rowers %}{{ rower.name }}{% endfor %}">
{% if log.logtype %}
<div class="absolute top-0 right-0 bg-primary-100 rounded-bl-md text-primary-950 text-xs w-32 px-2 py-1 text-center font-bold">
{% if log.logtype == 1 %}
Wanderfahrt
{% else %}
{% if log.logtype == 2 %}
Regatta
{% else %}
{{ log.logtype }}
{% endif %}
{% endif %}
</div>
{% endif %}
<div {% if log.logtype %}class="mt-4 sm:mt-0"{% endif %}>
<strong class="text-black dark:text-white">{{ log.boat.name }}</strong>
<small class="text-gray-600 dark:text-gray-100">({{ log.shipmaster_user.name -}}
{% if log.shipmaster_only_steering %}
- handgesteuert
{%- endif -%}
)</small>
<small class="block text-gray-600 dark:text-gray-100">
{% if state == "completed" and log.departure | date(format='%d.%m.%Y') == log.arrival | date(format='%d.%m.%Y') %}
{{ log.departure | date(format='%d.%m.%Y') }}
({{ log.departure | date(format='%H:%M') }}
-
{{ log.arrival | date(format='%H:%M') }})
{% else %}
{{ log.departure | date(format='%d.%m.%Y (%H:%M)') }}
{% if state == "completed" %}
-
{{ log.arrival | date(format='%d.%m.%Y (%H:%M)') }}
{% endif %}
{% endif %}
</small>
{% set amount_rowers = log.rowers | length %}
{% set amount_guests = log.boat.amount_seats - amount_rowers %}
{% if allowed_to_close and state == "on_water" %}
{{ log::home(log=log) }}
{% else %}
<div class="text-black dark:text-white">
{{ log.destination }}
{% if state == "completed" %}
<small class="text-gray-600 dark:text-gray-100">({{ log.distance_in_km }}
km)</small>
{% endif %}
{% if log.comments %}<span class="text-sm italic">- "{{ log.comments }}"</span>{% endif %}
</div>
{% if amount_guests > 0 or log.rowers | length > 0 %}
{% if not log.boat.amount_seats == 1 %}
<div class="text-sm text-gray-600 dark:text-gray-100">
Ruderer:
{% for rower in log.rowers -%}
{{ rower.name }}
{%- if rower.id == log.steering_user.id and rower.id != log.shipmaster_user.id %}
(Steuerperson){%- endif -%}
{%- if not loop.last or amount_guests > 0 and not log.boat.external %},{% endif %}
{% endfor -%}
{% if amount_guests > 0 and not log.boat.external %}
Gäste
<small class="text-gray-600 dark:text-gray-100">(ohne Account)</small>:
{{ amount_guests }}
{% endif %}
</div>
<details>
<summary style="list-style: none;">
{% if log.logtype %}
<div class="absolute top-0 right-0 bg-primary-100 rounded-bl-md text-primary-950 text-xs w-32 px-2 py-1 text-center font-bold">
{% if log.logtype == 1 %}
Wanderfahrt
{% else %}
{% if log.logtype == 2 %}
Regatta
{% else %}
{{ log.logtype }}
{% endif %}
{% endif %}
{% endif %}
</div>
{% endif %}
</div>
<div {% if log.logtype %}class="mt-4 sm:mt-0"{% endif %}>
<strong class="text-black dark:text-white">{{ log.boat.name }}</strong>
<small class="text-gray-600 dark:text-gray-100">({{ log.shipmaster_user.name -}}
{% if log.shipmaster_only_steering %}
- handgesteuert
{%- endif -%}
)</small>
<small class="block text-gray-600 dark:text-gray-100">
{% if state == "completed" and log.departure | date(format='%d.%m.%Y') == log.arrival | date(format='%d.%m.%Y') %}
{{ log.departure | date(format='%d.%m.%Y') }}
({{ log.departure | date(format='%H:%M') }}
-
{{ log.arrival | date(format='%H:%M') }})
{% else %}
{{ log.departure | date(format='%d.%m.%Y (%H:%M)') }}
{% if state == "completed" %}
-
{{ log.arrival | date(format='%d.%m.%Y (%H:%M)') }}
{% endif %}
{% endif %}
</small>
{% set amount_rowers = log.rowers | length %}
{% set amount_guests = log.boat.amount_seats - amount_rowers %}
{% if allowed_to_close and state == "on_water" %}
{{ log::home(log=log) }}
{% else %}
<div class="text-black dark:text-white">
{{ log.destination }}
{% if state == "completed" %}
<small class="text-gray-600 dark:text-gray-100">({{ log.distance_in_km }}
km)</small>
{% endif %}
{% if log.comments %}<span class="text-sm italic">- "{{ log.comments }}"</span>{% endif %}
</div>
{% if amount_guests > 0 or log.rowers | length > 0 %}
{% if not log.boat.amount_seats == 1 %}
<div class="text-sm text-gray-600 dark:text-gray-100">
Ruderer:
{% for rower in log.rowers -%}
{{ rower.name }}
{%- if rower.id == log.steering_user.id and rower.id != log.shipmaster_user.id %}
(Steuerperson){%- endif -%}
{%- if not loop.last or amount_guests > 0 and not log.boat.external %},{% endif %}
{% endfor -%}
{% if amount_guests > 0 and not log.boat.external %}
Gäste
<small class="text-gray-600 dark:text-gray-100">(ohne Account)</small>:
{{ amount_guests }}
{% endif %}
</div>
{% endif %}
{% endif %}
{% endif %}
</div>
</summary>
{% if allowed_to_edit %}
<form action="/log/update" method="post">
<input type="hidden" name="id" value="{{ log.id }}" />
<input type="hidden" name="boat_id" value="{{ log.boat_id }}" />
<input type="hidden" name="shipmaster" value="{{ log.shipmaster }}" />
<input type="hidden"
name="steering_person"
value="{{ log.steering_person }}" />
<input type="hidden"
name="shipmaster_only_steering"
value="{{ log.shipmaster_only_steering }}" />
<input type="datetime-local" name="departure" value="{{ log.departure }}" />
<input type="datetime-local" name="arrival" value="{{ log.arrival }}" />
<input type="hidden" name="destination" value="{{ log.destination }}" />
<input type="hidden" name="distance_in_km" value="{{ log.distance_in_km }}" />
<input type="hidden" name="comments" value="{{ log.comments }}" />
<input type="hidden" name="logtype" value="{{ log.logtype }}" />
<input type="submit" value="Updaten" />
</form>
<a href="/log/{{ log.id }}/delete"
class="w-28 btn btn-alert"
onclick="return confirm('Willst du diesen Logbucheintrag wirklich löschen?');">
{% include "includes/delete-icon" %}
Löschen
</a>
{% endif %}
</details>
</div>
{% endmacro show_old %}
{% macro home(log) %}
<form class="grid grid-cols-1 gap-3"
action="/log/{{ log.id }}"
method="post">
method="post"
onsubmit="var distance = parseFloat(document.getElementById('distance_in_km{{ log.id }}js').value); var logtype = document.getElementById('logtype{{ log.id }}js').value; if (distance > 50 && (!logtype || logtype === 'Normal')) { return confirm('Die eingegebene Distanz beträgt mehr als 50 km und es wurde kein Typ (Wanderfahrt, Regatta, ...) ausgewählt. Wenn es eine Wanderfahrt war, stell dies bitte unter \'Details ändern\' ein. Möchtest du das NICHT tun und den Eintrag OHNE Typ speichern?'); } return true;">
{{ macros::input(label='Ankunftszeit', name='arrival', type='datetime-local', required=true, class="change-id-js rounded-md current-date-time") }}
<div>
<label for="destination" class="text-sm text-gray-600 dark:text-gray-100">Ziel</label>

View File

@ -1,3 +1,42 @@
{% macro plannedtrips() %}
{% if planned_trips %}
<script>
function setChoiceByLabel(choicesInstance, label) {
const choices = choicesInstance.config.choices;
const normalizedLabel = label.trim().replace(/\n/g, '');
const choice = choices.find(c => {
const choiceLabel = c.label.trim().replace(/\n/g, '');
return choiceLabel === normalizedLabel;
});
if (choice) {
choicesInstance.setChoiceByValue(choice.value);
} else {
console.warn(`Choice with label "${normalizedLabel}" not found`);
}
}
</script>
<div class="bg-white dark:bg-primary-900 rounded-md shadow pb-2 mt-3">
<h2 class="h2">Heute geplante Ausfahrten</h2>
<div class="grid grid-cols-1 gap-3 mb-3 w-full">
{% for planned_trip in planned_trips | sort(attribute='planned_starting_time') %}
<div class="pt-2 px-3 border-t text-primary-900 dark:text-white">
<strong class="block">
{{ planned_trip.cox_name }} ({{ planned_trip.rower | length + 1 }} Personen)
<small>{{ planned_trip.planned_starting_time }}</small>
<button class="btn btn-primary"
onclick="choiceObjects['newrower'].removeActiveItems(-1);choiceObjects['newrower'].setChoiceByValue('{{ planned_trip.cox_id }}'); {% for rower in planned_trip.rower %}setChoiceByLabel(choiceObjects['newrower'], '{{ rower.name }}');{% endfor %}window.scrollTo(0,0); ">
👥
</button>
</strong>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% endmacro plannedtrips %}
{% macro boatreservation() %}
<div class="bg-white dark:bg-primary-900 rounded-md shadow pb-2 mt-3">
<h2 class="h2">Reservierungen ({{ reservations | length }})</h2>
@ -137,7 +176,7 @@
{% if readonly %}readonly{% endif %}>
</div>
{% endmacro input %}
{% macro checkbox(label, name, id='', checked=false, class='', disabled=false) %}
{% macro checkbox(label, name, id='', checked=false, class='', disabled=false, readonly=false) %}
<label for="{{ name }}{{ id }}"
class="flex items-center cursor-pointer text-black dark:text-white hover:text-gray-900 dark:hover:text-gray-100 {{ class }}">
<input type="checkbox"
@ -145,6 +184,7 @@
name="{{ name }}"
{% if checked %}checked{% endif %}
{% if disabled %}disabled{% endif %}
{% if readonly %}readonly="readonly"{% endif %}
class="h-4 w-4 accent-primary-600 dark:accent-primary-200 mr-2" />
{{ label }}
</label>

View File

@ -33,6 +33,11 @@
<div class="mt-1">{{ notification.message | safe }}</div>
</div>
<div>
{% if notification.link %}
<a href="{{ notification.link }}" class="inline-block">
<button class="btn btn-primary" type="button">🔗</button>
</a>
{% endif %}
{% if not notification.read_at %}
<a href="/notification/{{ notification.id }}/read" class="inline-block">
<button class="btn btn-primary" type="button">
@ -56,6 +61,11 @@
<strong>{{ notification.category }}</strong> &bullet; {{ notification.created_at | date(format="%d.%m.%Y %H:%M") }}
</small>
<div class="mt-1">{{ notification.message | safe }}</div>
{% if notification.link %}
<a href="{{ notification.link }}" class="inline-block">
<button class="btn btn-primary" type="button">🔗</button>
</a>
{% endif %}
</div>
{% endif %}
{% endfor %}
@ -155,6 +165,13 @@
<a href="/board/boathouse"
class="block w-100 py-2 hover:text-primary-600">Bootshaus</a>
</li>
<li class="py-1">
<a href="/admin/mail" class="block w-100 py-2 hover:text-primary-600">Mail ausschicken</a>
</li>
<li class="py-1">
<a href="/admin/notification"
class="block w-100 py-2 hover:text-primary-600">Nachricht ausschreiben</a>
</li>
</ul>
</div>
{% endif %}
@ -169,19 +186,12 @@
<li class="py-1">
<a href="/admin/user" class="block w-100 py-2 hover:text-primary-600">User</a>
</li>
<li class="py-1">
<a href="/admin/mail" class="block w-100 py-2 hover:text-primary-600">Mail</a>
</li>
<li class="py-1">
<a href="/admin/rss" class="block w-100 py-2 hover:text-primary-600">Logs</a>
</li>
<li class="py-1">
<a href="/admin/list" class="block w-100 py-2 hover:text-primary-600">Fingerabdruck-Liste überprüfen</a>
</li>
<li class="py-1">
<a href="/admin/notification"
class="block w-100 py-2 hover:text-primary-600">Nachricht ausschreiben</a>
</li>
</ul>
</div>
{% endif %}

View File

@ -53,6 +53,7 @@
</div>
</div>
{{ macros::boatreservation() }}
{{ macros::plannedtrips() }}
</div>
</div>
</div>

View File

@ -23,7 +23,15 @@
placeholder="Suchen nach Bootsname oder Ruderer...">
</div>
<div id="filter-result-js" class="search-result"></div>
{% for log in logs %}{{ log::show_old(log=log, state="completed", only_ones=false, index=loop.index) }}{% endfor %}
{% for log in logs %}
{% set_global allowed_to_edit = false %}
{% if loggedin_user %}
{% if "Vorstand" in loggedin_user.roles %}
{% set_global allowed_to_edit = true %}
{% endif %}
{% endif %}
{{ log::show_old(log=log, state="completed", only_ones=false, index=loop.index, allowed_to_edit=allowed_to_edit) }}
{% endfor %}
</div>
</div>
<script>

View File

@ -35,6 +35,7 @@
{% endif %}
</div>
{{ macros::boatreservation() }}
{{ macros::plannedtrips() }}
</div>
</div>
</div>

View File

@ -18,8 +18,8 @@
<script type="text/javascript">
var sepaqr = new sepaQR({
benefName: 'ASKÖ Ruderverein Donau Linz',
benefBIC: 'BKAUATWWXXX',
benefAccNr: 'AT131200080413001200',
benefBIC: 'ASPKAT2LXXX',
benefAccNr: 'AT582032032100729256',
amountEuro: {{ fee.sum_in_cents/100 }},
remittanceInf: 'Vereinsgebühren {{ fee.name }}',
});
@ -44,13 +44,13 @@
</ul>
</small>
{% endif %}
Bitte auf folgendes Konto überweisen: IBAN AT13 1200 0804 1300 1200. Alternativ kannst du auch mit deiner Bankapp den QR Code scannen, damit sollten alle Daten vorausgefüllt sein.
Bitte auf folgendes Konto überweisen: IBAN AT58 2032 0321 0072 9256. Alternativ kannst du auch mit deiner Bankapp den QR Code scannen, damit sollten alle Daten vorausgefüllt sein.
<br />
Falls die Berechnung nicht stimmt (korrekte Preise findest du <a href="https://rudernlinz.at/unser-verein/gebuhren/"
target="_blank"
rel="noopener noreferrer">hier</a>) melde dich bitte bei it@rudernlinz.at. @Studenten: Bitte die aktuelle Studienbestätigung an it@rudernlinz.at schicken.
<br />
<small>Wir aktualisieren den Ruderassistent unregelmäßig mit unserem Bankkonto. Falls du schon bezahlt hast, kannst du diese Nachricht getrost ignorieren :^)</small>
<small><a href="https://rudernlinz.at/unser-verein/vorstand/" target="_blank">Unsere Kassiere</a> aktualisieren den Ruderassistent unregelmäßig mit unserem Bankkonto. Falls du schon bezahlt hast, kannst du diese Nachricht getrost ignorieren. Wenn du schon vor "einigen Wochen" bezahlt hast bitte bei kassier@rudernlinz.at nachfragen :^)</small>
</div>
</div>
</div>
@ -67,7 +67,7 @@
{% endif %}
{% endfor %}
{% endif %}
<div class="bg-white dark:bg-primary-900 rounded-md flex justify-between flex-col shadow reset-js"
<div id="{{ day.day| date(format="%Y-%m-%d") }}" class="bg-white dark:bg-primary-900 rounded-md flex justify-between flex-col shadow reset-js"
style="min-height: 10rem"
data-trips="{{ amount_trips }}"
data-month="{{ day.day| date(format='%m') }}"
@ -391,15 +391,15 @@
</div>
{# --- START Add Buttons --- #}
{% if "manage_events" in loggedin_user.roles or "cox" in loggedin_user.roles %}
<div class="grid {% if "manage_events" in loggedin_user.roles %}grid-cols-2{% endif %} text-center">
<div class="grid {% if "manage_events" in loggedin_user.roles and "cox" in loggedin_user.roles %}grid-cols-2{% endif %} text-center">
{% if "manage_events" in loggedin_user.roles %}
<a href="#"
data-sidebar="true"
data-trigger="sidebar"
data-header="<strong>Event</strong> am {{ day.day| date(format='%d.%m.%Y') }} erstellen"
data-day="{{ day.day }}"
data-body="#addEventForm"
class="relative inline-block w-full bg-primary-900 hover:bg-primary-950 focus:bg-primary-950 dark:bg-primary-950 text-white py-2 rounded-bl-md text-sm font-semibold">
<a href="#" data-sidebar="true" data-trigger="sidebar" data-header="<strong>Event</strong> am {{ day.day| date(format='%d.%m.%Y') }} erstellen" data-day="{{ day.day }}" data-body="#addEventForm" class="relative inline-block w-full bg-primary-900 hover:bg-primary-950 focus:bg-primary-950 dark:bg-primary-950 text-white py-2 text-sm font-semibold
{% if "cox" in loggedin_user.roles %}
rounded-bl-md
{% else %}
rounded-b-md
{% endif %}
">
<span class="absolute inset-y-0 left-0 flex items-center pl-3">{% include "includes/plus-icon" %}</span>
Event
</a>

View File

@ -51,7 +51,7 @@
data-filter="{{ reservation.user_applicant.name }} {{ reservation.trailer.name }}"
class="w-full border-t bg-white dark:bg-primary-900 text-black dark:text-white p-3">
<div class="w-full">
<strong>Boot:</strong>
<strong>Hänger:</strong>
{{ reservation.trailer.name }}
<br />
<strong>Reservierung:</strong>

156
wordpress-notes.md Normal file
View File

@ -0,0 +1,156 @@
# Wordpress auth
Add the following code to `wp-content/themes/bravada/functions.php`:
```
function rot_auth( $user, $username, $password ){
// Make sure a username and password are present for us to work with
if($username == '' || $password == '') return;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://app.rudernlinz.at/wikiauth');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "name=$username&password=$password");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Execute the cURL session and get the response
$response = curl_exec($ch);
// Check for cURL errors
if(curl_errno($ch)){
$user = new WP_Error( 'denied', __('Curl error: ' . curl_error($ch)) );
}
// Close the cURL session
curl_close($ch);
if (strpos($response, 'SUCC') !== false) {
$user = get_user_by('login', $username);
if (!$user) {
// User does not exist, create a new one
$userdata = array(
'user_email' => $username,
'user_login' => $username,
'first_name' => $username,
'last_name' => ''
);
$new_user_id = wp_insert_user($userdata);
if (!is_wp_error($new_user_id)) {
// Load the new user info
$user = new WP_User($new_user_id);
// Set role based on username
if ($username == 'Philipp Hofer' || $username == 'Marie Birner') {
$user->set_role('administrator');
} else {
$user->set_role('editor');
}
} else {
// Handle error in user creation
return $new_user_id;
}
} else {
}
} else {
$user = new WP_Error( 'denied', __("Falscher Benutzername/Passwort. Verwendest du deine Accountdaten vom Ruderassistenten?") );
}
return $user;
}
// Comment this line if you wish to fall back on WordPress authentication
// Useful for times when the external service is offline
remove_action('authenticate', 'wp_authenticate_username_password', 20);
add_filter( 'authenticate', 'rot_auth', 10, 3 );
```
# Wordpress notify rowt on newly published article
Add the following code to `wp-content/themes/bravada/functions.php`:
```
function send_article_url_on_publish($new_status, $old_status, $post) {
// Check if the post is transitioning to 'publish' status
if ($new_status == 'publish' && $old_status != 'publish' && $post->post_type == 'post') {
// Get the URL of the newly published article
$article_url = get_permalink($post->ID);
$article_title = get_the_title($post->ID);
// URL to send the POST request to
$api_url = 'https://app.rudernlinz.at/new-blogpost';
// Prepare the data for the POST request
$body = array(
'article_url' => $article_url,
'article_title' => $article_title,
'pw' => "wordpress_key"
);
// Prepare the arguments for wp_remote_post
$args = array(
'body' => $body,
'timeout' => '5',
'redirection' => '5',
'httpversion' => '1.0',
'blocking' => true,
'headers' => array(),
'cookies' => array()
);
// Send the POST request
$response = wp_remote_post($api_url, $args);
// Optional: Check if the request was successful
if (is_wp_error($response)) {
error_log('Failed to send POST request: ' . $response->get_error_message());
} else {
error_log('POST request sent successfully with article URL: ' . $article_url);
}
}
if ($new_status != 'publish' && $old_status == 'publish' && $post->post_type == 'post') {
$article_url = get_permalink($post->ID);
// URL to send the POST request to
$api_url = 'https://app.rudernlinz.at/blogpost-unpublished';
// Prepare the data for the POST request
$body = array(
'article_url' => $article_url,
'pw' => "wordpress_key"
);
// Prepare the arguments for wp_remote_post
$args = array(
'body' => $body,
'timeout' => '5',
'redirection' => '5',
'httpversion' => '1.0',
'blocking' => true,
'headers' => array(),
'cookies' => array()
);
// Send the POST request
$response = wp_remote_post($api_url, $args);
// Optional: Check if the request was successful
if (is_wp_error($response)) {
error_log('Failed to send POST request: ' . $response->get_error_message());
} else {
error_log('POST request sent successfully with article URL: ' . $article_url);
}
}
}
// Hook the function to the 'transition_post_status' action
add_action('transition_post_status', 'send_article_url_on_publish', 10, 3);
```