Compare commits
194 Commits
test
...
fdd9c3bdff
Author | SHA1 | Date | |
---|---|---|---|
fdd9c3bdff | |||
ae6c129fd3 | |||
396aa204a4 | |||
bbe4949203 | |||
94130f9230 | |||
010627c91d | |||
36276e5415 | |||
4bb0e54635 | |||
a518023892 | |||
3f06e91e24 | |||
2b4345ba77 | |||
412c45a9df | |||
d971c1504c | |||
cf9b79e56e | |||
5d01d18e70 | |||
1e96a2d6e1 | |||
ff81ab0246 | |||
202e128c98 | |||
ecb347c204 | |||
2bc426be52 | |||
0a130709c7 | |||
6e8a5927a6 | |||
a31bacb3e1 | |||
93c8316543 | |||
6e72e2a753 | |||
c847c3300f | |||
116c7523d2 | |||
eb15421d08 | |||
818cf0d40b | |||
ed9d93410e | |||
c162e0a66f | |||
e965d33a7b | |||
3d77a2325c | |||
afb6af8ece | |||
799e94a50f | |||
c41dc0853a | |||
74edcfa119 | |||
cc7bd3a416 | |||
bfee85a963 | |||
f55f45d960 | |||
c68593a67d | |||
20da86f69e | |||
8588e1f71b | |||
8efb3aea2c | |||
83aa9bc84c | |||
6171bb0f85 | |||
e040764902 | |||
eeab4c167b | |||
25161fc8e9 | |||
dea53d8396 | |||
80ac131fb2 | |||
8e65a6540d | |||
d7e5731753 | |||
1a4d5ac569 | |||
668fc5e295 | |||
4f9778eccf | |||
09d4c5d958 | |||
11dd978135 | |||
d7d0a3fedd | |||
e4333a05d7 | |||
f687e18195 | |||
a4f72d746c | |||
e55f380c4f | |||
bb502a4561 | |||
1340639f91 | |||
ce28c95853 | |||
2c3f69a562 | |||
0bf7094770 | |||
a75c892cfb | |||
f71ab634d7 | |||
0c770f6ddc | |||
6b71449183 | |||
d59b3f4345 | |||
7e41cd3f73 | |||
2a8c339dcd | |||
0dd10e1dd6 | |||
2d2e44126a | |||
def8028446 | |||
db318c23cd | |||
4555391dd3 | |||
23aa6aa0f8 | |||
a682d1e6ce | |||
8aca437eb3 | |||
cd1bf12e68 | |||
5f7591f52a | |||
127d9784ad | |||
bf4ea502d3 | |||
c13dfdaa77 | |||
26aa222bc6 | |||
0bc9e11b9a | |||
2fdcab9030 | |||
7689a39ac5 | |||
b43682ac39 | |||
8d7a1c707d | |||
958dda9f52 | |||
1eea8c9662 | |||
b4b922222c | |||
84e76e8d65 | |||
bdfcc6bc0a | |||
afa88b9529 | |||
4229a4e021 | |||
6cd555298d | |||
f6207e2994 | |||
4da996251a | |||
c44c0d8505 | |||
aa9568f326 | |||
1db09cd8ac | |||
59ef93d6fa | |||
4a3ee5b551 | |||
c73b3e94c3 | |||
4969a0d90a | |||
3efcd99bbc | |||
4f0f509ad6 | |||
8112f1ed2a | |||
b1252e8d5c | |||
9cb9cfe2a1 | |||
a62fd116ea | |||
622bc700f3 | |||
2540a3dc7c | |||
0e5fd25e61 | |||
72b86d4dad | |||
16fbeea81b | |||
bd6fbe772e | |||
647970e1fc | |||
1e1c1bb6d9 | |||
088fe98995 | |||
4237fafdff | |||
6b24008c17 | |||
4fbd3c7717 | |||
ce8a095b31 | |||
6c191cf59e | |||
b0698e70a4 | |||
6f7283f754 | |||
323f721fc0 | |||
d0bcf1f384 | |||
bd7cd0020e | |||
22bfe48d18 | |||
c379a6ca79 | |||
3543ffe9e1 | |||
1d6770f11b | |||
1ad6509568 | |||
705f2ddc52 | |||
dba1e08c5d | |||
1dc0c9c0e1 | |||
45004567ed | |||
3dff956544 | |||
79f8efc34b | |||
5c31fac230 | |||
3e983e05f9 | |||
8f91cc4e88 | |||
c55f9743aa | |||
76290a64ae | |||
4b48fbaa82 | |||
d25cd491d0 | |||
def8affb5f | |||
dfa7be9928 | |||
03a467270d | |||
1c04462c30 | |||
7af53203f8 | |||
b2393eb6ec | |||
a5e82851ba | |||
80725e223b | |||
70be6726db | |||
08fc324cc6 | |||
df007524ed | |||
5720767af3 | |||
1215bdbd84 | |||
734490efe7 | |||
980fcc0c0c | |||
84f23e6e55 | |||
36193e3a64 | |||
c6e3458588 | |||
a0528c1c65 | |||
5e4d708884 | |||
6e41758104 | |||
c916381fb0 | |||
7d44204533 | |||
145892104b | |||
6c0f0e6b04 | |||
e004d81ca1 | |||
41b5aff329 | |||
76c8456380 | |||
95f43a73cf | |||
3d340bf803 | |||
ae096ad602 | |||
25d8b1ea7c | |||
410cd05acc | |||
972811c2cf | |||
e22d2d718e | |||
b55f122f1d | |||
d27489d714 | |||
c340d1a916 | |||
e7732b9e96 | |||
97dc9308bc |
195
Cargo.lock
generated
195
Cargo.lock
generated
@ -175,7 +175,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -186,7 +186,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -221,9 +221,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.72"
|
version = "0.3.73"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11"
|
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cc",
|
"cc",
|
||||||
@ -266,9 +266,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.5.0"
|
version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@ -309,9 +309,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.16.0"
|
version = "1.16.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5"
|
checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
@ -327,9 +327,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.98"
|
version = "1.0.101"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
|
checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@ -620,11 +620,11 @@ version = "0.4.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a"
|
checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"proc-macro2-diagnostics",
|
"proc-macro2-diagnostics",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -647,9 +647,9 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.12.0"
|
version = "1.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@ -785,7 +785,7 @@ checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"spin 0.9.8",
|
"spin",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -894,7 +894,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -992,8 +992,8 @@ dependencies = [
|
|||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"bstr",
|
"bstr",
|
||||||
"log",
|
"log",
|
||||||
"regex-automata 0.4.6",
|
"regex-automata 0.4.7",
|
||||||
"regex-syntax 0.8.3",
|
"regex-syntax 0.8.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1002,7 +1002,7 @@ version = "0.9.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
|
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"ignore",
|
"ignore",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
@ -1139,9 +1139,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.8.0"
|
version = "1.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
|
checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httpdate"
|
name = "httpdate"
|
||||||
@ -1237,7 +1237,7 @@ dependencies = [
|
|||||||
"globset",
|
"globset",
|
||||||
"log",
|
"log",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-automata 0.4.6",
|
"regex-automata 0.4.7",
|
||||||
"same-file",
|
"same-file",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
@ -1306,15 +1306,6 @@ version = "1.70.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
|
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
@ -1372,11 +1363,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"spin 0.5.2",
|
"spin",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1485,9 +1476,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.2"
|
version = "2.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mime"
|
name = "mime"
|
||||||
@ -1503,9 +1494,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.7.3"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
|
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler",
|
"adler",
|
||||||
]
|
]
|
||||||
@ -1535,7 +1526,7 @@ dependencies = [
|
|||||||
"httparse",
|
"httparse",
|
||||||
"memchr",
|
"memchr",
|
||||||
"mime",
|
"mime",
|
||||||
"spin 0.9.8",
|
"spin",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"version_check",
|
"version_check",
|
||||||
@ -1583,7 +1574,7 @@ version = "6.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
|
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"filetime",
|
"filetime",
|
||||||
"fsevent-sys",
|
"fsevent-sys",
|
||||||
@ -1671,9 +1662,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.35.0"
|
version = "0.36.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
|
checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@ -1696,7 +1687,7 @@ version = "0.10.64"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
|
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
@ -1713,7 +1704,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1768,7 +1759,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.5.1",
|
"redox_syscall 0.5.2",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-targets 0.52.5",
|
"windows-targets 0.52.5",
|
||||||
]
|
]
|
||||||
@ -1819,7 +1810,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"proc-macro2-diagnostics",
|
"proc-macro2-diagnostics",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1868,7 +1859,7 @@ dependencies = [
|
|||||||
"pest_meta",
|
"pest_meta",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1985,9 +1976,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.85"
|
version = "1.0.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
|
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@ -2000,7 +1991,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
"version_check",
|
"version_check",
|
||||||
"yansi",
|
"yansi",
|
||||||
]
|
]
|
||||||
@ -2076,11 +2067,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.1"
|
version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
|
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2100,19 +2091,19 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.4"
|
version = "1.10.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
|
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-automata 0.4.6",
|
"regex-automata 0.4.7",
|
||||||
"regex-syntax 0.8.3",
|
"regex-syntax 0.8.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2126,13 +2117,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.6"
|
version = "0.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
|
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-syntax 0.8.3",
|
"regex-syntax 0.8.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2143,9 +2134,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
@ -2157,7 +2148,7 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"libc",
|
"libc",
|
||||||
"spin 0.9.8",
|
"spin",
|
||||||
"untrusted",
|
"untrusted",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
@ -2211,7 +2202,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rocket_http",
|
"rocket_http",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
@ -2267,7 +2258,7 @@ dependencies = [
|
|||||||
"env_logger",
|
"env_logger",
|
||||||
"futures",
|
"futures",
|
||||||
"ics",
|
"ics",
|
||||||
"itertools 0.13.0",
|
"itertools",
|
||||||
"job_scheduler_ng",
|
"job_scheduler_ng",
|
||||||
"lettre",
|
"lettre",
|
||||||
"log",
|
"log",
|
||||||
@ -2280,6 +2271,7 @@ dependencies = [
|
|||||||
"sqlx",
|
"sqlx",
|
||||||
"tera",
|
"tera",
|
||||||
"ureq",
|
"ureq",
|
||||||
|
"urlencoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2314,7 +2306,7 @@ version = "0.38.34"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
@ -2440,7 +2432,7 @@ version = "2.11.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
|
checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@ -2474,14 +2466,14 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.117"
|
version = "1.0.118"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -2588,12 +2580,6 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"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]]
|
[[package]]
|
||||||
name = "spin"
|
name = "spin"
|
||||||
version = "0.9.8"
|
version = "0.9.8"
|
||||||
@ -2615,11 +2601,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlformat"
|
name = "sqlformat"
|
||||||
version = "0.2.3"
|
version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c"
|
checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools 0.12.1",
|
|
||||||
"nom",
|
"nom",
|
||||||
"unicode_categories",
|
"unicode_categories",
|
||||||
]
|
]
|
||||||
@ -2728,7 +2713,7 @@ checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -2772,7 +2757,7 @@ checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"chrono",
|
"chrono",
|
||||||
"crc",
|
"crc",
|
||||||
@ -2873,9 +2858,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.5.0"
|
version = "2.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
@ -2890,9 +2875,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.66"
|
version = "2.0.68"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
|
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -2950,7 +2935,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2996,9 +2981,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.6.0"
|
version = "1.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tinyvec_macros",
|
"tinyvec_macros",
|
||||||
]
|
]
|
||||||
@ -3035,7 +3020,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3122,7 +3107,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3328,14 +3313,14 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"url",
|
"url",
|
||||||
"webpki-roots 0.26.2",
|
"webpki-roots 0.26.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.0"
|
version = "2.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
|
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"idna",
|
"idna",
|
||||||
@ -3350,15 +3335,15 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.1"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.8.0"
|
version = "1.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
@ -3433,7 +3418,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3455,7 +3440,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
@ -3474,9 +3459,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "0.26.2"
|
version = "0.26.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c452ad30530b54a4d8e71952716a212b08efd0f3562baa66c29a618b07da7c3"
|
checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
@ -3691,9 +3676,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.6.11"
|
version = "0.6.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56c52728401e1dc672a56e81e593e912aa54c78f40246869f78359a2bf24d29d"
|
checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@ -3724,7 +3709,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.66",
|
"syn 2.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -28,6 +28,7 @@ itertools = "0.13"
|
|||||||
job_scheduler_ng = "2.0"
|
job_scheduler_ng = "2.0"
|
||||||
ureq = { version = "2.9", features = ["json"] }
|
ureq = { version = "2.9", features = ["json"] }
|
||||||
regex = "1.10"
|
regex = "1.10"
|
||||||
|
urlencoding = "2.1"
|
||||||
|
|
||||||
[target.'cfg(not(windows))'.dependencies]
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
openssl = { version = "0.10", features = [ "vendored" ] }
|
openssl = { version = "0.10", features = [ "vendored" ] }
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
secret_key = "/NtVGizglEoyoxBLzsRDWTy4oAG1qDw4J4O+CWJSv+fypD7W9sam8hUY4j90EZsbZk8wEradS5zBoWtWKi3k8w=="
|
secret_key = "/NtVGizglEoyoxBLzsRDWTy4oAG1qDw4J4O+CWJSv+fypD7W9sam8hUY4j90EZsbZk8wEradS5zBoWtWKi3k8w=="
|
||||||
rss_key = "rss-key-for-ci"
|
rss_key = "rss-key-for-ci"
|
||||||
limits = { file = "10 MiB", data-form = "10 MiB"}
|
limits = { file = "10 MiB", data-form = "10 MiB"}
|
||||||
smtp_pw = "8kIjlLH79Ky6D3jQ"
|
smtp_pw = "8kIjlLH79Ky6D3j"
|
||||||
usage_log_path = "./usage.txt"
|
usage_log_path = "./usage.txt"
|
||||||
openweathermap_key = "c8dab8f91b5b815d76e9879cbaecd8d5"
|
openweathermap_key = "c8dab8f91b5b815d76e9879cbaecd8d5"
|
||||||
|
wordpress_key = "pw-to-allow-sending-notifications"
|
||||||
|
2
fd
2
fd
@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/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
|
#sqlite3 db.sqlite < seeds.sql
|
||||||
|
|
||||||
|
112
frontend/main.ts
112
frontend/main.ts
@ -8,7 +8,7 @@ export interface choiceMap {
|
|||||||
declare var loggedin_user_id: string;
|
declare var loggedin_user_id: string;
|
||||||
let choiceObjects: choiceMap = {};
|
let choiceObjects: choiceMap = {};
|
||||||
let boat_in_ottensheim = true;
|
let boat_in_ottensheim = true;
|
||||||
let boat_reserved_today= true;
|
let boat_reserved_today = true;
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
changeTheme();
|
changeTheme();
|
||||||
@ -103,7 +103,11 @@ function setTheme(theme: string, setLocalStorage = true) {
|
|||||||
function setCurrentdate(input: HTMLInputElement) {
|
function setCurrentdate(input: HTMLInputElement) {
|
||||||
if (input) {
|
if (input) {
|
||||||
const now = new Date();
|
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;
|
input.value = formattedDateTime;
|
||||||
}
|
}
|
||||||
@ -139,28 +143,32 @@ function selectBoatChange() {
|
|||||||
boatSelect.addEventListener(
|
boatSelect.addEventListener(
|
||||||
"addItem",
|
"addItem",
|
||||||
function (e) {
|
function (e) {
|
||||||
|
|
||||||
const event = e as ChoiceBoatEvent;
|
const event = e as ChoiceBoatEvent;
|
||||||
boat_reserved_today = event.detail.customProperties.boat_reserved_today;
|
boat_reserved_today = event.detail.customProperties.boat_reserved_today;
|
||||||
if (boat_reserved_today){
|
if (boat_reserved_today) {
|
||||||
alert(event.detail.label.trim()+' wurde heute reserviert. Bitte kontrolliere, dass du die Reservierung nicht störst.');
|
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;
|
boat_in_ottensheim = event.detail.customProperties.boat_in_ottensheim;
|
||||||
|
|
||||||
const amount_seats = event.detail.customProperties.amount_seats;
|
const amount_seats = event.detail.customProperties.amount_seats;
|
||||||
setMaxAmountRowers("newrower", amount_seats);
|
setMaxAmountRowers("newrower", amount_seats);
|
||||||
|
|
||||||
let only_steering = <HTMLSelectElement>document.querySelector('#shipmaster_only_steering');
|
let only_steering = <HTMLSelectElement>(
|
||||||
|
document.querySelector("#shipmaster_only_steering")
|
||||||
|
);
|
||||||
if (event.detail.customProperties.default_handoperated) {
|
if (event.detail.customProperties.default_handoperated) {
|
||||||
only_steering.setAttribute('checked', 'true');
|
only_steering.setAttribute("checked", "true");
|
||||||
}else {
|
} else {
|
||||||
only_steering.removeAttribute('checked');
|
only_steering.removeAttribute("checked");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.detail.customProperties.convert_handoperated_possible) {
|
if (event.detail.customProperties.convert_handoperated_possible) {
|
||||||
only_steering.removeAttribute('disabled');
|
only_steering.removeAttribute("readonly");
|
||||||
}else {
|
} else {
|
||||||
only_steering.setAttribute('disabled', 'disabled');
|
only_steering.setAttribute("readonly", "readonly");
|
||||||
}
|
}
|
||||||
|
|
||||||
const destination = <HTMLSelectElement>(
|
const destination = <HTMLSelectElement>(
|
||||||
@ -170,22 +178,35 @@ function selectBoatChange() {
|
|||||||
|
|
||||||
if (event.detail.customProperties.owner) {
|
if (event.detail.customProperties.owner) {
|
||||||
choiceObjects["newrower"].setChoiceByValue(
|
choiceObjects["newrower"].setChoiceByValue(
|
||||||
event.detail.customProperties.owner.toString(),
|
event.detail.customProperties.owner.toString()
|
||||||
);
|
);
|
||||||
|
|
||||||
if(event.detail.value === '36') {
|
if (event.detail.value === "36") {
|
||||||
/** custom code for Etsch */
|
/** custom code for Etsch */
|
||||||
choiceObjects["newrower"].setChoiceByValue("81");
|
choiceObjects["newrower"].setChoiceByValue("81");
|
||||||
}
|
}
|
||||||
}else if (typeof loggedin_user_id !== 'undefined'){
|
} 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);
|
choiceObjects["newrower"].setChoiceByValue(loggedin_user_id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const inputElement = document.getElementById(
|
const inputElement = document.getElementById(
|
||||||
"departure",
|
"departure"
|
||||||
) as HTMLInputElement;
|
) as HTMLInputElement;
|
||||||
const now = new Date();
|
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;
|
inputElement.value = formattedDateTime;
|
||||||
|
|
||||||
@ -194,7 +215,7 @@ function selectBoatChange() {
|
|||||||
);
|
);
|
||||||
destinput.dispatchEvent(new Event("input"));
|
destinput.dispatchEvent(new Event("input"));
|
||||||
},
|
},
|
||||||
false,
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
choiceObjects[boatSelect.id] = boatChoice;
|
choiceObjects[boatSelect.id] = boatChoice;
|
||||||
@ -222,16 +243,16 @@ function reloadPage() {
|
|||||||
|
|
||||||
function setMaxAmountRowers(name: string, rowers: number) {
|
function setMaxAmountRowers(name: string, rowers: number) {
|
||||||
if (choiceObjects[name]) {
|
if (choiceObjects[name]) {
|
||||||
choiceObjects[name].removeActiveItems(-1);
|
//choiceObjects[name].removeActiveItems(-1);
|
||||||
//let curSelection = choiceObjects[name].getValue(true);
|
let curSelection = choiceObjects[name].getValue(true);
|
||||||
//let amount_to_delete = (<any>curSelection).length - rowers;
|
let amount_to_delete = (<any>curSelection).length - rowers;
|
||||||
|
|
||||||
//if (amount_to_delete > 0){
|
if (amount_to_delete > 0) {
|
||||||
// let to_delete = (<any>curSelection).slice(-amount_to_delete);
|
let to_delete = (<any>curSelection).slice(-amount_to_delete);
|
||||||
// for (let del of to_delete) {
|
for (let del of to_delete) {
|
||||||
// choiceObjects[name].removeActiveItemsByValue(del);
|
choiceObjects[name].removeActiveItemsByValue(del);
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
let input = <HTMLElement>document.querySelector("#" + name);
|
let input = <HTMLElement>document.querySelector("#" + name);
|
||||||
if (input) {
|
if (input) {
|
||||||
@ -239,24 +260,24 @@ function setMaxAmountRowers(name: string, rowers: number) {
|
|||||||
if (rowers === 0) {
|
if (rowers === 0) {
|
||||||
choiceObjects[name].disable();
|
choiceObjects[name].disable();
|
||||||
input.parentElement?.parentElement?.parentElement?.classList.add(
|
input.parentElement?.parentElement?.parentElement?.classList.add(
|
||||||
"hidden",
|
"hidden"
|
||||||
);
|
);
|
||||||
input.parentElement?.parentElement?.parentElement?.classList.add(
|
input.parentElement?.parentElement?.parentElement?.classList.add(
|
||||||
"md:block",
|
"md:block"
|
||||||
);
|
);
|
||||||
input.parentElement?.parentElement?.parentElement?.classList.add(
|
input.parentElement?.parentElement?.parentElement?.classList.add(
|
||||||
"opacity-50",
|
"opacity-50"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
choiceObjects[name].enable();
|
choiceObjects[name].enable();
|
||||||
input.parentElement?.parentElement?.parentElement?.classList.remove(
|
input.parentElement?.parentElement?.parentElement?.classList.remove(
|
||||||
"hidden",
|
"hidden"
|
||||||
);
|
);
|
||||||
input.parentElement?.parentElement?.parentElement?.classList.remove(
|
input.parentElement?.parentElement?.parentElement?.classList.remove(
|
||||||
"md:block",
|
"md:block"
|
||||||
);
|
);
|
||||||
input.parentElement?.parentElement?.parentElement?.classList.remove(
|
input.parentElement?.parentElement?.parentElement?.classList.remove(
|
||||||
"opacity-50",
|
"opacity-50"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,7 +314,7 @@ function setMaxAmountRowers(name: string, rowers: number) {
|
|||||||
|
|
||||||
function initBoatActions() {
|
function initBoatActions() {
|
||||||
const boatSelects = document.querySelectorAll(
|
const boatSelects = document.querySelectorAll(
|
||||||
'.boats-js[data-onclick="true"]',
|
'.boats-js[data-onclick="true"]'
|
||||||
);
|
);
|
||||||
if (boatSelects) {
|
if (boatSelects) {
|
||||||
Array.prototype.forEach.call(boatSelects, (select: HTMLInputElement) => {
|
Array.prototype.forEach.call(boatSelects, (select: HTMLInputElement) => {
|
||||||
@ -366,7 +387,7 @@ function initNewChoice(select: HTMLInputElement) {
|
|||||||
steering_person.setAttribute("required", "required");
|
steering_person.setAttribute("required", "required");
|
||||||
}
|
}
|
||||||
const choice = new Choices(select, {
|
const choice = new Choices(select, {
|
||||||
searchFields: ['label', 'value', 'customProperties.searchableText'],
|
searchFields: ["label", "value", "customProperties.searchableText"],
|
||||||
removeItemButton: true,
|
removeItemButton: true,
|
||||||
loadingText: "Wird geladen...",
|
loadingText: "Wird geladen...",
|
||||||
noResultsText: "Keine Ergebnisse gefunden",
|
noResultsText: "Keine Ergebnisse gefunden",
|
||||||
@ -449,7 +470,7 @@ function initNewChoice(select: HTMLInputElement) {
|
|||||||
steeringSelect.add(new Option(name, user_id));
|
steeringSelect.add(new Option(name, user_id));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
false,
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
select.addEventListener(
|
select.addEventListener(
|
||||||
@ -478,7 +499,7 @@ function initNewChoice(select: HTMLInputElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
false,
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
choiceObjects[select.id] = choice;
|
choiceObjects[select.id] = choice;
|
||||||
@ -508,7 +529,7 @@ function initToggle() {
|
|||||||
}
|
}
|
||||||
sessionStorage.setItem(
|
sessionStorage.setItem(
|
||||||
"tripsFilter",
|
"tripsFilter",
|
||||||
JSON.stringify(Array.from(filterMap.entries())),
|
JSON.stringify(Array.from(filterMap.entries()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
resetFilteredElements();
|
resetFilteredElements();
|
||||||
@ -535,7 +556,7 @@ function initToggle() {
|
|||||||
} else {
|
} else {
|
||||||
sessionStorage.setItem(
|
sessionStorage.setItem(
|
||||||
"tripsFilter",
|
"tripsFilter",
|
||||||
JSON.stringify(Array.from(filterObject.entries())),
|
JSON.stringify(Array.from(filterObject.entries()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -547,14 +568,14 @@ function resetFilteredElements() {
|
|||||||
hiddenElements,
|
hiddenElements,
|
||||||
(hiddenElement: HTMLButtonElement) => {
|
(hiddenElement: HTMLButtonElement) => {
|
||||||
hiddenElement.classList.remove("hidden");
|
hiddenElement.classList.remove("hidden");
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function triggerFilterAction(activeFilter: any) {
|
function triggerFilterAction(activeFilter: any) {
|
||||||
const activeBtn = document.querySelector(
|
const activeBtn = document.querySelector(
|
||||||
'button[data-action="' + activeFilter + '"]',
|
'button[data-action="' + activeFilter + '"]'
|
||||||
);
|
);
|
||||||
if (activeBtn) {
|
if (activeBtn) {
|
||||||
activeBtn.setAttribute("aria-pressed", "true");
|
activeBtn.setAttribute("aria-pressed", "true");
|
||||||
@ -654,7 +675,7 @@ function initSidebar() {
|
|||||||
sidebar.toggle();
|
sidebar.toggle();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -751,13 +772,14 @@ function addRelationMagic(bodyElement: HTMLElement) {
|
|||||||
dataList.options,
|
dataList.options,
|
||||||
function (option) {
|
function (option) {
|
||||||
return option.value === field.value;
|
return option.value === field.value;
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (option && option.value !== ""){
|
if (option && option.value !== "") {
|
||||||
// Get distance
|
// Get distance
|
||||||
const distance = option.getAttribute("distance");
|
const distance = option.getAttribute("distance");
|
||||||
if (distance && relatedField.value === "") relatedField.value = distance;
|
if (distance && relatedField.value === "")
|
||||||
|
relatedField.value = distance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -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('(cox2)');
|
||||||
await expect(page.locator('body')).toContainText('Ottensheim (25 km)');
|
await expect(page.locator('body')).toContainText('Ottensheim (25 km)');
|
||||||
await expect(page.locator('body')).toContainText('Ruderer: cox2, rower2');
|
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) => {
|
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('(cox2)');
|
||||||
await expect(page.locator('body')).toContainText('Ottensheim (25 km)');
|
await expect(page.locator('body')).toContainText('Ottensheim (25 km)');
|
||||||
await expect(page.locator('body')).toContainText('Ruderer: cox2, rower2');
|
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();
|
||||||
});
|
});
|
||||||
|
@ -213,3 +213,9 @@ CREATE TABLE IF NOT EXISTS "trailer_reservation" (
|
|||||||
"created_at" datetime not null default CURRENT_TIMESTAMP
|
"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
|
||||||
|
);
|
||||||
|
|
||||||
|
73
notes.md
73
notes.md
@ -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 );
|
|
||||||
```
|
|
13
seeds.sql
13
seeds.sql
@ -9,6 +9,9 @@ INSERT INTO "role" (name) VALUES ('paid');
|
|||||||
INSERT INTO "role" (name) VALUES ('Vorstand');
|
INSERT INTO "role" (name) VALUES ('Vorstand');
|
||||||
INSERT INTO "role" (name) VALUES ('Bootsführer');
|
INSERT INTO "role" (name) VALUES ('Bootsführer');
|
||||||
INSERT INTO "role" (name) VALUES ('schnupperant');
|
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" (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,1);
|
||||||
INSERT INTO "user_role" (user_id, role_id) VALUES(1,2);
|
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_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" (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,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 "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);
|
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 ('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) 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, 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 ('Wanderfahrt');
|
||||||
INSERT INTO "logbook_type" (name) VALUES ('Regatta');
|
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');
|
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 "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('Großer Hänger');
|
||||||
INSERT INTO "trailer" (name) VALUES('Kleiner Hänger');
|
INSERT INTO "trailer" (name) VALUES('Kleiner Hänger');
|
||||||
|
insert into distance(destination, distance_in_km) values('Ottensheim', 25);
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
|
use chrono::NaiveDateTime;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rocket::serde::{Deserialize, Serialize};
|
use rocket::serde::{Deserialize, Serialize};
|
||||||
use rocket::FromForm;
|
use rocket::FromForm;
|
||||||
@ -391,6 +392,39 @@ ORDER BY amount_seats DESC
|
|||||||
.await
|
.await
|
||||||
.ok()
|
.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)]
|
#[cfg(test)]
|
||||||
|
33
src/model/distance.rs
Normal file
33
src/model/distance.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,10 @@ use ics::{
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sqlx::{FromRow, Row, SqlitePool};
|
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)]
|
#[derive(Serialize, Clone, FromRow, Debug, PartialEq)]
|
||||||
pub struct Event {
|
pub struct Event {
|
||||||
@ -213,12 +216,35 @@ WHERE trip_details.id=?
|
|||||||
.ok()
|
.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(
|
pub async fn create(
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
name: &str,
|
name: &str,
|
||||||
planned_amount_cox: i32,
|
planned_amount_cox: i32,
|
||||||
trip_details: &TripDetails,
|
trip_details: &TripDetails,
|
||||||
) {
|
) {
|
||||||
|
if trip_details.always_show {
|
||||||
|
Self::advertise(
|
||||||
|
db,
|
||||||
|
&trip_details.day,
|
||||||
|
&trip_details.planned_starting_time,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO planned_event(name, planned_amount_cox, trip_details_id) VALUES(?, ?, ?)",
|
"INSERT INTO planned_event(name, planned_amount_cox, trip_details_id) VALUES(?, ?, ?)",
|
||||||
name,
|
name,
|
||||||
@ -258,6 +284,16 @@ WHERE trip_details.id=?
|
|||||||
.await
|
.await
|
||||||
.unwrap(); //Okay, as planned_event can only be created with proper DB backing
|
.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 {
|
if update.max_people == 0 && !was_already_cancelled {
|
||||||
let coxes = Registration::all_cox(db, self.id).await;
|
let coxes = Registration::all_cox(db, self.id).await;
|
||||||
for user in coxes {
|
for user in coxes {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
use chrono::{Datelike, Local, NaiveDateTime};
|
use chrono::{Datelike, Duration, Local, NaiveDateTime};
|
||||||
use rocket::FromForm;
|
use rocket::FromForm;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
||||||
@ -60,6 +60,22 @@ pub struct LogToFinalize {
|
|||||||
pub rowers: Vec<i64>,
|
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 {
|
impl TryFrom<LogToAdd> for LogToFinalize {
|
||||||
type Error = String;
|
type Error = String;
|
||||||
|
|
||||||
@ -94,6 +110,25 @@ pub struct LogbookWithBoatAndRowers {
|
|||||||
pub rowers: Vec<User>,
|
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)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum LogbookUpdateError {
|
pub enum LogbookUpdateError {
|
||||||
NotYourEntry,
|
NotYourEntry,
|
||||||
@ -105,6 +140,9 @@ pub enum LogbookUpdateError {
|
|||||||
UserNotAllowedToUseBoat,
|
UserNotAllowedToUseBoat,
|
||||||
OnlyAllowedToEndTripsEndingToday,
|
OnlyAllowedToEndTripsEndingToday,
|
||||||
TooFast(i64, i64),
|
TooFast(i64, i64),
|
||||||
|
AlreadyFinalized,
|
||||||
|
ExternalSteeringPersonMustSteerOrShipmaster,
|
||||||
|
BoatAlreadyOnWater,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@ -129,6 +167,8 @@ pub enum LogbookCreateError {
|
|||||||
OnlyAllowedToEndTripsEndingToday,
|
OnlyAllowedToEndTripsEndingToday,
|
||||||
CantChangeHandoperatableStatusForThisBoat,
|
CantChangeHandoperatableStatusForThisBoat,
|
||||||
TooFast(i64, i64),
|
TooFast(i64, i64),
|
||||||
|
AlreadyFinalized,
|
||||||
|
ExternalSteeringPersonMustSteerOrShipmaster,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LogbookUpdateError> for LogbookCreateError {
|
impl From<LogbookUpdateError> for LogbookCreateError {
|
||||||
@ -153,6 +193,11 @@ impl From<LogbookUpdateError> for LogbookCreateError {
|
|||||||
LogbookCreateError::OnlyAllowedToEndTripsEndingToday
|
LogbookCreateError::OnlyAllowedToEndTripsEndingToday
|
||||||
}
|
}
|
||||||
LogbookUpdateError::TooFast(km, min) => LogbookCreateError::TooFast(km, min),
|
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
|
.await
|
||||||
.ok()
|
.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!(
|
sqlx::query_as!(
|
||||||
Self,
|
Self,
|
||||||
"
|
"
|
||||||
@ -219,15 +264,7 @@ ORDER BY departure DESC
|
|||||||
|
|
||||||
let mut ret = Vec::new();
|
let mut ret = Vec::new();
|
||||||
for log in logs {
|
for log in logs {
|
||||||
ret.push(LogbookWithBoatAndRowers {
|
ret.push(LogbookWithBoatAndRowers::from(db, log).await);
|
||||||
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
|
ret
|
||||||
}
|
}
|
||||||
@ -242,7 +279,7 @@ ORDER BY departure DESC
|
|||||||
FROM logbook
|
FROM logbook
|
||||||
JOIN rower ON logbook.id = rower.logbook_id
|
JOIN rower ON logbook.id = rower.logbook_id
|
||||||
WHERE arrival is not null AND rower_id = {}
|
WHERE arrival is not null AND rower_id = {}
|
||||||
ORDER BY departure DESC
|
ORDER BY arrival DESC
|
||||||
", user.id)
|
", user.id)
|
||||||
)
|
)
|
||||||
.fetch_all(db)
|
.fetch_all(db)
|
||||||
@ -251,15 +288,7 @@ ORDER BY departure DESC
|
|||||||
|
|
||||||
let mut ret = Vec::new();
|
let mut ret = Vec::new();
|
||||||
for log in logs {
|
for log in logs {
|
||||||
ret.push(LogbookWithBoatAndRowers {
|
ret.push(LogbookWithBoatAndRowers::from(db, log).await);
|
||||||
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
|
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
|
SELECT id, boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km, comments, logtype
|
||||||
FROM logbook
|
FROM logbook
|
||||||
WHERE arrival is not null AND arrival LIKE '{}-%'
|
WHERE arrival is not null AND arrival LIKE '{}-%'
|
||||||
ORDER BY departure DESC
|
ORDER BY arrival DESC
|
||||||
", year)
|
", year)
|
||||||
)
|
)
|
||||||
.fetch_all(db)
|
.fetch_all(db)
|
||||||
@ -284,15 +313,7 @@ ORDER BY departure DESC
|
|||||||
|
|
||||||
let mut ret = Vec::new();
|
let mut ret = Vec::new();
|
||||||
for log in logs {
|
for log in logs {
|
||||||
ret.push(LogbookWithBoatAndRowers {
|
ret.push(LogbookWithBoatAndRowers::from(db, log).await);
|
||||||
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
|
ret
|
||||||
}
|
}
|
||||||
@ -329,13 +350,12 @@ ORDER BY departure DESC
|
|||||||
let mut tx = db.begin().await.unwrap();
|
let mut tx = db.begin().await.unwrap();
|
||||||
|
|
||||||
let inserted_row = sqlx::query!(
|
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.boat_id,
|
||||||
log.shipmaster,
|
log.shipmaster,
|
||||||
log.steering_person,
|
log.steering_person,
|
||||||
log.shipmaster_only_steering,
|
log.shipmaster_only_steering,
|
||||||
log.departure,
|
log.departure,
|
||||||
log.arrival,
|
|
||||||
log.destination,
|
log.destination,
|
||||||
log.distance_in_km,
|
log.distance_in_km,
|
||||||
log.comments,
|
log.comments,
|
||||||
@ -391,6 +411,18 @@ ORDER BY departure DESC
|
|||||||
if user.on_water(db).await {
|
if user.on_water(db).await {
|
||||||
return Err(LogbookCreateError::RowerAlreadyOnWater(Box::new(user)));
|
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 {
|
if !boat.shipmaster_allowed(db, created_by_user).await {
|
||||||
@ -437,23 +469,33 @@ ORDER BY departure DESC
|
|||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn distances(db: &SqlitePool) -> Vec<(String, i64)> {
|
pub async fn update(
|
||||||
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;")
|
&self,
|
||||||
.fetch_all(db)
|
db: &SqlitePool,
|
||||||
.await
|
data: LogToUpdate,
|
||||||
.unwrap();
|
user: &User,
|
||||||
|
) -> Result<(), LogbookAdminUpdateError> {
|
||||||
result
|
if !user.has_role(db, "Vorstand").await {
|
||||||
.into_iter()
|
return Err(LogbookAdminUpdateError::NotAllowed);
|
||||||
.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>) {
|
async fn remove_rowers(&self, db: &mut Transaction<'_, Sqlite>) {
|
||||||
@ -496,6 +538,10 @@ ORDER BY departure DESC
|
|||||||
return Err(LogbookUpdateError::NotYourEntry);
|
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
|
let boat = Boat::find_by_id_tx(db, self.boat_id as i32).await.unwrap(); //ok
|
||||||
|
|
||||||
if boat.amount_seats == 1 {
|
if boat.amount_seats == 1 {
|
||||||
@ -529,6 +575,12 @@ ORDER BY departure DESC
|
|||||||
return Err(LogbookUpdateError::ArrivalNotAfterDeparture);
|
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;
|
let duration_in_mins = (arr.and_utc().timestamp() - dep.and_utc().timestamp()) / 60;
|
||||||
// Not possible to row < 1 min / 500 m = < 2 min / km
|
// Not possible to row < 1 min / 500 m = < 2 min / km
|
||||||
let possible_distance_km = duration_in_mins / 2;
|
let possible_distance_km = duration_in_mins / 2;
|
||||||
@ -553,6 +605,19 @@ ORDER BY departure DESC
|
|||||||
|
|
||||||
self.remove_rowers(db).await;
|
self.remove_rowers(db).await;
|
||||||
for rower in &log.rowers {
|
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)
|
Rower::create(db, self.id, *rower)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| LogbookUpdateError::RowerCreateError(*rower, e.to_string()))?;
|
.map_err(|e| LogbookUpdateError::RowerCreateError(*rower, e.to_string()))?;
|
||||||
@ -623,16 +688,58 @@ ORDER BY departure DESC
|
|||||||
pub async fn delete(&self, db: &SqlitePool, user: &User) -> Result<(), LogbookDeleteError> {
|
pub async fn delete(&self, db: &SqlitePool, user: &User) -> Result<(), LogbookDeleteError> {
|
||||||
Log::create(db, format!("{} deleted trip: {self:?}", user.name)).await;
|
Log::create(db, format!("{} deleted trip: {self:?}", user.name)).await;
|
||||||
|
|
||||||
|
if self.arrival.is_none() {
|
||||||
if user.has_role(db, "admin").await
|
if user.has_role(db, "admin").await
|
||||||
|| user.has_role(db, "Vorstand").await
|
|| user.has_role(db, "Vorstand").await
|
||||||
|| user.id == self.shipmaster
|
|| 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)
|
sqlx::query!("DELETE FROM logbook WHERE id=?", self.id)
|
||||||
.execute(db)
|
.execute(db)
|
||||||
.await
|
.await
|
||||||
.unwrap(); //Okay, because we can only create a Logbook of a valid id
|
.unwrap(); //Okay, because we can only create a Logbook of a valid id
|
||||||
return Ok(());
|
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)
|
Err(LogbookDeleteError::NotYourEntry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -953,21 +1060,6 @@ mod test {
|
|||||||
assert_eq!(res, Err(LogbookCreateError::TooManyRowers(1, 2)));
|
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]
|
#[sqlx::test]
|
||||||
fn test_succ_home() {
|
fn test_succ_home() {
|
||||||
let pool = testdb!();
|
let pool = testdb!();
|
||||||
|
@ -182,7 +182,7 @@ dein Vereinsbeitrag für das aktuelle Jahr beträgt {}€",
|
|||||||
fees.name
|
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\
|
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
|
Wenn du die Vereinsgebühren schon bezahlt hast, kannst du diese Mail einfach ignorieren.\n\n
|
||||||
Beste Grüße\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\
|
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.
|
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\
|
Mit freundlichen Grüßen,\n\
|
||||||
Der Vorstand");
|
Der Vorstand");
|
||||||
|
@ -14,6 +14,7 @@ pub mod boat;
|
|||||||
pub mod boatdamage;
|
pub mod boatdamage;
|
||||||
pub mod boathouse;
|
pub mod boathouse;
|
||||||
pub mod boatreservation;
|
pub mod boatreservation;
|
||||||
|
pub mod distance;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod family;
|
pub mod family;
|
||||||
pub mod location;
|
pub mod location;
|
||||||
|
@ -179,6 +179,14 @@ ORDER BY read_at DESC, created_at DESC;
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.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)]
|
#[cfg(test)]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use chrono::NaiveDate;
|
use chrono::{Local, NaiveDate};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
@ -164,6 +164,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> {
|
pub async fn get_for_day(db: &SqlitePool, day: NaiveDate) -> Vec<TripWithUserAndType> {
|
||||||
let day = format!("{day}");
|
let day = format!("{day}");
|
||||||
let trips = sqlx::query_as!(
|
let trips = sqlx::query_as!(
|
||||||
|
@ -213,7 +213,7 @@ ORDER BY day;",
|
|||||||
|
|
||||||
pub(crate) async fn user_allowed_to_change(&self, db: &SqlitePool, user: &User) -> bool {
|
pub(crate) async fn user_allowed_to_change(&self, db: &SqlitePool, user: &User) -> bool {
|
||||||
if self.belongs_to_event(db).await {
|
if self.belongs_to_event(db).await {
|
||||||
user.has_role(db, "planned_event").await
|
user.has_role(db, "manage_events").await
|
||||||
} else {
|
} else {
|
||||||
self.user_is_cox(db, user).await != CoxAtTrip::No
|
self.user_is_cox(db, user).await != CoxAtTrip::No
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ const REGULAR: i32 = 22000;
|
|||||||
const UNTERSTUETZEND: i32 = 2500;
|
const UNTERSTUETZEND: i32 = 2500;
|
||||||
const FOERDERND: i32 = 8500;
|
const FOERDERND: i32 = 8500;
|
||||||
pub const SCHECKBUCH: i32 = 3000;
|
pub const SCHECKBUCH: i32 = 3000;
|
||||||
|
const EINSCHREIBGEBUEHR: i32 = 3000;
|
||||||
|
|
||||||
#[derive(FromRow, Serialize, Deserialize, Clone, Debug, Eq, Hash, PartialEq)]
|
#[derive(FromRow, Serialize, Deserialize, Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
pub struct User {
|
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.
|
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
|
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.
|
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!
|
Wir freuen uns darauf, dich bald am Wasser zu sehen und gemeinsam tolle Erfahrungen zu sammeln!
|
||||||
|
|
||||||
@ -361,19 +362,51 @@ 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 {
|
if self.has_role(db, "Unterstützend").await {
|
||||||
fee.add("Unterstützendes Mitglied".into(), UNTERSTUETZEND);
|
fee.add("Unterstützendes Mitglied".into(), UNTERSTUETZEND);
|
||||||
} else if self.has_role(db, "Förderndes Mitglied").await {
|
} else if self.has_role(db, "Förderndes Mitglied").await {
|
||||||
fee.add("Förderndes Mitglied".into(), FOERDERND);
|
fee.add("Förderndes Mitglied".into(), FOERDERND);
|
||||||
} else if Family::find_by_opt_id(db, self.family_id).await.is_none() {
|
} 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 {
|
if self.has_role(db, "Student").await || self.has_role(db, "Schüler").await {
|
||||||
|
if halfprice {
|
||||||
|
fee.add("Schüler/Student (Halbpreis)".into(), STUDENT_OR_PUPIL / 2);
|
||||||
|
} else {
|
||||||
fee.add("Schüler/Student".into(), STUDENT_OR_PUPIL);
|
fee.add("Schüler/Student".into(), STUDENT_OR_PUPIL);
|
||||||
|
}
|
||||||
} else if self.has_role(db, "Ehrenmitglied").await {
|
} else if self.has_role(db, "Ehrenmitglied").await {
|
||||||
fee.add("Ehrenmitglied".into(), 0);
|
fee.add("Ehrenmitglied".into(), 0);
|
||||||
|
} else {
|
||||||
|
if halfprice {
|
||||||
|
fee.add("Mitgliedsbeitrag (Halbpreis)".into(), REGULAR / 2);
|
||||||
} else {
|
} else {
|
||||||
fee.add("Mitgliedsbeitrag".into(), REGULAR);
|
fee.add("Mitgliedsbeitrag".into(), REGULAR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fee
|
fee
|
||||||
}
|
}
|
||||||
@ -627,6 +660,14 @@ ORDER BY last_access DESC
|
|||||||
.is_ok()
|
.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<'_>) {
|
pub async fn update(&self, db: &SqlitePool, data: UserEditForm<'_>) {
|
||||||
let mut family_id = data.family_id;
|
let mut family_id = data.family_id;
|
||||||
|
|
||||||
@ -714,6 +755,7 @@ ORDER BY last_access DESC
|
|||||||
if ![
|
if ![
|
||||||
"n-sageder",
|
"n-sageder",
|
||||||
"p-hofer",
|
"p-hofer",
|
||||||
|
"marie-birner",
|
||||||
"daniel-kortschak",
|
"daniel-kortschak",
|
||||||
"rudernlinz",
|
"rudernlinz",
|
||||||
"m-birner",
|
"m-birner",
|
||||||
@ -896,266 +938,83 @@ impl<'r> FromRequest<'r> for User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TechUser {
|
/// 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,
|
pub(crate) user: User,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for TechUser {
|
impl Deref for $name {
|
||||||
type Target = User;
|
type Target = User;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.user
|
&self.user
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
impl $name {
|
||||||
impl<'r> FromRequest<'r> for TechUser {
|
pub fn into_inner(self) -> User {
|
||||||
|
self.user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for $name {
|
||||||
type Error = LoginError;
|
type Error = LoginError;
|
||||||
|
|
||||||
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
let db = req.rocket().state::<SqlitePool>().unwrap();
|
let db = req.rocket().state::<SqlitePool>().unwrap();
|
||||||
|
|
||||||
match User::from_request(req).await {
|
match User::from_request(req).await {
|
||||||
Outcome::Success(user) => {
|
Outcome::Success(user) => {
|
||||||
if user.has_role(db, "tech").await {
|
if special_user!(@check_roles user, db, $($role)*) {
|
||||||
Outcome::Success(TechUser { user })
|
Outcome::Success($name { user })
|
||||||
} else {
|
} else {
|
||||||
Outcome::Error((Status::Forbidden, LoginError::NotACox))
|
Outcome::Forward(Status::Forbidden)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Outcome::Error(f) => Outcome::Error(f),
|
Outcome::Error(f) => Outcome::Error(f),
|
||||||
Outcome::Forward(f) => Outcome::Forward(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 {
|
impl $name {
|
||||||
pub async fn new(db: &SqlitePool, user: User) -> Option<Self> {
|
pub async fn new(db: &SqlitePool, user: User) -> Option<Self> {
|
||||||
if user.has_role(db, "cox").await {
|
if special_user!(@check_roles user, db, $($role)*) {
|
||||||
Some(CoxUser { user })
|
Some($name { user })
|
||||||
} else {
|
} else {
|
||||||
None
|
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 })
|
|
||||||
} else {
|
|
||||||
Outcome::Error((Status::Forbidden, LoginError::NotACox))
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
Outcome::Error(f) => Outcome::Error(f),
|
(@check_roles $user:ident, $db:ident, $(+$role:expr),* $(,-$neg_role:expr)*) => {
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Outcome::Error(f) => Outcome::Error(f),
|
|
||||||
Outcome::Forward(f) => Outcome::Forward(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct AllowedForPlannedTripsUser(pub(crate) User);
|
|
||||||
|
|
||||||
#[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))
|
let mut has_positive_role = false;
|
||||||
} else {
|
$(
|
||||||
Outcome::Error((Status::Forbidden, LoginError::NotACox))
|
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)]
|
special_user!(TechUser, +"tech");
|
||||||
pub struct SchnupperBetreuerUser(pub(crate) User);
|
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");
|
||||||
|
|
||||||
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)]
|
#[derive(FromRow, Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct UserWithRolesAndMembershipPdf {
|
pub struct UserWithRolesAndMembershipPdf {
|
||||||
#[serde(flatten)]
|
#[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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -1266,7 +1106,7 @@ mod test {
|
|||||||
fn test_cox() {
|
fn test_cox() {
|
||||||
let pool = testdb!();
|
let pool = testdb!();
|
||||||
let res = User::cox(&pool).await;
|
let res = User::cox(&pool).await;
|
||||||
assert_eq!(res.len(), 3);
|
assert_eq!(res.len(), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sqlx::test]
|
#[sqlx::test]
|
||||||
|
@ -14,8 +14,12 @@ pub fn schedule(db: &SqlitePool, config: &Config) {
|
|||||||
let openweathermap_key = config.openweathermap_key.clone();
|
let openweathermap_key = config.openweathermap_key.clone();
|
||||||
|
|
||||||
tokio::task::spawn(async {
|
tokio::task::spawn(async {
|
||||||
waterlevel::update(&db).await.unwrap();
|
if let Err(e) = waterlevel::update(&db).await {
|
||||||
weather::update(&db, &openweathermap_key).await.unwrap();
|
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();
|
let mut sched = JobScheduler::new();
|
||||||
|
|
||||||
@ -26,10 +30,12 @@ pub fn schedule(db: &SqlitePool, config: &Config) {
|
|||||||
// nicer one's rust (stable) support async closures
|
// nicer one's rust (stable) support async closures
|
||||||
task::block_in_place(|| {
|
task::block_in_place(|| {
|
||||||
tokio::runtime::Handle::current().block_on(async {
|
tokio::runtime::Handle::current().block_on(async {
|
||||||
waterlevel::update(&db_clone).await.unwrap();
|
if let Err(e) = waterlevel::update(&db_clone).await {
|
||||||
weather::update(&db_clone, &openweathermap_key)
|
log::error!("Water level update error: {e}, trying again next time");
|
||||||
.await
|
}
|
||||||
.unwrap();
|
if let Err(e) = weather::update(&db_clone, &openweathermap_key).await {
|
||||||
|
log::error!("Weather update error: {e}, trying again next time");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
@ -9,14 +9,14 @@ use sqlx::SqlitePool;
|
|||||||
use crate::model::log::Log;
|
use crate::model::log::Log;
|
||||||
use crate::model::mail::Mail;
|
use crate::model::mail::Mail;
|
||||||
use crate::model::role::Role;
|
use crate::model::role::Role;
|
||||||
use crate::model::user::AdminUser;
|
|
||||||
use crate::model::user::UserWithDetails;
|
use crate::model::user::UserWithDetails;
|
||||||
|
use crate::model::user::{AdminUser, VorstandUser};
|
||||||
use crate::tera::Config;
|
use crate::tera::Config;
|
||||||
|
|
||||||
#[get("/mail")]
|
#[get("/mail")]
|
||||||
async fn index(
|
async fn index(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
admin: AdminUser,
|
admin: VorstandUser,
|
||||||
flash: Option<FlashMessage<'_>>,
|
flash: Option<FlashMessage<'_>>,
|
||||||
) -> Template {
|
) -> Template {
|
||||||
let mut context = Context::new();
|
let mut context = Context::new();
|
||||||
@ -65,7 +65,7 @@ async fn update(
|
|||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
data: Form<MailToSend<'_>>,
|
data: Form<MailToSend<'_>>,
|
||||||
config: &State<Config>,
|
config: &State<Config>,
|
||||||
admin: AdminUser,
|
admin: VorstandUser,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
let d = data.into_inner();
|
let d = data.into_inner();
|
||||||
Log::create(db, format!("{admin:?} trying to send this mail: {d:?}")).await;
|
Log::create(db, format!("{admin:?} trying to send this mail: {d:?}")).await;
|
||||||
|
@ -2,7 +2,7 @@ use crate::model::{
|
|||||||
log::Log,
|
log::Log,
|
||||||
notification::Notification,
|
notification::Notification,
|
||||||
role::Role,
|
role::Role,
|
||||||
user::{AdminUser, User, UserWithDetails},
|
user::{User, UserWithDetails, VorstandUser},
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rocket::{
|
use rocket::{
|
||||||
@ -18,7 +18,7 @@ use sqlx::SqlitePool;
|
|||||||
#[get("/notification")]
|
#[get("/notification")]
|
||||||
async fn index(
|
async fn index(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
user: AdminUser,
|
user: VorstandUser,
|
||||||
flash: Option<FlashMessage<'_>>,
|
flash: Option<FlashMessage<'_>>,
|
||||||
) -> Template {
|
) -> Template {
|
||||||
let mut context = Context::new();
|
let mut context = Context::new();
|
||||||
@ -62,7 +62,7 @@ pub struct NotificationToSendUser {
|
|||||||
async fn send_group(
|
async fn send_group(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
data: Form<NotificationToSendGroup>,
|
data: Form<NotificationToSendGroup>,
|
||||||
admin: AdminUser,
|
admin: VorstandUser,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
let d = data.into_inner();
|
let d = data.into_inner();
|
||||||
Log::create(
|
Log::create(
|
||||||
@ -89,7 +89,7 @@ async fn send_group(
|
|||||||
async fn send_user(
|
async fn send_user(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
data: Form<NotificationToSendUser>,
|
data: Form<NotificationToSendUser>,
|
||||||
admin: AdminUser,
|
admin: VorstandUser,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
let d = data.into_inner();
|
let d = data.into_inner();
|
||||||
Log::create(
|
Log::create(
|
||||||
|
@ -29,7 +29,7 @@ async fn index(
|
|||||||
context.insert("schnupperanten", &users);
|
context.insert("schnupperanten", &users);
|
||||||
context.insert(
|
context.insert(
|
||||||
"loggedin_user",
|
"loggedin_user",
|
||||||
&UserWithDetails::from_user(user.into(), db).await,
|
&UserWithDetails::from_user(user.user, db).await,
|
||||||
);
|
);
|
||||||
|
|
||||||
Template::render("admin/schnupper/index", context.into_json())
|
Template::render("admin/schnupper/index", context.into_json())
|
||||||
|
@ -7,13 +7,14 @@ use crate::{
|
|||||||
logbook::Logbook,
|
logbook::Logbook,
|
||||||
role::Role,
|
role::Role,
|
||||||
user::{
|
user::{
|
||||||
AdminUser, User, UserWithDetails, UserWithMembershipPdf, UserWithRolesAndMembershipPdf,
|
AdminUser, AllowedToEditPaymentStatusUser, ManageUserUser, SchnupperBetreuerUser, User,
|
||||||
VorstandUser,
|
UserWithDetails, UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tera::Config,
|
tera::Config,
|
||||||
};
|
};
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
|
use lettre::Address;
|
||||||
use rocket::{
|
use rocket::{
|
||||||
form::Form,
|
form::Form,
|
||||||
fs::TempFile,
|
fs::TempFile,
|
||||||
@ -54,8 +55,8 @@ async fn index(
|
|||||||
.map(|u| async move { UserWithRolesAndMembershipPdf::from_user(db, u).await })
|
.map(|u| async move { UserWithRolesAndMembershipPdf::from_user(db, u).await })
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let user: User = user.into();
|
let user: User = user.into_inner();
|
||||||
let allowed_to_edit = user.has_role(db, "admin").await;
|
let allowed_to_edit = ManageUserUser::new(db, user.clone()).await.is_some();
|
||||||
|
|
||||||
let users: Vec<UserWithRolesAndMembershipPdf> = join_all(user_futures).await;
|
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 users: Vec<UserWithRolesAndMembershipPdf> = join_all(user_futures).await;
|
||||||
|
|
||||||
let user: User = user.user;
|
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 roles = Role::all(db).await;
|
||||||
let families = Family::all_with_members(db).await;
|
let families = Family::all_with_members(db).await;
|
||||||
@ -110,7 +111,7 @@ async fn index_admin(
|
|||||||
#[get("/user/fees")]
|
#[get("/user/fees")]
|
||||||
async fn fees(
|
async fn fees(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
admin: VorstandUser,
|
user: VorstandUser,
|
||||||
flash: Option<FlashMessage<'_>>,
|
flash: Option<FlashMessage<'_>>,
|
||||||
) -> Template {
|
) -> Template {
|
||||||
let mut context = Context::new();
|
let mut context = Context::new();
|
||||||
@ -130,7 +131,7 @@ async fn fees(
|
|||||||
}
|
}
|
||||||
context.insert(
|
context.insert(
|
||||||
"loggedin_user",
|
"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())
|
Template::render("admin/user/fees", context.into_json())
|
||||||
@ -161,7 +162,7 @@ async fn scheckbuch(
|
|||||||
}
|
}
|
||||||
context.insert(
|
context.insert(
|
||||||
"loggedin_user",
|
"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())
|
Template::render("admin/user/scheckbuch", context.into_json())
|
||||||
@ -170,7 +171,7 @@ async fn scheckbuch(
|
|||||||
#[get("/user/fees/paid?<user_ids>")]
|
#[get("/user/fees/paid?<user_ids>")]
|
||||||
async fn fees_paid(
|
async fn fees_paid(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
admin: AdminUser,
|
calling_user: AllowedToEditPaymentStatusUser,
|
||||||
user_ids: Vec<i32>,
|
user_ids: Vec<i32>,
|
||||||
referer: Referer,
|
referer: Referer,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
@ -181,7 +182,10 @@ async fn fees_paid(
|
|||||||
if user.has_role(db, "paid").await {
|
if user.has_role(db, "paid").await {
|
||||||
Log::create(
|
Log::create(
|
||||||
db,
|
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;
|
.await;
|
||||||
user.remove_role(db, &Role::find_by_name(db, "paid").await.unwrap())
|
user.remove_role(db, &Role::find_by_name(db, "paid").await.unwrap())
|
||||||
@ -189,7 +193,10 @@ async fn fees_paid(
|
|||||||
} else {
|
} else {
|
||||||
Log::create(
|
Log::create(
|
||||||
db,
|
db,
|
||||||
format!("{} set fees paid for '{}'", admin.user.name, user.name),
|
format!(
|
||||||
|
"{} set fees paid for '{}'",
|
||||||
|
calling_user.user.name, user.name
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
user.add_role(db, &Role::find_by_name(db, "paid").await.unwrap())
|
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")]
|
#[get("/user/<user>/send-welcome-mail")]
|
||||||
async fn send_welcome_mail(
|
async fn send_welcome_mail(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
_admin: AdminUser,
|
_admin: ManageUserUser,
|
||||||
config: &State<Config>,
|
config: &State<Config>,
|
||||||
user: i32,
|
user: i32,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
@ -226,7 +233,7 @@ async fn send_welcome_mail(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/user/<user>/reset-pw")]
|
#[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;
|
let user = User::find_by_id(db, user).await;
|
||||||
match user {
|
match user {
|
||||||
Some(user) => {
|
Some(user) => {
|
||||||
@ -246,7 +253,7 @@ async fn resetpw(db: &State<SqlitePool>, admin: AdminUser, user: i32) -> Flash<R
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/user/<user>/delete")]
|
#[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;
|
let user = User::find_by_id(db, user).await;
|
||||||
Log::create(db, format!("{} deleted user: {user:?}", admin.user.name)).await;
|
Log::create(db, format!("{} deleted user: {user:?}", admin.user.name)).await;
|
||||||
match user {
|
match user {
|
||||||
@ -283,7 +290,7 @@ pub struct UserEditForm<'a> {
|
|||||||
async fn update(
|
async fn update(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
data: Form<UserEditForm<'_>>,
|
data: Form<UserEditForm<'_>>,
|
||||||
admin: AdminUser,
|
admin: ManageUserUser,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
let user = User::find_by_id(db, data.id).await;
|
let user = User::find_by_id(db, data.id).await;
|
||||||
Log::create(
|
Log::create(
|
||||||
@ -306,7 +313,7 @@ async fn update(
|
|||||||
#[get("/user/<user>/membership")]
|
#[get("/user/<user>/membership")]
|
||||||
async fn download_membership_pdf(
|
async fn download_membership_pdf(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
admin: AdminUser,
|
admin: ManageUserUser,
|
||||||
user: i32,
|
user: i32,
|
||||||
) -> (ContentType, Vec<u8>) {
|
) -> (ContentType, Vec<u8>) {
|
||||||
let user = User::find_by_id(db, user).await.unwrap();
|
let user = User::find_by_id(db, user).await.unwrap();
|
||||||
@ -332,7 +339,7 @@ struct UserAddForm<'r> {
|
|||||||
async fn create(
|
async fn create(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
data: Form<UserAddForm<'_>>,
|
data: Form<UserAddForm<'_>>,
|
||||||
admin: AdminUser,
|
admin: ManageUserUser,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
if User::create(db, data.name).await {
|
if User::create(db, data.name).await {
|
||||||
Log::create(
|
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> {
|
pub fn routes() -> Vec<Route> {
|
||||||
routes![
|
routes![
|
||||||
index,
|
index,
|
||||||
@ -356,6 +461,8 @@ pub fn routes() -> Vec<Route> {
|
|||||||
resetpw,
|
resetpw,
|
||||||
update,
|
update,
|
||||||
create,
|
create,
|
||||||
|
create_scheckbuch,
|
||||||
|
schnupper_to_scheckbuch,
|
||||||
delete,
|
delete,
|
||||||
fees,
|
fees,
|
||||||
fees_paid,
|
fees_paid,
|
||||||
|
@ -39,7 +39,7 @@ async fn index(
|
|||||||
|
|
||||||
context.insert(
|
context.insert(
|
||||||
"loggedin_user",
|
"loggedin_user",
|
||||||
&UserWithDetails::from_user(admin.into(), db).await,
|
&UserWithDetails::from_user(admin.into_inner(), db).await,
|
||||||
);
|
);
|
||||||
|
|
||||||
Template::render("board/boathouse", context.into_json())
|
Template::render("board/boathouse", context.into_json())
|
||||||
|
@ -59,7 +59,7 @@ async fn index(
|
|||||||
context.insert("boats", &boats);
|
context.insert("boats", &boats);
|
||||||
context.insert(
|
context.insert(
|
||||||
"loggedin_user",
|
"loggedin_user",
|
||||||
&UserWithDetails::from_user(user.into(), db).await,
|
&UserWithDetails::from_user(user.into_inner(), db).await,
|
||||||
);
|
);
|
||||||
|
|
||||||
Template::render("boatdamages", context.into_json())
|
Template::render("boatdamages", context.into_json())
|
||||||
@ -78,7 +78,7 @@ async fn create<'r>(
|
|||||||
data: Form<FormBoatDamageToAdd<'r>>,
|
data: Form<FormBoatDamageToAdd<'r>>,
|
||||||
user: DonauLinzUser,
|
user: DonauLinzUser,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
let user: User = user.into();
|
let user: User = user.into_inner();
|
||||||
let boatdamage_to_add = BoatDamageToAdd {
|
let boatdamage_to_add = BoatDamageToAdd {
|
||||||
boat_id: data.boat_id,
|
boat_id: data.boat_id,
|
||||||
desc: data.desc,
|
desc: data.desc,
|
||||||
|
@ -75,7 +75,7 @@ async fn index(
|
|||||||
context.insert("user", &User::all(db).await);
|
context.insert("user", &User::all(db).await);
|
||||||
context.insert(
|
context.insert(
|
||||||
"loggedin_user",
|
"loggedin_user",
|
||||||
&UserWithDetails::from_user(user.into(), db).await,
|
&UserWithDetails::from_user(user.into_inner(), db).await,
|
||||||
);
|
);
|
||||||
|
|
||||||
Template::render("boatreservations", context.into_json())
|
Template::render("boatreservations", context.into_json())
|
||||||
@ -97,7 +97,7 @@ async fn create<'r>(
|
|||||||
data: Form<FormBoatReservationToAdd<'r>>,
|
data: Form<FormBoatReservationToAdd<'r>>,
|
||||||
user: DonauLinzUser,
|
user: DonauLinzUser,
|
||||||
) -> Flash<Redirect> {
|
) -> 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 boat = Boat::find_by_id(db, data.boat_id as i32).await.unwrap();
|
||||||
let boatreservation_to_add = BoatReservationToAdd {
|
let boatreservation_to_add = BoatReservationToAdd {
|
||||||
boat: &boat,
|
boat: &boat,
|
||||||
|
@ -18,13 +18,15 @@ use tera::Context;
|
|||||||
use crate::model::{
|
use crate::model::{
|
||||||
boat::Boat,
|
boat::Boat,
|
||||||
boatreservation::BoatReservation,
|
boatreservation::BoatReservation,
|
||||||
|
distance::Distance,
|
||||||
log::Log,
|
log::Log,
|
||||||
logbook::{
|
logbook::{
|
||||||
LogToAdd, LogToFinalize, Logbook, LogbookCreateError, LogbookDeleteError,
|
LogToAdd, LogToFinalize, LogToUpdate, Logbook, LogbookAdminUpdateError, LogbookCreateError,
|
||||||
LogbookUpdateError,
|
LogbookDeleteError, LogbookUpdateError,
|
||||||
},
|
},
|
||||||
logtype::LogType,
|
logtype::LogType,
|
||||||
user::{AdminUser, DonauLinzUser, User, UserWithDetails},
|
trip::Trip,
|
||||||
|
user::{AdminUser, DonauLinzUser, User, UserWithDetails, VorstandUser},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct KioskCookie(());
|
pub struct KioskCookie(());
|
||||||
@ -68,11 +70,13 @@ async fn index(
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
users.retain(|u| {
|
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 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;
|
let on_water = Logbook::on_water(db).await;
|
||||||
|
|
||||||
@ -82,6 +86,7 @@ async fn index(
|
|||||||
}
|
}
|
||||||
|
|
||||||
context.insert("boats", &boats);
|
context.insert("boats", &boats);
|
||||||
|
context.insert("planned_trips", &Trip::get_for_today(db).await);
|
||||||
context.insert(
|
context.insert(
|
||||||
"reservations",
|
"reservations",
|
||||||
&BoatReservation::all_future_with_groups(db).await,
|
&BoatReservation::all_future_with_groups(db).await,
|
||||||
@ -91,7 +96,7 @@ async fn index(
|
|||||||
context.insert("logtypes", &logtypes);
|
context.insert("logtypes", &logtypes);
|
||||||
context.insert(
|
context.insert(
|
||||||
"loggedin_user",
|
"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("on_water", &on_water);
|
||||||
context.insert("distances", &distances);
|
context.insert("distances", &distances);
|
||||||
@ -105,7 +110,7 @@ async fn show(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
|
|||||||
|
|
||||||
Template::render(
|
Template::render(
|
||||||
"log.completed",
|
"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 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;
|
let on_water = Logbook::on_water(db).await;
|
||||||
|
|
||||||
@ -185,6 +190,7 @@ async fn kiosk(
|
|||||||
context.insert("flash", &msg.into_inner());
|
context.insert("flash", &msg.into_inner());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.insert("planned_trips", &Trip::get_for_today(db).await);
|
||||||
context.insert("boats", &boats);
|
context.insert("boats", &boats);
|
||||||
context.insert(
|
context.insert(
|
||||||
"reservations",
|
"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::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::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::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;
|
.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(
|
async fn home_logbook(
|
||||||
db: &SqlitePool,
|
db: &SqlitePool,
|
||||||
data: Form<LogToFinalize>,
|
data: Form<LogToFinalize>,
|
||||||
logbook_id: i32,
|
logbook_id: i64,
|
||||||
user: &DonauLinzUser,
|
user: &DonauLinzUser,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
let logbook: Option<Logbook> = Logbook::find_by_id(db, logbook_id).await;
|
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::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::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::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(
|
Err(e) => Flash::error(
|
||||||
Redirect::to("/log"),
|
Redirect::to("/log"),
|
||||||
format!("Eintrag {logbook_id} konnte nicht abgesendet werden (Fehler: {e:?})!"),
|
format!("Eintrag {logbook_id} konnte nicht abgesendet werden (Fehler: {e:?})!"),
|
||||||
@ -312,7 +359,7 @@ async fn home_logbook(
|
|||||||
async fn home_kiosk(
|
async fn home_kiosk(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
data: Form<LogToFinalize>,
|
data: Form<LogToFinalize>,
|
||||||
logbook_id: i32,
|
logbook_id: i64,
|
||||||
_kiosk: KioskCookie,
|
_kiosk: KioskCookie,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
let logbook = Logbook::find_by_id(db, logbook_id).await.unwrap(); //TODO: fixme
|
let logbook = Logbook::find_by_id(db, logbook_id).await.unwrap(); //TODO: fixme
|
||||||
@ -327,11 +374,14 @@ async fn home_kiosk(
|
|||||||
db,
|
db,
|
||||||
data,
|
data,
|
||||||
logbook_id,
|
logbook_id,
|
||||||
&DonauLinzUser(
|
&DonauLinzUser::new(
|
||||||
|
db,
|
||||||
User::find_by_id(db, logbook.shipmaster as i32)
|
User::find_by_id(db, logbook.shipmaster as i32)
|
||||||
.await
|
.await
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
), //TODO: fixme
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -340,7 +390,7 @@ async fn home_kiosk(
|
|||||||
async fn home(
|
async fn home(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
data: Form<LogToFinalize>,
|
data: Form<LogToFinalize>,
|
||||||
logbook_id: i32,
|
logbook_id: i64,
|
||||||
user: DonauLinzUser,
|
user: DonauLinzUser,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
Log::create(
|
Log::create(
|
||||||
@ -356,9 +406,14 @@ async fn home(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/<logbook_id>/delete", rank = 2)]
|
#[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;
|
let logbook = Logbook::find_by_id(db, logbook_id).await;
|
||||||
if let Some(logbook) = logbook {
|
if let Some(logbook) = logbook {
|
||||||
|
let redirect = if logbook.arrival.is_some() {
|
||||||
|
"/log/show"
|
||||||
|
} else {
|
||||||
|
"/log"
|
||||||
|
};
|
||||||
Log::create(
|
Log::create(
|
||||||
db,
|
db,
|
||||||
format!("User {} tries to delete log entry {logbook_id}", &user.name),
|
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;
|
.await;
|
||||||
match logbook.delete(db, &user).await {
|
match logbook.delete(db, &user).await {
|
||||||
Ok(_) => Flash::success(
|
Ok(_) => Flash::success(
|
||||||
Redirect::to("/log"),
|
Redirect::to(redirect),
|
||||||
format!("Eintrag {} gelöscht!", logbook_id),
|
format!("Eintrag {} von {} gelöscht!", logbook_id, user.name),
|
||||||
),
|
),
|
||||||
Err(LogbookDeleteError::NotYourEntry) => Flash::error(
|
Err(LogbookDeleteError::NotYourEntry) => Flash::error(
|
||||||
Redirect::to("/log"),
|
Redirect::to(redirect),
|
||||||
"Du hast nicht die Berechtigung, den Eintrag zu löschen!",
|
"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")]
|
#[get("/<logbook_id>/delete")]
|
||||||
async fn delete_kiosk(
|
async fn delete_kiosk(
|
||||||
db: &State<SqlitePool>,
|
db: &State<SqlitePool>,
|
||||||
logbook_id: i32,
|
logbook_id: i64,
|
||||||
_kiosk: KioskCookie,
|
_kiosk: KioskCookie,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
let logbook = Logbook::find_by_id(db, logbook_id).await;
|
let logbook = Logbook::find_by_id(db, logbook_id).await;
|
||||||
@ -425,7 +480,8 @@ pub fn routes() -> Vec<Route> {
|
|||||||
show_kiosk,
|
show_kiosk,
|
||||||
show_for_year,
|
show_for_year,
|
||||||
delete,
|
delete,
|
||||||
delete_kiosk
|
delete_kiosk,
|
||||||
|
update
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +118,60 @@ fn unauthorized_error(req: &Request) -> Redirect {
|
|||||||
Redirect::to("/auth")
|
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
|
#[catch(403)] //forbidden
|
||||||
fn forbidden_error() -> Flash<Redirect> {
|
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.")
|
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,
|
smtp_pw: String,
|
||||||
usage_log_path: String,
|
usage_log_path: String,
|
||||||
pub openweathermap_key: String,
|
pub openweathermap_key: String,
|
||||||
|
wordpress_key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
|
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("/", routes![index, steering, impressum])
|
||||||
.mount("/auth", auth::routes())
|
.mount("/auth", auth::routes())
|
||||||
.mount("/wikiauth", routes![wikiauth])
|
.mount("/wikiauth", routes![wikiauth])
|
||||||
|
.mount("/new-blogpost", routes![new_blogpost])
|
||||||
|
.mount("/blogpost-unpublished", routes![blogpost_unpublished])
|
||||||
.mount("/log", log::routes())
|
.mount("/log", log::routes())
|
||||||
.mount("/planned", planned::routes())
|
.mount("/planned", planned::routes())
|
||||||
.mount("/ergo", ergo::routes())
|
.mount("/ergo", ergo::routes())
|
||||||
|
@ -22,7 +22,7 @@ async fn index(
|
|||||||
user: AllowedForPlannedTripsUser,
|
user: AllowedForPlannedTripsUser,
|
||||||
flash: Option<FlashMessage<'_>>,
|
flash: Option<FlashMessage<'_>>,
|
||||||
) -> Template {
|
) -> Template {
|
||||||
let user: User = user.into();
|
let user: User = user.into_inner();
|
||||||
|
|
||||||
let mut context = Context::new();
|
let mut context = Context::new();
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ async fn join(
|
|||||||
user: AllowedForPlannedTripsUser,
|
user: AllowedForPlannedTripsUser,
|
||||||
user_note: Option<String>,
|
user_note: Option<String>,
|
||||||
) -> Flash<Redirect> {
|
) -> 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 {
|
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
||||||
return Flash::error(Redirect::to("/"), "Trip_details do not exist.");
|
return Flash::error(Redirect::to("/"), "Trip_details do not exist.");
|
||||||
@ -113,7 +113,7 @@ async fn remove_guest(
|
|||||||
user: AllowedForPlannedTripsUser,
|
user: AllowedForPlannedTripsUser,
|
||||||
name: String,
|
name: String,
|
||||||
) -> Flash<Redirect> {
|
) -> 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 {
|
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
||||||
return Flash::error(Redirect::to("/planned"), "TripDetailsId does not exist");
|
return Flash::error(Redirect::to("/planned"), "TripDetailsId does not exist");
|
||||||
@ -160,7 +160,7 @@ async fn remove(
|
|||||||
trip_details_id: i64,
|
trip_details_id: i64,
|
||||||
user: AllowedForPlannedTripsUser,
|
user: AllowedForPlannedTripsUser,
|
||||||
) -> Flash<Redirect> {
|
) -> 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 {
|
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
||||||
return Flash::error(Redirect::to("/planned"), "TripDetailsId does not exist");
|
return Flash::error(Redirect::to("/planned"), "TripDetailsId does not exist");
|
||||||
|
@ -16,7 +16,7 @@ async fn index_boat(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
|
|||||||
|
|
||||||
Template::render(
|
Template::render(
|
||||||
"stat.boats",
|
"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(
|
Template::render(
|
||||||
"stat.people",
|
"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),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ async fn index(
|
|||||||
context.insert("user", &User::all(db).await);
|
context.insert("user", &User::all(db).await);
|
||||||
context.insert(
|
context.insert(
|
||||||
"loggedin_user",
|
"loggedin_user",
|
||||||
&UserWithDetails::from_user(user.into(), db).await,
|
&UserWithDetails::from_user(user.into_inner(), db).await,
|
||||||
);
|
);
|
||||||
|
|
||||||
Template::render("trailerreservations", context.into_json())
|
Template::render("trailerreservations", context.into_json())
|
||||||
@ -81,7 +81,7 @@ async fn create<'r>(
|
|||||||
data: Form<FormTrailerReservationToAdd<'r>>,
|
data: Form<FormTrailerReservationToAdd<'r>>,
|
||||||
user: DonauLinzUser,
|
user: DonauLinzUser,
|
||||||
) -> Flash<Redirect> {
|
) -> 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)
|
let trailer = Trailer::find_by_id(db, data.trailer_id as i32)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -12,7 +12,13 @@
|
|||||||
{% for user in schnupperanten %}
|
{% for user in schnupperanten %}
|
||||||
<li class="py-1"
|
<li class="py-1"
|
||||||
{% if "paid" in user.roles %}style="background-color: green;"{% endif %}>
|
{% 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>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ol>
|
</ol>
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
{% if not loop.last %}+{% endif %}
|
{% if not loop.last %}+{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</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>
|
<a href="/admin/user/fees/paid?{{ fee.user_ids }}">Zahlungsstatus ändern</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,37 +36,53 @@
|
|||||||
placeholder="Suchen nach (Name, [yes|no]-role:<name>, has-[no-]membership-pdf)" />
|
placeholder="Suchen nach (Name, [yes|no]-role:<name>, has-[no-]membership-pdf)" />
|
||||||
</div>
|
</div>
|
||||||
<!-- END filterBar -->
|
<!-- 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="search-result"></div>
|
||||||
<div id="filter-result-js"
|
|
||||||
class="text-primary-950 dark:text-white text-right"></div>
|
|
||||||
{% for user in users %}
|
{% for user in users %}
|
||||||
<div data-filterable="true"
|
<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 %} ">
|
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"
|
<form action="/admin/user"
|
||||||
method="post"
|
method="post"
|
||||||
enctype="multipart/form-data"
|
enctype="multipart/form-data"
|
||||||
class="bg-white dark:bg-primary-900 p-3 rounded-md w-full">
|
class="inline">
|
||||||
<div class="w-full grid gap-3">
|
• <a class="font-normal text-primary-600 dark:text-primary-200 hover:text-primary-900 dark:hover:text-primary-300 underline"
|
||||||
<input type="hidden" name="id" value="{{ user.id }}" />
|
href="/admin/user/{{ user.id }}/send-welcome-mail"
|
||||||
<div class="font-bold mb-1 text-black dark:text-white">
|
onclick="return confirm('Willst du wirklich das Willkommensmail an {{ user.name }} ausschicken?');">Willkommensmail verschicken</a>
|
||||||
{{ user.name }}
|
</form>
|
||||||
{% if user.last_access %}
|
|
||||||
(last access:
|
|
||||||
{{ user.last_access | date }})
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if user.last_access %}• ⏳ {{ 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="w-full mt-2">
|
||||||
{% if user.pw %}
|
{% 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"
|
<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">Passwort zurücksetzen</a>
|
href="/admin/user/{{ user.id }}/reset-pw"
|
||||||
|
onclick="return confirm('Willst du wirklich das Passwort zurücksetzen?');">Passwort zurücksetzen</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not user.last_access and "admin" in loggedin_user.roles %}
|
<div class="w-full grid gap-3 mt-3">
|
||||||
<a class="block mt-1 font-normal text-primary-600 dark:text-primary-200 hover:text-primary-900 dark:hover:text-primary-300 underline"
|
<input type="hidden" name="id" value="{{ user.id }}" />
|
||||||
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">
|
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-3">
|
||||||
{% for role in roles %}
|
{% 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) }}
|
{{ macros::checkbox(label=role.name, name="roles[" ~ role.id ~ "]", id=loop.index , checked=role.name in user.roles, disabled=allowed_to_edit == false) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
<hr class="sm:col-span-2 lg:col-span-4 my-3" />
|
||||||
{% if user.membership_pdf %}
|
{% if user.membership_pdf %}
|
||||||
<a href="/admin/user/{{ user.id }}/membership"
|
<a href="/admin/user/{{ user.id }}/membership"
|
||||||
class="text-black dark:text-white">Beitrittserklärung herunterladen</a>
|
class="text-black dark:text-white">Beitrittserklärung herunterladen</a>
|
||||||
@ -100,8 +116,8 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
|
</details>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
@ -4,6 +4,34 @@
|
|||||||
{% block content %}
|
{% 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">
|
<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>
|
<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 -->
|
<!-- START filterBar -->
|
||||||
<div class="search-wrapper">
|
<div class="search-wrapper">
|
||||||
<label for="name" class="sr-only">Suche</label>
|
<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>
|
<li>{{ log::show_old(log=trip, state="completed", only_ones=false, index=loop.index) }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</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>
|
<a href="/admin/user/fees/paid?user_ids[]={{ user.id }}">Zahlungsstatus ändern</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
Folgende Daten werden verarbeitet:
|
Folgende Daten werden verarbeitet:
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<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>
|
||||||
<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.
|
Cookie: Diese Website verwendet nur einen Cookie (loggedin_user), der verschlüsselte Informationen über den Login-Status speichert. Weitere Cookies werden nicht verwendet.
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
<div class="col-span-4 md:col-span-1">
|
<div class="col-span-4 md:col-span-1">
|
||||||
<div class="text-sm text-gray-600 dark:text-gray-100">Bootssteuerung</div>
|
<div class="text-sm text-gray-600 dark:text-gray-100">Bootssteuerung</div>
|
||||||
<div class="h-10 flex items-center">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
{{ log::rower_select(id="newrower", selected=[], class="col-span-4", init=true) }}
|
{{ log::rower_select(id="newrower", selected=[], class="col-span-4", init=true) }}
|
||||||
@ -53,9 +53,13 @@
|
|||||||
id="destination"
|
id="destination"
|
||||||
name="destination"
|
name="destination"
|
||||||
value=""
|
value=""
|
||||||
data-relation="distance_in_km" />
|
data-relation="distance_in_km"
|
||||||
|
autocomplete="off" />
|
||||||
<datalist id="destinations">
|
<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>
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative col-span-2">
|
<div class="relative col-span-2">
|
||||||
@ -169,10 +173,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endmacro show %}
|
{% 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"
|
<div class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative"
|
||||||
data-filterable="true"
|
data-filterable="true"
|
||||||
data-filter="{{ log.boat.name }} {% for rower in log.rowers %}{{ rower.name }}{% endfor %}">
|
data-filter="{{ log.boat.name }} {% for rower in log.rowers %}{{ rower.name }}{% endfor %}">
|
||||||
|
<details>
|
||||||
|
<summary style="list-style: none;">
|
||||||
{% if log.logtype %}
|
{% 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">
|
<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 %}
|
{% if log.logtype == 1 %}
|
||||||
@ -240,12 +246,41 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</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>
|
</div>
|
||||||
{% endmacro show_old %}
|
{% endmacro show_old %}
|
||||||
{% macro home(log) %}
|
{% macro home(log) %}
|
||||||
<form class="grid grid-cols-1 gap-3"
|
<form class="grid grid-cols-1 gap-3"
|
||||||
action="/log/{{ log.id }}"
|
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") }}
|
{{ macros::input(label='Ankunftszeit', name='arrival', type='datetime-local', required=true, class="change-id-js rounded-md current-date-time") }}
|
||||||
<div>
|
<div>
|
||||||
<label for="destination" class="text-sm text-gray-600 dark:text-gray-100">Ziel</label>
|
<label for="destination" class="text-sm text-gray-600 dark:text-gray-100">Ziel</label>
|
||||||
|
@ -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() %}
|
{% macro boatreservation() %}
|
||||||
<div class="bg-white dark:bg-primary-900 rounded-md shadow pb-2 mt-3">
|
<div class="bg-white dark:bg-primary-900 rounded-md shadow pb-2 mt-3">
|
||||||
<h2 class="h2">Reservierungen ({{ reservations | length }})</h2>
|
<h2 class="h2">Reservierungen ({{ reservations | length }})</h2>
|
||||||
@ -137,7 +176,7 @@
|
|||||||
{% if readonly %}readonly{% endif %}>
|
{% if readonly %}readonly{% endif %}>
|
||||||
</div>
|
</div>
|
||||||
{% endmacro input %}
|
{% 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 }}"
|
<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 }}">
|
class="flex items-center cursor-pointer text-black dark:text-white hover:text-gray-900 dark:hover:text-gray-100 {{ class }}">
|
||||||
<input type="checkbox"
|
<input type="checkbox"
|
||||||
@ -145,6 +184,7 @@
|
|||||||
name="{{ name }}"
|
name="{{ name }}"
|
||||||
{% if checked %}checked{% endif %}
|
{% if checked %}checked{% endif %}
|
||||||
{% if disabled %}disabled{% endif %}
|
{% if disabled %}disabled{% endif %}
|
||||||
|
{% if readonly %}readonly="readonly"{% endif %}
|
||||||
class="h-4 w-4 accent-primary-600 dark:accent-primary-200 mr-2" />
|
class="h-4 w-4 accent-primary-600 dark:accent-primary-200 mr-2" />
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</label>
|
</label>
|
||||||
|
@ -33,6 +33,11 @@
|
|||||||
<div class="mt-1">{{ notification.message | safe }}</div>
|
<div class="mt-1">{{ notification.message | safe }}</div>
|
||||||
</div>
|
</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 %}
|
{% if not notification.read_at %}
|
||||||
<a href="/notification/{{ notification.id }}/read" class="inline-block">
|
<a href="/notification/{{ notification.id }}/read" class="inline-block">
|
||||||
<button class="btn btn-primary" type="button">
|
<button class="btn btn-primary" type="button">
|
||||||
@ -56,6 +61,11 @@
|
|||||||
<strong>{{ notification.category }}</strong> • {{ notification.created_at | date(format="%d.%m.%Y %H:%M") }}
|
<strong>{{ notification.category }}</strong> • {{ notification.created_at | date(format="%d.%m.%Y %H:%M") }}
|
||||||
</small>
|
</small>
|
||||||
<div class="mt-1">{{ notification.message | safe }}</div>
|
<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>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -155,6 +165,13 @@
|
|||||||
<a href="/board/boathouse"
|
<a href="/board/boathouse"
|
||||||
class="block w-100 py-2 hover:text-primary-600">Bootshaus</a>
|
class="block w-100 py-2 hover:text-primary-600">Bootshaus</a>
|
||||||
</li>
|
</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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -169,19 +186,12 @@
|
|||||||
<li class="py-1">
|
<li class="py-1">
|
||||||
<a href="/admin/user" class="block w-100 py-2 hover:text-primary-600">User</a>
|
<a href="/admin/user" class="block w-100 py-2 hover:text-primary-600">User</a>
|
||||||
</li>
|
</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">
|
<li class="py-1">
|
||||||
<a href="/admin/rss" class="block w-100 py-2 hover:text-primary-600">Logs</a>
|
<a href="/admin/rss" class="block w-100 py-2 hover:text-primary-600">Logs</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="py-1">
|
<li class="py-1">
|
||||||
<a href="/admin/list" class="block w-100 py-2 hover:text-primary-600">Fingerabdruck-Liste überprüfen</a>
|
<a href="/admin/list" class="block w-100 py-2 hover:text-primary-600">Fingerabdruck-Liste überprüfen</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="py-1">
|
|
||||||
<a href="/admin/notification"
|
|
||||||
class="block w-100 py-2 hover:text-primary-600">Nachricht ausschreiben</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -53,6 +53,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ macros::boatreservation() }}
|
{{ macros::boatreservation() }}
|
||||||
|
{{ macros::plannedtrips() }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,7 +23,15 @@
|
|||||||
placeholder="Suchen nach Bootsname oder Ruderer...">
|
placeholder="Suchen nach Bootsname oder Ruderer...">
|
||||||
</div>
|
</div>
|
||||||
<div id="filter-result-js" class="search-result"></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>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{{ macros::boatreservation() }}
|
{{ macros::boatreservation() }}
|
||||||
|
{{ macros::plannedtrips() }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var sepaqr = new sepaQR({
|
var sepaqr = new sepaQR({
|
||||||
benefName: 'ASKÖ Ruderverein Donau Linz',
|
benefName: 'ASKÖ Ruderverein Donau Linz',
|
||||||
benefBIC: 'BKAUATWWXXX',
|
benefBIC: 'ASPKAT2LXXX',
|
||||||
benefAccNr: 'AT131200080413001200',
|
benefAccNr: 'AT582032032100729256',
|
||||||
amountEuro: {{ fee.sum_in_cents/100 }},
|
amountEuro: {{ fee.sum_in_cents/100 }},
|
||||||
remittanceInf: 'Vereinsgebühren {{ fee.name }}',
|
remittanceInf: 'Vereinsgebühren {{ fee.name }}',
|
||||||
});
|
});
|
||||||
@ -44,13 +44,13 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</small>
|
</small>
|
||||||
{% endif %}
|
{% 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 />
|
<br />
|
||||||
Falls die Berechnung nicht stimmt (korrekte Preise findest du <a href="https://rudernlinz.at/unser-verein/gebuhren/"
|
Falls die Berechnung nicht stimmt (korrekte Preise findest du <a href="https://rudernlinz.at/unser-verein/gebuhren/"
|
||||||
target="_blank"
|
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.
|
rel="noopener noreferrer">hier</a>) melde dich bitte bei it@rudernlinz.at. @Studenten: Bitte die aktuelle Studienbestätigung an it@rudernlinz.at schicken.
|
||||||
<br />
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -67,7 +67,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% 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"
|
style="min-height: 10rem"
|
||||||
data-trips="{{ amount_trips }}"
|
data-trips="{{ amount_trips }}"
|
||||||
data-month="{{ day.day| date(format='%m') }}"
|
data-month="{{ day.day| date(format='%m') }}"
|
||||||
@ -391,15 +391,15 @@
|
|||||||
</div>
|
</div>
|
||||||
{# --- START Add Buttons --- #}
|
{# --- START Add Buttons --- #}
|
||||||
{% if "manage_events" in loggedin_user.roles or "cox" in loggedin_user.roles %}
|
{% 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 %}
|
{% if "manage_events" in loggedin_user.roles %}
|
||||||
<a href="#"
|
<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
|
||||||
data-sidebar="true"
|
{% if "cox" in loggedin_user.roles %}
|
||||||
data-trigger="sidebar"
|
rounded-bl-md
|
||||||
data-header="<strong>Event</strong> am {{ day.day| date(format='%d.%m.%Y') }} erstellen"
|
{% else %}
|
||||||
data-day="{{ day.day }}"
|
rounded-b-md
|
||||||
data-body="#addEventForm"
|
{% endif %}
|
||||||
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">
|
">
|
||||||
<span class="absolute inset-y-0 left-0 flex items-center pl-3">{% include "includes/plus-icon" %}</span>
|
<span class="absolute inset-y-0 left-0 flex items-center pl-3">{% include "includes/plus-icon" %}</span>
|
||||||
Event
|
Event
|
||||||
</a>
|
</a>
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
data-filter="{{ reservation.user_applicant.name }} {{ reservation.trailer.name }}"
|
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">
|
class="w-full border-t bg-white dark:bg-primary-900 text-black dark:text-white p-3">
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<strong>Boot:</strong>
|
<strong>Hänger:</strong>
|
||||||
{{ reservation.trailer.name }}
|
{{ reservation.trailer.name }}
|
||||||
<br />
|
<br />
|
||||||
<strong>Reservierung:</strong>
|
<strong>Reservierung:</strong>
|
||||||
|
156
wordpress-notes.md
Normal file
156
wordpress-notes.md
Normal 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);
|
||||||
|
```
|
Reference in New Issue
Block a user