Compare commits
668 Commits
fradrive/a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b0029ba04 | |||
| e554048f5a | |||
| e59fff352f | |||
| e9d4174b83 | |||
| 90613faf72 | |||
| 6a070a6775 | |||
| ea113cf57a | |||
| 6ffc49ae0e | |||
| ab8b17229a | |||
| 74f7633837 | |||
| d92d23bc99 | |||
| 4959736c90 | |||
| ade27e6479 | |||
| cbadef0a73 | |||
| 2a27a1efa6 | |||
| 620e3e4700 | |||
| f0798e8836 | |||
| 3c5edb1b97 | |||
| 4f7855b9ee | |||
| 547f34d2ec | |||
| 08788427a8 | |||
| 1e896da4a3 | |||
| 7e5c256b4c | |||
| 43319fbcca | |||
| f946e99da3 | |||
| cfe2318f81 | |||
| 64ff002ffb | |||
| 8397c468a0 | |||
| 81721b0794 | |||
| 40dadd5876 | |||
| b7e5b8f111 | |||
| 8ec2875590 | |||
| 6d1b177ce9 | |||
| 9c82558d71 | |||
| e8f9c21b7c | |||
| e1a02879d6 | |||
| 109e845db6 | |||
| 53abdb7cc3 | |||
| 97446aa9ef | |||
| 407ba543a1 | |||
| f61c35cfe7 | |||
| b0972bb154 | |||
| 8bc3663ee2 | |||
| 776e6b6736 | |||
| be5e609b1f | |||
| cc5da9a2a9 | |||
| e551fadd29 | |||
| 2ed626ea4a | |||
| f4823aaf28 | |||
| 760b102d52 | |||
| 000d8100db | |||
| d209a110e8 | |||
| 0af8598d6d | |||
| c3d27c25b5 | |||
| 1e6547e903 | |||
| e4abf915ee | |||
| 6299612adc | |||
| 8f54ea1051 | |||
| c1dbd61c14 | |||
| e35a5e99a6 | |||
| ab00a4f665 | |||
| f929e03129 | |||
| 21d32fd4cf | |||
| 4df8bd2fa5 | |||
| d1fa01fcc5 | |||
| ec02767552 | |||
| cfd25348ad | |||
| e1419766f3 | |||
| 5b6e4e60e7 | |||
| bc47387c91 | |||
| 0fde59c19a | |||
| 507a7e02fc | |||
| 43f5c5f485 | |||
| b9f70c7796 | |||
| 6ccbb3b7ff | |||
| 8b0466e74e | |||
| 689e6347da | |||
| 11fdcf0d44 | |||
| 58152beb03 | |||
| 803e8bfedb | |||
| d853e8559b | |||
| e6f0454e78 | |||
| 8c8ffa5183 | |||
| fee14edf36 | |||
| 0bbb679a43 | |||
| 6063eb24a2 | |||
| 28e2739e51 | |||
| c17c18f924 | |||
| d65fb2f4cd | |||
| ab28c8c243 | |||
| 6e2d545772 | |||
| fa0541aa4e | |||
| b5215cc7e8 | |||
| a1668f891a | |||
| c813c665ed | |||
| 9a0e8988fa | |||
| 9d3198f49b | |||
| 2caa5aec5b | |||
| 3def8ca916 | |||
| a97c3a5c9d | |||
| 468af9de9d | |||
| 91e21db758 | |||
| 6ea3a30afc | |||
| 3a66bed173 | |||
| f869a829d2 | |||
| b9b1d3e57b | |||
| 93196a6400 | |||
| feb8d92bc1 | |||
| 073432c75b | |||
| 0725a9a908 | |||
| 0ac75e0d59 | |||
| 62d698503d | |||
| 9e2a964ef7 | |||
| 357e943f21 | |||
| 5bf85394d4 | |||
| 99f03078a1 | |||
| 7ca3237ad0 | |||
| 9e2f2214ce | |||
| ff9014ce05 | |||
| 622c01b9be | |||
| 6d49ea092b | |||
| d4f3ce7bf3 | |||
| 8b03409554 | |||
| 45bc5ca9f5 | |||
| 3dfc7f8c8b | |||
| e25a8569c5 | |||
| 37efc89e07 | |||
| 975bf13d9c | |||
| 2559346d96 | |||
| 5f1af130ed | |||
| d4a0e1f201 | |||
| 47e56280fc | |||
| f108c6cfec | |||
| e4fa1ddd68 | |||
| f8c36636ff | |||
| 0a93f79f4e | |||
| b3d1dabfc2 | |||
| c212f2e8d7 | |||
| 2cc529be39 | |||
| f425bd9afe | |||
| d161c296ad | |||
| b7ed7338d7 | |||
| 07663516e5 | |||
| 18cdc52df0 | |||
| c04614ff86 | |||
| 766b8589d6 | |||
| f37c08099c | |||
| d7acc7a2d0 | |||
| 7ad7fe609c | |||
| 822c43c8a7 | |||
| 8721bdb3f3 | |||
| 73aecc2df8 | |||
| c38e87e1e0 | |||
| dfe4352575 | |||
| a2a89a8aad | |||
| 73ea2f54df | |||
| 34199a37fd | |||
| 554c1eec27 | |||
| e5cbd096ce | |||
| 2ae11dc25c | |||
| d61788a1f5 | |||
| ab5e432b77 | |||
| b5f5fb784c | |||
| d83cb66c8b | |||
| a6d0105903 | |||
| cf019e6daa | |||
| 0eac40457b | |||
| d1306303cf | |||
| ad8e67dab1 | |||
| 76e0710c7b | |||
| a3beca87d1 | |||
| 996e6a0ce5 | |||
| da74b95729 | |||
| f5754cd6b1 | |||
| 64b21d6fe6 | |||
| 9fd80f2552 | |||
| ac3271242d | |||
| 7e022ca0a1 | |||
| ab2e81f34d | |||
| e6c57035f9 | |||
| bb101dee7b | |||
| e553ad4358 | |||
| 5b9d757ca4 | |||
| aa1d230e49 | |||
| 6acfd849ae | |||
| 5a9ed747d2 | |||
| 396312092a | |||
| ea0fa9a3fa | |||
| 3fb2226204 | |||
| b77e9e1d1c | |||
| 9814712c61 | |||
| 400d0a546e | |||
| 9451d90a9e | |||
| a732e26337 | |||
| f47134c2f0 | |||
| ff2347b1c9 | |||
| ccf9340449 | |||
| e5750ea7a0 | |||
| 7fd13677d3 | |||
| 32a79ee2c9 | |||
| 6750798920 | |||
| 3c4a0b86c1 | |||
| 29182cb6dd | |||
| 6084f92ad7 | |||
| e2e5cc7bee | |||
| 2fbd28154c | |||
| 21273e361a | |||
| 5944efcb86 | |||
| fdbaa3c9d4 | |||
| 30807af3c4 | |||
| f465cc9723 | |||
| b8d41d10c9 | |||
| 697979c277 | |||
| a5dfd5e10f | |||
| 13a648de18 | |||
| 6fd45f6896 | |||
| fea749f367 | |||
| 64a123387f | |||
| 2e4e1a94c9 | |||
| a52c8a6ad7 | |||
| fd6a5384d3 | |||
| 4f8850b3b4 | |||
| b7af6312f9 | |||
| 234dd28f48 | |||
| 890f8ad8b6 | |||
| d56a1cdd46 | |||
| cb2778e206 | |||
| a373abad26 | |||
| 3b7762f451 | |||
| 54c08cc64b | |||
| 1f7c175a58 | |||
| 4c29150371 | |||
| d213c8e4a1 | |||
| 7a5917131c | |||
| 1c5ca24dc5 | |||
| 4a51f94a8f | |||
| b51f8a454a | |||
| d625fbe8e3 | |||
| 08d2f8c2fc | |||
| 66eaa4f7dc | |||
| 724e4a0bec | |||
| dcb947b1fb | |||
| 09d10e1ba2 | |||
| 17a3541fe2 | |||
| 9985151002 | |||
| c179c03f9d | |||
| 0b7175c26c | |||
| d4f8a6c77b | |||
| c382be9325 | |||
| d578e80282 | |||
| 57a4aeb475 | |||
| caf8e8b71e | |||
| 66ef4066b3 | |||
| ae9be9e285 | |||
| b39f69df12 | |||
| ad2375b338 | |||
| ef36e22f76 | |||
| 99adff80cd | |||
| ce4869f155 | |||
| 64797536e3 | |||
| d4f7dce716 | |||
| 192c733749 | |||
| 42695cf5ef | |||
| e2be8bbd5c | |||
|
|
57f5cac75a | ||
| 99c3383581 | |||
| 482dbe5c4e | |||
| 263894b058 | |||
| 3303c4eebf | |||
| 618c78a69d | |||
| 67552a666e | |||
| 2a0bca1230 | |||
| 4a843fe30e | |||
| 42f1a802b5 | |||
| 1464a9a582 | |||
| ce3852e3d3 | |||
| c71814d1ef | |||
| bbb9f9fadb | |||
| 6d44f36e2a | |||
| 47f853bd4a | |||
| a592ad7094 | |||
| 798a07e36c | |||
| fd388b91f4 | |||
| d1fce58ec2 | |||
| 4154b1f26b | |||
| f5d57d9e5e | |||
| a06f345391 | |||
| 97471884f0 | |||
| 28837c41ab | |||
| 9581e5513e | |||
| d332c0c11a | |||
| f439ea45af | |||
| de45731a9b | |||
| f40448cd31 | |||
| 9bf38d8198 | |||
| e8d66a4734 | |||
| 45c3f11a83 | |||
| cb807fce98 | |||
| b5340a18a2 | |||
| 83afdf760f | |||
| 61aba7e515 | |||
| b566e59eb1 | |||
| 2356bf80a5 | |||
| decc5af682 | |||
| c7b5a3c6cb | |||
| 88f24fe6f1 | |||
| dd5d283f88 | |||
| 1d48b627f6 | |||
| a4b2af7f15 | |||
| 2509358878 | |||
| f36f234c42 | |||
| ce45d26a21 | |||
| db77850c4f | |||
| d4f0d69428 | |||
| 4dbf226e02 | |||
| b73557a1ee | |||
| 8b93b6a665 | |||
| 5ea0289eb7 | |||
| 30fae33ded | |||
| 4c39670866 | |||
| 5c8a571c76 | |||
| fb20defc42 | |||
| 48ef25aa8f | |||
| 0b9a1257db | |||
| c334fa4bf3 | |||
| 3aa89019a8 | |||
| fc0ca7b854 | |||
| 9878956716 | |||
| 3acb847915 | |||
| a15862ea72 | |||
| 527a270cbf | |||
| 50eda5f65f | |||
| fcc802753a | |||
| df6a7ee1e2 | |||
| 6aa06292b8 | |||
| 1d3345cbba | |||
| 34c0928718 | |||
| b1ce55597e | |||
| 75e4975c52 | |||
| ef9a5dc5a9 | |||
| 929eb1b175 | |||
| 57d9447b4f | |||
| eb541b4e91 | |||
| 92aca1b830 | |||
| 0a06efd76c | |||
| 640a2e61d1 | |||
| 17bde4de09 | |||
| a166ac181f | |||
| 06bb44cf71 | |||
| fcceef265d | |||
| 212cb71807 | |||
| 2636c9d41a | |||
| 092a4c78d5 | |||
| fb41caceff | |||
| 0b00fffd27 | |||
| 076dff2a60 | |||
| e645517d32 | |||
| 577a2fb45d | |||
| b10cbc39cc | |||
| 8973ea5849 | |||
| dc6079ec3b | |||
| 400a3449c5 | |||
| c5c4a62de0 | |||
| 7fc6e43131 | |||
| 4ae59fc1fa | |||
| cf5759bc60 | |||
| 60fc5f8b63 | |||
| 5163ed06c6 | |||
| 83bab6b86b | |||
| b9f2d3bda4 | |||
| b7d6474ace | |||
| 0f9a7a8c53 | |||
| 8f8b6d84ae | |||
| 975c9c6c00 | |||
| 4fa7385154 | |||
| 44c4b3b6a8 | |||
| 715b751363 | |||
| 612d975384 | |||
| 8c4f848675 | |||
| 6761767c6c | |||
| ecde6b0fac | |||
| 698a9c5497 | |||
| 65cdc8ddfe | |||
| 42ff02d27e | |||
| 25c4ba7136 | |||
| a6fb00f072 | |||
| 71c290996d | |||
| 674f6fd81f | |||
| 63e6d94df2 | |||
| 5d8802732a | |||
| a360101d44 | |||
| f627de503e | |||
| 9ee80f8f7f | |||
| 390ff317ea | |||
| a24e44efc9 | |||
| a98c3190e0 | |||
| 8500e72dee | |||
| 631d157688 | |||
| 3865bda64d | |||
| 2d37315d18 | |||
| 5936435c54 | |||
| 8165892b2e | |||
| 069561763c | |||
| 2c12477c57 | |||
| 53f54189f9 | |||
| d2b20674f5 | |||
| 67da3c99f2 | |||
| 2aa14ee2e1 | |||
| 5f7b2aac26 | |||
| 7373bc9147 | |||
| d7a94b9619 | |||
| a42e8a88f0 | |||
| ce7597238d | |||
| bb7b7cf3dc | |||
| ef0d71e19e | |||
| 13ee3e7315 | |||
| 647964fc35 | |||
| 90703f4921 | |||
| ff176faa12 | |||
| 230ca0c40f | |||
| 0ab1cd17be | |||
| 0f3bf98235 | |||
| aae1926840 | |||
| 954a23936a | |||
| a29d8f3698 | |||
| 47166094e7 | |||
| 06f4024426 | |||
| 4abf6aa221 | |||
| a28786412e | |||
| 9ca9c38830 | |||
| 315eedd1bc | |||
| dfa03f8ba8 | |||
| 19eea7abe8 | |||
| ebecbf5c7f | |||
| 18b9df974a | |||
| 29bffb6a47 | |||
| a9d56c51dc | |||
| 603f04f026 | |||
| 6d221fa3c2 | |||
| 601ce7abdf | |||
| 4cdf39a1fd | |||
| cd9b542265 | |||
| 5d8d8cf17e | |||
| 47987a7e09 | |||
| 09c516c0b1 | |||
| 19b3570dda | |||
| c011d887ce | |||
| 92e83475a9 | |||
| 41cb7d2abc | |||
| fde97b048a | |||
| ebf250bd8c | |||
| d81e6e15dc | |||
| db4b1d8730 | |||
| 3924d14abd | |||
| f4adfdf872 | |||
| cbb44f106a | |||
| b4a88abcf8 | |||
| 22f651ee44 | |||
| a57cdde450 | |||
| 11861c4d01 | |||
| 1e81ff5ec5 | |||
| cfec7874e6 | |||
| 855aee7f78 | |||
| e831a76c27 | |||
| f888da3ab0 | |||
| fa4f9b2447 | |||
| aca6cd5f4e | |||
| 87f0b2edab | |||
| 16d0fdd1c8 | |||
| 8fcfc9586e | |||
| bc0b449689 | |||
| 9caf2af540 | |||
| be527ada32 | |||
| e3b6a7e4c6 | |||
| f776aaaef5 | |||
| c75d914dc3 | |||
| 55ea2c26f4 | |||
| 6355f81f02 | |||
| 95857b4624 | |||
| dd67429139 | |||
| b7d4f6913d | |||
| 60644528fc | |||
| 9ac275c9eb | |||
| 8b0218ba89 | |||
| 382fa7fc07 | |||
| cdb23115ef | |||
| ae4470333e | |||
| f48862efbc | |||
| 095fde54b7 | |||
| 330e89bb6b | |||
| a84812640f | |||
| bb708ca540 | |||
| f7b2f35421 | |||
| 81b821c88a | |||
| 50e4f297b8 | |||
| 8896885bd9 | |||
| 9bc9a0f1b9 | |||
| cb9e09d071 | |||
| 9bb2586c1e | |||
| 61d414e3d5 | |||
| e59a2fc373 | |||
| 54531a6da9 | |||
| db06f5fe42 | |||
| a320ed498a | |||
| 273cc288d4 | |||
| e17b26a363 | |||
| a72142352e | |||
| 11752dc5ac | |||
| 29e5ed25df | |||
| 88e6e8f329 | |||
| dfa07a95eb | |||
| 520e649fc8 | |||
| 967341fa1e | |||
| 2bdb85faa1 | |||
| 726b05bbe1 | |||
| 16b6b33c37 | |||
| ecd1a0fc21 | |||
| b85c8bd74f | |||
| 1f677ba56b | |||
| a39dfd58d8 | |||
| dfa96c7f3e | |||
| b591e22880 | |||
| 3ef94a390c | |||
| 391c87be9f | |||
| 8b0737e2aa | |||
| 93052f5e74 | |||
| 6039d1d4b7 | |||
| ac77aa176a | |||
| 6df9e456a6 | |||
| c4a0eb647a | |||
| 82c3243caf | |||
| 1286dc7e78 | |||
| b68eff63ca | |||
| 7f5687b43d | |||
| 30e5694712 | |||
| bf53c639e7 | |||
| 52bb1db947 | |||
| 7e756985ce | |||
| 875d79bf01 | |||
| 3c5cffb409 | |||
| 0fd436a3cb | |||
| c8e1de6a86 | |||
| a7ed659866 | |||
| 0b19705e80 | |||
| 4754ad4b80 | |||
| 1fd24f608d | |||
| 6052af4d90 | |||
| f7ad290053 | |||
| 9a63bebe1b | |||
| dc9e910e26 | |||
| a74c3d80ca | |||
| 7298b00628 | |||
| a452b032c4 | |||
| 172b5086dc | |||
| da26901f8b | |||
| 1b668c7b28 | |||
| 69d689fe90 | |||
| d9a6eab833 | |||
| 2909deb4f6 | |||
| c6f2b21927 | |||
| 34ada53de0 | |||
| e8c9c2199e | |||
| 3085b8d91d | |||
| 12f4bcfa1b | |||
| 9c156f1b58 | |||
| 8ecdaca4d6 | |||
| 9131d99fa3 | |||
| 3014b68992 | |||
| 5397c7be35 | |||
| 6cec571341 | |||
| 88bf21c9c5 | |||
| 35096ace01 | |||
| 9cf7f3965a | |||
| b4ba0a30dc | |||
| 8d64ca9842 | |||
| 20b3a39bc3 | |||
| b84577984a | |||
| 00d8d684f4 | |||
| 8bcdbd95f0 | |||
| a25acfac05 | |||
| 5bb49cd889 | |||
| 21ab898607 | |||
| a06ec1b298 | |||
| 26ce2b83e2 | |||
| 9422892f72 | |||
| 15ab17aeca | |||
| f72e020dfe | |||
| aa9c1ad5e2 | |||
| a825cc814e | |||
| 9651104d0f | |||
| b57297ce1b | |||
| 9818cc6186 | |||
| 82db2e5bd4 | |||
| 747bd50cfb | |||
| eb6d868a11 | |||
| 3a7b55ac8e | |||
| 0bfe34b61e | |||
| 993be351e2 | |||
| f60d8dddc7 | |||
| 53b2d37478 | |||
| bca7afe92d | |||
| b53166f75e | |||
| 94865d1581 | |||
| d1738c2963 | |||
| d0e45ff2f1 | |||
| 1e74770ea2 | |||
| c8a3625e31 | |||
| ccfe440530 | |||
| dc14600478 | |||
| 1254e95fce | |||
| 904f07a0b6 | |||
| fbf58f5aec | |||
| 0c1e1d934f | |||
| 9b9e892612 | |||
| 16a1c1effe | |||
| 289ce8acf8 | |||
| 3646aa8fa5 | |||
| 6903bc82f8 | |||
| 0567f438f6 | |||
| bfe9bfda0a | |||
| 5249abbe20 | |||
| f63f38bdaa | |||
| 9b77bfcdc0 | |||
| 5b9a554545 | |||
| 2fc7ac610b | |||
| 681f16be93 | |||
| a407094253 | |||
| 240c6f81f8 | |||
| 256740e15e | |||
| 7ed891a120 | |||
| 1ce8f75c2d | |||
| 80c632df1c | |||
| 57842a53e7 | |||
| 2e59d3c2ea | |||
| a8df40d9f8 | |||
| 1b224630eb | |||
| c596491e49 | |||
| 9b93c00301 | |||
| ee4e67fbda | |||
| 0beb0e4011 | |||
| c9efbd56f1 | |||
| bf8cd4fa89 | |||
| 2d62acea5e | |||
| 3d595271d9 | |||
| 05b807a49a | |||
| f9930f2a00 | |||
| 6683d5e4e8 | |||
| ab48e40ac7 | |||
| ee5439cd0b | |||
| a1dd70b143 | |||
| 46f3a3324c | |||
| 23bc9033e7 | |||
| 6dc3d8d059 | |||
| aaa2d679fd | |||
| 3e5f271cac | |||
| 8a6af742d5 | |||
| 9abf8b69bf | |||
| 878f98604c | |||
| a28fb72021 | |||
| ff7675542a | |||
| bbaa42eefa | |||
| d5c345ef69 | |||
| f22252ecc3 | |||
| 43dbe18110 | |||
| a0295c7654 | |||
| 64ea50ebf6 | |||
| e29bce7b22 | |||
| d377d717d2 |
137
.gitlab-ci.yml
137
.gitlab-ci.yml
@ -2,11 +2,12 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
workflow:
|
# workflow:
|
||||||
rules:
|
# rules:
|
||||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
# - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||||
- if: $CI_MERGE_REQUEST_ID
|
# - if: $CI_MERGE_REQUEST_ID
|
||||||
- if: $CI_COMMIT_TAG =~ /^v/
|
# - if: $CI_COMMIT_TAG =~ /^v/
|
||||||
|
# - if: $CI_COMMIT_TAG =~ /^t/
|
||||||
|
|
||||||
default:
|
default:
|
||||||
image:
|
image:
|
||||||
@ -33,8 +34,7 @@ stages:
|
|||||||
node dependencies:
|
node dependencies:
|
||||||
stage: frontend:build
|
stage: frontend:build
|
||||||
script:
|
script:
|
||||||
- source .gitlab-ci/construct-flake-url.sh
|
- nix -L build -o result ".#uniworxNodeDependencies"
|
||||||
- nix -L build -o result "${FLAKE}#uniworxNodeDependencies"
|
|
||||||
- nix-store --export $(nix-store -qR result) | xz -T0 -2 > node-dependencies.nar.xz
|
- nix-store --export $(nix-store -qR result) | xz -T0 -2 > node-dependencies.nar.xz
|
||||||
before_script: &nix-before
|
before_script: &nix-before
|
||||||
- git config --global init.defaultBranch master
|
- git config --global init.defaultBranch master
|
||||||
@ -55,8 +55,7 @@ well known:
|
|||||||
stage: frontend:build
|
stage: frontend:build
|
||||||
script:
|
script:
|
||||||
- xzcat node-dependencies.nar.xz | nix-store --import
|
- xzcat node-dependencies.nar.xz | nix-store --import
|
||||||
- source .gitlab-ci/construct-flake-url.sh
|
- nix -L build -o result ".#uniworxWellKnown"
|
||||||
- nix -L build -o result "${FLAKE}#uniworxWellKnown"
|
|
||||||
- nix-store --export $(nix-store -qR result) | xz -T0 -2 > well-known.nar.xz
|
- nix-store --export $(nix-store -qR result) | xz -T0 -2 > well-known.nar.xz
|
||||||
before_script: *nix-before
|
before_script: *nix-before
|
||||||
needs:
|
needs:
|
||||||
@ -75,8 +74,7 @@ frontend:
|
|||||||
script:
|
script:
|
||||||
- xzcat node-dependencies.nar.xz | nix-store --import
|
- xzcat node-dependencies.nar.xz | nix-store --import
|
||||||
- xzcat well-known.nar.xz | nix-store --import
|
- xzcat well-known.nar.xz | nix-store --import
|
||||||
- source .gitlab-ci/construct-flake-url.sh
|
- nix -L build -o result ".#uniworxFrontend"
|
||||||
- nix -L build -o result "${FLAKE}#uniworxFrontend"
|
|
||||||
- nix-store --export $(nix-store -qR result) | xz -T0 -2 > frontend.nar.xz
|
- nix-store --export $(nix-store -qR result) | xz -T0 -2 > frontend.nar.xz
|
||||||
before_script: *nix-before
|
before_script: *nix-before
|
||||||
needs:
|
needs:
|
||||||
@ -96,8 +94,7 @@ uniworx:lib:uniworx:
|
|||||||
stage: backend:build
|
stage: backend:build
|
||||||
script:
|
script:
|
||||||
- xzcat frontend.nar.xz | nix-store --import
|
- xzcat frontend.nar.xz | nix-store --import
|
||||||
- source .gitlab-ci/construct-flake-url.sh
|
- nix -L build -o result ".#uniworx:lib:uniworx"
|
||||||
- nix -L build -o result "${FLAKE}#uniworx:lib:uniworx"
|
|
||||||
- nix-store --export $(nix-store -qR result) | xz -T0 -2 > uniworx:lib:uniworx.nar.xz
|
- nix-store --export $(nix-store -qR result) | xz -T0 -2 > uniworx:lib:uniworx.nar.xz
|
||||||
before_script: *nix-before
|
before_script: *nix-before
|
||||||
needs:
|
needs:
|
||||||
@ -119,8 +116,7 @@ uniworx:exe:uniworx:
|
|||||||
stage: backend:build
|
stage: backend:build
|
||||||
script:
|
script:
|
||||||
- xzcat uniworx:lib:uniworx.nar.xz | nix-store --import
|
- xzcat uniworx:lib:uniworx.nar.xz | nix-store --import
|
||||||
- source .gitlab-ci/construct-flake-url.sh
|
- nix -L build -o result ".#uniworx:exe:uniworx"
|
||||||
- nix -L build -o result "${FLAKE}#uniworx:exe:uniworx"
|
|
||||||
- nix-store --export $(nix-store -qR result) | xz -T0 -2 > uniworx:exe:uniworx.nar.xz
|
- nix-store --export $(nix-store -qR result) | xz -T0 -2 > uniworx:exe:uniworx.nar.xz
|
||||||
before_script: *nix-before
|
before_script: *nix-before
|
||||||
needs:
|
needs:
|
||||||
@ -144,8 +140,7 @@ uniworx:exe:uniworxdb:
|
|||||||
stage: backend:build
|
stage: backend:build
|
||||||
script:
|
script:
|
||||||
- xzcat uniworx:lib:uniworx.nar.xz | nix-store --import
|
- xzcat uniworx:lib:uniworx.nar.xz | nix-store --import
|
||||||
- source .gitlab-ci/construct-flake-url.sh
|
- nix -L build -o result ".#uniworx:exe:uniworxdb"
|
||||||
- nix -L build -o result "${FLAKE}#uniworx:exe:uniworxdb"
|
|
||||||
- nix-store --export $(nix-store -qR result) | xz -T0 -2 > uniworx:exe:uniworxdb.nar.xz
|
- nix-store --export $(nix-store -qR result) | xz -T0 -2 > uniworx:exe:uniworxdb.nar.xz
|
||||||
before_script: *nix-before
|
before_script: *nix-before
|
||||||
needs:
|
needs:
|
||||||
@ -169,8 +164,7 @@ uniworx:exe:uniworxload:
|
|||||||
stage: backend:build
|
stage: backend:build
|
||||||
script:
|
script:
|
||||||
- xzcat uniworx:lib:uniworx.nar.xz | nix-store --import
|
- xzcat uniworx:lib:uniworx.nar.xz | nix-store --import
|
||||||
- source .gitlab-ci/construct-flake-url.sh
|
- nix -L build -o result ".#uniworx:exe:uniworxload"
|
||||||
- nix -L build -o result "${FLAKE}#uniworx:exe:uniworxload"
|
|
||||||
- nix-store --export $(nix-store -qR result) | xz -T0 -2 > uniworx:exe:uniworxload.nar.xz
|
- nix-store --export $(nix-store -qR result) | xz -T0 -2 > uniworx:exe:uniworxload.nar.xz
|
||||||
before_script: *nix-before
|
before_script: *nix-before
|
||||||
needs:
|
needs:
|
||||||
@ -195,8 +189,7 @@ check:
|
|||||||
script:
|
script:
|
||||||
- xzcat frontend.nar.xz | nix-store --import
|
- xzcat frontend.nar.xz | nix-store --import
|
||||||
- xzcat uniworx:lib:uniworx.nar.xz | nix-store --import
|
- xzcat uniworx:lib:uniworx.nar.xz | nix-store --import
|
||||||
- source .gitlab-ci/construct-flake-url.sh
|
- nix -L flake check .
|
||||||
- nix -L flake check ${FLAKE}
|
|
||||||
before_script: *nix-before
|
before_script: *nix-before
|
||||||
needs:
|
needs:
|
||||||
- job: node dependencies # transitive
|
- job: node dependencies # transitive
|
||||||
@ -214,8 +207,7 @@ container:
|
|||||||
stage: container:build
|
stage: container:build
|
||||||
script:
|
script:
|
||||||
- xzcat uniworx:exe:uniworx.nar.xz | nix-store --import
|
- xzcat uniworx:exe:uniworx.nar.xz | nix-store --import
|
||||||
- source .gitlab-ci/construct-flake-url.sh
|
- cp -pr --reflink=auto -L $(nix build --print-out-paths ".#uniworxDocker") uniworx.tar.gz
|
||||||
- cp -pr --reflink=auto -L $(nix build --print-out-paths "${FLAKE}#uniworxDocker") uniworx.tar.gz
|
|
||||||
before_script: *nix-before
|
before_script: *nix-before
|
||||||
needs:
|
needs:
|
||||||
- job: node dependencies # transitive
|
- job: node dependencies # transitive
|
||||||
@ -239,6 +231,34 @@ container:
|
|||||||
interruptible: true
|
interruptible: true
|
||||||
rules: &release-rules
|
rules: &release-rules
|
||||||
- if: $CI_COMMIT_TAG =~ /^v/
|
- if: $CI_COMMIT_TAG =~ /^v/
|
||||||
|
test container:
|
||||||
|
stage: container:build
|
||||||
|
script:
|
||||||
|
- xzcat uniworx:exe:uniworx.nar.xz | nix-store --import
|
||||||
|
- cp -pr --reflink=auto -L $(nix build --print-out-paths ".#uniworxTestDocker") uniworx.tar.gz
|
||||||
|
before_script: *nix-before
|
||||||
|
needs:
|
||||||
|
- job: node dependencies # transitive
|
||||||
|
artifacts: false
|
||||||
|
- job: well known # transitive
|
||||||
|
artifacts: false
|
||||||
|
- job: frontend # tranitive
|
||||||
|
artifacts: false
|
||||||
|
- job: uniworx:lib:uniworx # transitive
|
||||||
|
artifacts: false
|
||||||
|
- job: uniworx:exe:uniworx
|
||||||
|
artifacts: true
|
||||||
|
- job: check # sanity
|
||||||
|
artifacts: false
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- uniworx.tar.gz
|
||||||
|
name: "${CI_JOB_NAME}-${CI_COMMIT_SHORT_SHA}"
|
||||||
|
expire_in: "1 day"
|
||||||
|
retry: 2
|
||||||
|
interruptible: true
|
||||||
|
rules: &test-release-rules
|
||||||
|
- if: $CI_COMMIT_TAG =~ /^t/
|
||||||
|
|
||||||
parse changelog:
|
parse changelog:
|
||||||
stage: prepare release
|
stage: prepare release
|
||||||
@ -249,9 +269,30 @@ parse changelog:
|
|||||||
before_script: *nix-before
|
before_script: *nix-before
|
||||||
script:
|
script:
|
||||||
- xzcat node-dependencies.nar.xz | nix-store --import
|
- xzcat node-dependencies.nar.xz | nix-store --import
|
||||||
- source .gitlab-ci/construct-flake-url.sh
|
- nix -L run ".#jqChangelogJson" -- -r '.versions[0].version' > .current-version
|
||||||
- nix -L run "${FLAKE}#jqChangelogJson" -- -r '.versions[0].version' > .current-version
|
- nix -L run ".#jqChangelogJson" -- -r '.versions[0].body' > .current-changelog.md
|
||||||
- nix -L run "${FLAKE}#jqChangelogJson" -- -r '.versions[0].body' > .current-changelog.md
|
- echo "VERSION=$(cat .current-version)" >> build.env
|
||||||
|
artifacts:
|
||||||
|
reports:
|
||||||
|
dotenv: build.env
|
||||||
|
paths:
|
||||||
|
- .current-version
|
||||||
|
- .current-changelog.md
|
||||||
|
name: "changelog-${CI_COMMIT_SHORT_SHA}"
|
||||||
|
expire_in: "1 day"
|
||||||
|
retry: 2
|
||||||
|
interruptible: true
|
||||||
|
parse test changelog:
|
||||||
|
stage: prepare release
|
||||||
|
needs:
|
||||||
|
- job: node dependencies
|
||||||
|
artifacts: true
|
||||||
|
rules: *test-release-rules
|
||||||
|
before_script: *nix-before
|
||||||
|
script:
|
||||||
|
- xzcat node-dependencies.nar.xz | nix-store --import
|
||||||
|
- nix -L run ".#jqChangelogJson" -- -r '.versions[0].version' > .current-version
|
||||||
|
- nix -L run ".#jqChangelogJson" -- -r '.versions[0].body' > .current-changelog.md
|
||||||
- echo "VERSION=$(cat .current-version)" >> build.env
|
- echo "VERSION=$(cat .current-version)" >> build.env
|
||||||
artifacts:
|
artifacts:
|
||||||
reports:
|
reports:
|
||||||
@ -291,6 +332,33 @@ upload container:
|
|||||||
artifacts: false
|
artifacts: false
|
||||||
rules: *release-rules
|
rules: *release-rules
|
||||||
retry: 2
|
retry: 2
|
||||||
|
upload test container:
|
||||||
|
variables:
|
||||||
|
GIT_STRATEGY: none
|
||||||
|
stage: release
|
||||||
|
image: quay.io/skopeo/stable:latest
|
||||||
|
script:
|
||||||
|
- skopeo --insecure-policy copy --dest-creds "${CI_REGISTRY_USER}:${CI_JOB_TOKEN}" docker-archive://$(pwd)/uniworx.tar.gz docker://${CI_REGISTRY}/fradrive/fradrive/test:${CI_COMMIT_REF_NAME}
|
||||||
|
- skopeo --insecure-policy copy --src-creds "${CI_REGISTRY_USER}:${CI_JOB_TOKEN}" --dest-creds "${CI_REGISTRY_USER}:${CI_JOB_TOKEN}" docker://${CI_REGISTRY}/fradrive/fradrive/test:${CI_COMMIT_REF_NAME} docker://${CI_REGISTRY}/fradrive/fradrive/test:latest
|
||||||
|
needs:
|
||||||
|
- job: node dependencies # transitive
|
||||||
|
artifacts: false
|
||||||
|
- job: well known # transitive
|
||||||
|
artifacts: false
|
||||||
|
- job: frontend # tranitive
|
||||||
|
artifacts: false
|
||||||
|
- job: uniworx:lib:uniworx # transitive
|
||||||
|
artifacts: false
|
||||||
|
- job: uniworx:exe:uniworx # transitive
|
||||||
|
artifacts: false
|
||||||
|
- job: test container
|
||||||
|
artifacts: true
|
||||||
|
- job: parse test changelog
|
||||||
|
artifacts: true
|
||||||
|
- job: check # sanity
|
||||||
|
artifacts: false
|
||||||
|
rules: *test-release-rules
|
||||||
|
retry: 2
|
||||||
|
|
||||||
release:
|
release:
|
||||||
variables:
|
variables:
|
||||||
@ -309,3 +377,20 @@ release:
|
|||||||
artifacts: false
|
artifacts: false
|
||||||
- job: parse changelog
|
- job: parse changelog
|
||||||
artifacts: true
|
artifacts: true
|
||||||
|
test release:
|
||||||
|
variables:
|
||||||
|
GIT_STRATEGY: none
|
||||||
|
stage: release
|
||||||
|
image: registry.gitlab.com/gitlab-org/release-cli:latest
|
||||||
|
rules: *test-release-rules
|
||||||
|
script:
|
||||||
|
- echo "Will create test release ${VERSION}-test..."
|
||||||
|
release:
|
||||||
|
name: "${VERSION}-test"
|
||||||
|
tag_name: '$CI_COMMIT_TAG'
|
||||||
|
description: .current-changelog.md
|
||||||
|
needs:
|
||||||
|
- job: check # sanity
|
||||||
|
artifacts: false
|
||||||
|
- job: parse test changelog
|
||||||
|
artifacts: true
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
if [ -n "${CI_COMMIT_TAG}" ]; then
|
|
||||||
ref="refs/tags/${CI_COMMIT_TAG}"
|
|
||||||
else
|
|
||||||
ref="refs/heads/${CI_COMMIT_BRANCH}"
|
|
||||||
fi
|
|
||||||
export FLAKE="git+${CI_REPOSITORY_URL}?rev=${CI_COMMIT_SHA}&ref=${ref}"
|
|
||||||
540
CHANGELOG.md
540
CHANGELOG.md
@ -2,6 +2,546 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
## [27.4.79](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.78...v27.4.79) (2024-09-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **notifications:** fix [#180](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/180) qualification expiry notification are sent only once ([74f7633](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/74f7633837870448f7cab1013719f42ab49941fe))
|
||||||
|
* **supervision:** fix [#181](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/181) by unifying deletion of supervision ([6a070a6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6a070a67756bd4ef4b9b5efc176f34c7ed183f1a))
|
||||||
|
|
||||||
|
## [27.4.78](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.77...v27.4.78) (2024-09-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** acs auto synch had inverted success/failure ([4f7855b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4f7855b9ee7133c5ee7e2ca63d63e5d9f060d62f))
|
||||||
|
* **avs:** fix [#124](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/124) avs auto synch filter working ([2a27a1e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2a27a1efa673a4245a7e8667bd30c79ac1891b9c))
|
||||||
|
* **avs:** fix [#178](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/178) by deleting old superiors for individual users ([ade27e6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ade27e647913ffe4432b41d585b3e00d1c68d4a0))
|
||||||
|
* **avs:** typo in superior remark, towards [#178](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/178) ([3c5edb1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3c5edb1b970c8c154d9957837007815b29e23964))
|
||||||
|
* **mail:** fix [#179](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/179) by adding download links for PDF attachments ([620e3e4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/620e3e470080831826ccc960dd876e7bb4fcea03))
|
||||||
|
|
||||||
|
## [27.4.77](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.76...v27.4.77) (2024-09-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** attempt LDAP upsert before creating avs users ([cfe2318](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cfe2318f81c951a7f7310e8bcd9ec25d79417587))
|
||||||
|
* **avs:** company superiors are now irregular supervisors and old ones are deleted ([7e5c256](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7e5c256b4c15a15f7218dd7c1490d5e7add4b1c1))
|
||||||
|
* **avs:** fix [#124](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/124) implement automatic avs driving licence synchronisation ([cc5da9a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cc5da9a2a9bfc8a29f6fe19260bd6dc5412ad4a1))
|
||||||
|
* **avs:** switch company did not always increase priority ([8ec2875](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8ec2875590718f28c3bab8c10141065e11f1405c))
|
||||||
|
* **build:** minor linter fix ([be5e609](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/be5e609b1fe879428784d78fa62a559d0764a85a))
|
||||||
|
* **firm:** fix [#174](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/174) by adding address search filter to all company view ([40dadd5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/40dadd58762156005b5889b93a56ffdc044b4460))
|
||||||
|
* **firm:** fix [#175](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/175) by separating superiors in firm tables and selections ([8397c46](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8397c468a04af42ba3baee2f84a0051adbc74374))
|
||||||
|
* **ldap:** no more timeout for ldap synch all button ([f946e99](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f946e99da3bc37514a4e3621438ac133cdc16732))
|
||||||
|
* **linter:** minor bug in exam-correct.hs ([8bc3663](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8bc3663ee2e4ded19091ebe350de82cd693093fc))
|
||||||
|
* **mail:** display html emails no longer distorts page ([b0972bb](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b0972bb154f453edd545fb4f658d9f5ff79966eb)), closes [#2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/2)
|
||||||
|
* **model:** flip erroneous boolean SQL default for CompanyPostalAddress ([b7e5b8f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b7e5b8f111b5115d816d984c6ef2f12edfcef5bb))
|
||||||
|
* **user:** fix pagination and count for supervision tables ([9c82558](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9c82558d71a032dad27e892c489c7004d091e088))
|
||||||
|
|
||||||
|
## [27.4.76](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.75...v27.4.76) (2024-08-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **ap:** disambiguate action message ([8b0466e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8b0466e74e36e1d0d07518fd317d46b00ab53eff))
|
||||||
|
* **avs:** fix [#173](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/173) by not using firm superior email as display email ([43f5c5f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/43f5c5f4854d1ab2af27b479e72a58e2818a5696))
|
||||||
|
* **avs:** towards [#117](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/117) update if current value is Nothing even if oldval == newval ([d1fa01f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d1fa01fcc5125c4adee8849f9c944884926f78ad))
|
||||||
|
* **avs:** using firm superior as UserEmail is a no-go due to uniqueness constraints ([507a7e0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/507a7e02fc68476d01031dc9f9ee1a669a453ed1))
|
||||||
|
* **build:** linter likes it ([f929e03](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f929e03129378e08c8a08ed4bd6f8e8716401813))
|
||||||
|
* **course:** fix [#150](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/150) course edit for associated qualifications requires school admin or lecturer rights ([5b6e4e6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5b6e4e60e7d2957fbce93ee2e2d6d3464b4e3db7))
|
||||||
|
* **course:** fix [#148](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/148) course qualification ordering ([cfd2534](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cfd25348ad3b63ac6bc5031467a3c4ead2e07eed)), closes [#150](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/150)
|
||||||
|
* **course:** fix [#149](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/149) course cloning proposes associated qualifications ([e141976](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e1419766f3a06f702abad0ea42f6552305504ba0))
|
||||||
|
* **course:** fix [#150](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/150) no longer allow duplicated associated qualifications and orders due to editing existing ([ec02767](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ec027675525b30198378745ed281f60a42471807))
|
||||||
|
* **course:** WIP course cloning should propose same associated qualifications, towards [#149](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/149) ([bc47387](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bc47387c91dda60a2f12e52dba28ea7b079316f0))
|
||||||
|
* **lms:** max e-learning tries default removed and info added to lms overview ([11fdcf0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/11fdcf0d445b8cfe97c3a3c26513a9229937c536))
|
||||||
|
* **user:** format userDisplayNames having umlaut substitutes with respect to userSurname correctly ([e35a5e9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e35a5e99a6cea0976fd1c28f919e7d0ac0338503))
|
||||||
|
|
||||||
|
## [27.4.75](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.74...v27.4.75) (2024-07-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **build:** make linter happy again ([c17c18f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c17c18f9247ef322bc051602a3cb4a52cd50affa))
|
||||||
|
* **build:** minor ([ab28c8c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ab28c8c2437680023d80e6ab43113d4328b3a151))
|
||||||
|
* **firm:** fix [#157](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/157) by removing redundant duplicated code in firm user and supervision handling ([28e2739](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/28e2739e515700d15c75647c0efe2fe9a9cf15b1))
|
||||||
|
* **job:** change some queueJob' to queueJob instead ([fa0541a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fa0541aa4eaf10f98535a0959593b148b8346109))
|
||||||
|
* **lms:** allow 2nd reminders to be independent of renewal period ([d853e85](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d853e8559b753865ee818bf24764f5c8d2e2303f))
|
||||||
|
* **lms:** move lms reuse info from QualificationR to LmsR ([468af9d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/468af9de9da44a8ad685ca4bb6890a3e630b58be))
|
||||||
|
* **lms:** send second reminder indepentently from renewal period ([a97c3a5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a97c3a5c9d3cb9dddf90f561712f0845400893bd))
|
||||||
|
* **nix:** workaround parsing port numbers failed in nix-shell ([b5215cc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b5215cc7e8df3a7ad636271c8e6950979b2b8e42))
|
||||||
|
* **users:** nameHtml no longer complains about differing case for surname and displayname ([a1668f8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a1668f891a36b887439afb098f016ef22535af42))
|
||||||
|
* **users:** remove users with company post address from list of unreachable users ([c813c66](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c813c665ed306135b7813d91d23310341c689f41))
|
||||||
|
|
||||||
|
## [27.4.74](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.73...v27.4.74) (2024-07-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **lms:** fix [#161](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/161) lms for multiple joint qualifications ([f869a82](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f869a829d2c1a726930864b3af62d1f0fbebe955))
|
||||||
|
|
||||||
|
## [27.4.73](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.72...v27.4.73) (2024-07-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **letter:** rephrase some minor letter parts ([0ac75e0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0ac75e0d5948cb90855d0e36ca8e99c22a0f6fcb))
|
||||||
|
|
||||||
|
## [27.4.72](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.71...v27.4.72) (2024-07-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** do not associate users by AvsInfoPersonEmail ([9e2f221](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9e2f2214ce5c7ee1e8d80e6fa75298b7a70d9043))
|
||||||
|
* **avs:** fix superfluous quotes for matriculation numbers on newly created users ([ff9014c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ff9014ce05d197c1dc0fce0774a640789cb38b26))
|
||||||
|
* **avs:** towards [#169](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/169) - superiors are elevated to max priority for that company ([5bf8539](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5bf85394d4db6de8f10b4e318d667130d37601ac))
|
||||||
|
* **firm:** supervisor secondary did not work as intended ([d4f3ce7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d4f3ce7bf3d208b16f95ab81971b47dfa752939a))
|
||||||
|
|
||||||
|
## [27.4.71](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.70...v27.4.71) (2024-06-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** company superior emails become company wide supervisors ([37efc89](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/37efc89e0723452e6d271ba5b43d6bd026642190))
|
||||||
|
* **avs:** match mobile number better between LDAP and AVS ([f108c6c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f108c6cfec2d94d866e7c1605b0abe5471fd0f2b))
|
||||||
|
* **avs:** new AVS from existing LDAP user no longer misses fields ([2559346](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2559346d963ede802321dfc8cbd2088d9a5de685))
|
||||||
|
* **avs:** priority for picking primary email demote superior ([e4fa1dd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e4fa1ddd6873910bef82d569fe16aca936efc567))
|
||||||
|
* **build:** add missing license file ([8721bdb](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8721bdb3f349658baab144d64c19942bfd7fa49a))
|
||||||
|
* **build:** hlint wants a newtype instead ([18cdc52](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/18cdc52df094b9dbccd4f015561367cea59e33fe))
|
||||||
|
* **doc:** fix erroneous unintentional haddock annotations ([3dfc7f8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3dfc7f8c8b12dd6ef87848a75f1669d700fffe4c))
|
||||||
|
* **i18n:** add missing translation for new primary company ([c212f2e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c212f2e8d735616e59c9b8111a34118e3a48fd47))
|
||||||
|
* **i18n:** add missing translation for new primary company ([2cc529b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2cc529be39655c317ca028f8f09fa80826ec668d))
|
||||||
|
* **ldap:** match mobile number better between LDAP and AVS ([47e5628](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/47e56280fce4ad37e6bc3b9f1c61cb7867069cc5))
|
||||||
|
* **letter:** adjust spacing, pin location and interpolation ([d4a0e1f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d4a0e1f201151f76e8e9afd67b456cc878d2afde))
|
||||||
|
* **letter:** convenience links working again ([5f1af13](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5f1af130edae7ada2f0c7f7829890bbe0d4f395a))
|
||||||
|
* **letter:** expiry and valid dates were wrong ([f8c3663](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f8c36636ff1f2591507e993af32ed01af94cf1fc))
|
||||||
|
* **letter:** switch markdown for renewal letter too ([c38e87e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c38e87e1e0e9285a10c00521b7440cd8246af88a))
|
||||||
|
* **print:** fix [#167](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/167) by sotring affected user in PrintJob ([73aecc2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/73aecc2df833bdeed93a113b6c756e36b50491b7))
|
||||||
|
|
||||||
|
## [27.4.70](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.69...v27.4.70) (2024-06-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **build:** hlint wants a newtype instead ([0766351](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/07663516e520814e26740d671325b7cd10855dd4))
|
||||||
|
|
||||||
|
## [27.4.69](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.68...v27.4.69) (2024-06-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** fix type causing avs surname upate not working ([822c43c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/822c43c8a7db2086954ad187502ec2c4f1811d17))
|
||||||
|
* **avs:** keep company on unchange address/email only if either is non-empty ([766b858](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/766b8589d6945df21fc6ce90d35a004655ffa471))
|
||||||
|
* **avs:** synch job deletes used row instead of truncation ([d7acc7a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d7acc7a2d0fe5fc18929a8cb2d9c9f8a259c9944))
|
||||||
|
|
||||||
|
## [27.4.68](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.67...v27.4.68) (2024-06-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **letter:** minor ([2ae11dc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2ae11dc25c000486af9acc26439a0580f5c687f2))
|
||||||
|
|
||||||
|
## [27.4.67](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.66...v27.4.67) (2024-06-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** fix rare avs update bug involving values optional in avs but compulsory in user entity ([a6d0105](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a6d0105903caba0eb47715eeb217ea2c53d99e23))
|
||||||
|
|
||||||
|
## [27.4.66](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.65...v27.4.66) (2024-06-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** fix [#164](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/164) by removing companyPersonalNumber and companyDepartment upon ldap sync expiry ([da74b95](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/da74b957295caefb010c90297af557f997b18e7c))
|
||||||
|
* **avs:** fix [#165](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/165) by updating userCompanyDepartmen and userCompanyPersonalNumer ([76e0710](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/76e0710c7b54a40d2c236299ea4fabd009d3f35a))
|
||||||
|
* **avs:** repeated avs sync enqueue no longe violates duplicate db uniqueness constraints ([996e6a0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/996e6a0ce563bda96638863efd40ce38fce8ac2b))
|
||||||
|
* **avs:** update email on manual company switch ([9fd80f2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9fd80f25526eefce217c659f6ea2991771c11ece)), closes [#164](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/164)
|
||||||
|
|
||||||
|
## [27.4.65](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.64...v27.4.65) (2024-06-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** company update no longer fails on duplicate key ([bb101de](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bb101dee7b40cd3d8ba10a559af642396d5b87b5))
|
||||||
|
* **avs:** profile page correctly indicates automatic email and postal addresses ([e553ad4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e553ad4358a71fc96fa946533f0441d4af5202c9))
|
||||||
|
* **avs:** steps towards [#164](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/164) ([aa1d230](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/aa1d230e497f0e59dbea9f4fd5c7da773f5a4280))
|
||||||
|
* **lette:** adjust window for new pin letters ([6acfd84](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6acfd849aeb473a018f7a9c34e69f61b3c22b6f8))
|
||||||
|
|
||||||
|
## [27.4.64](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.63...v27.4.64) (2024-05-27)
|
||||||
|
|
||||||
|
## [27.4.63](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.62...v27.4.63) (2024-05-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** company update checks uniques and ignores those updates if necessary ([9451d90](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9451d90a9e00d08a2a7d169c4674d99ff1018ee9))
|
||||||
|
|
||||||
|
## [27.4.62](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.61...v27.4.62) (2024-05-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** avs update on company shorthands working now ([ff2347b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ff2347b1c950c7a2bb281cdcd07a52925e23b9f0))
|
||||||
|
* **avs:** deal gracefully with empty card status results ([ccf9340](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ccf934044938277d821eb4b9ea08a8a134e84189))
|
||||||
|
|
||||||
|
## [27.4.61](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.60...v27.4.61) (2024-05-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** fix [#76](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/76) allowing company changes and fix [#69](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/69) ([3c4a0b8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3c4a0b86c1e3d8a28405ab73b964ba1b988d2822))
|
||||||
|
* **build:** add missing tex packages ([6750798](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6750798920dc76882f4e8ef39b47018fb7b77e44))
|
||||||
|
* **build:** workaround non modal form result handler ([2fbd281](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2fbd28154cd7aea282eaa2604a42263ac90e3b1e))
|
||||||
|
|
||||||
|
## [27.4.60](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.59...v27.4.60) (2024-04-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** disable caching by 0s no longer causes an exception ([d578e80](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d578e80282c8bf6872fa6040514a9d2c85582707))
|
||||||
|
* **avs:** fix [#152](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/152) by providing new online avs card filter throughout ([ad2375b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ad2375b338866f37c8b7825a9eab12fa6c9abccb))
|
||||||
|
* **avs:** fix [#36](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/36) and remove dead code ([4f8850b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4f8850b3b4f710f9cf59163175b27599c97ac5c0))
|
||||||
|
* **avs:** fix [#69](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/69) by redesigning live avs status page ([697979c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/697979c277ce7198f4573d6cea30373a1fcc17da))
|
||||||
|
* **avs:** invalidate contact cache after licence writes ([c382be9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c382be9325fcc92e13cb5dc2ad7c20b198db26fc))
|
||||||
|
* **avs:** several minor bugfixes ([a52c8a6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a52c8a6ad709029a8822d383370b0d2bdd25e7d7)), closes [#158](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/158)
|
||||||
|
* **build:** add import needed for production only ([724e4a0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/724e4a0bec343ab9c6d172d8e93b8040bbe3fe7d))
|
||||||
|
* **build:** migration needs to check for table existens first ([f439ea4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f439ea45af9b1c4a029fc1b9b6383f3c97194ed0))
|
||||||
|
* **build:** minor error non-development code ([66eaa4f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/66eaa4f7dcc124b631414d4a1adbe555a4029100))
|
||||||
|
* **build:** missing parameters added ([83afdf7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/83afdf760f93fc1a553de3a122b444412ed84ba4))
|
||||||
|
* **build:** simple type error ([d56a1cd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d56a1cdd46259418faa737b9bb0a9d9ffba442e0))
|
||||||
|
* **build:** type error in test db fill data ([f465cc9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f465cc972367233a4944dd0aeb81b223a187bb85))
|
||||||
|
* **doc:** minor haddock problems ([d4f8a6c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d4f8a6c77b2a4a4540935f7f0beca0d0605508c8))
|
||||||
|
* **firm:** supervisor filter acts weird in test environment ([b566e59](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b566e59eb1325485fe26dc4f0b5cb63165c58f74))
|
||||||
|
* **i18n:** fix some bad plurals ([890f8ad](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/890f8ad8b60115533faa6b99f4c4504243cbfb1d))
|
||||||
|
* **lint:** remove minor superfluous dollar ([64a1233](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/64a123387f3539b73649d02a6ecd97de577097e6))
|
||||||
|
* **qualification:** fix [#159](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/159) by removing an misleadingly named column for user qualification table ([fd6a538](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fd6a5384d3517958a3c7726e32eed3bad197a591))
|
||||||
|
|
||||||
|
## [27.4.59](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.58...v27.4.59) (2024-02-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **sql:** remove potential bug in relation to missing parenthesis after not_ ([42695cf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/42695cf5ef9f21691dc027f1ec97d57eec72f03e))
|
||||||
|
|
||||||
|
## [27.4.58](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.57...v27.4.58) (2024-02-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **health:** negative interface routes working as intended now ([3303c4e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3303c4eebf928e527d2f9c1eb6e2495c10b94b13))
|
||||||
|
* **lms:** previouly failed notifications will be sent again ([263894b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/263894b05899ce55635d790f5334729fbc655ecc))
|
||||||
|
|
||||||
|
## [27.4.57](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.56...v27.4.57) (2024-02-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **course:** fix [#147](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/147) abort addd participant aborts now ([d332c0c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d332c0c11afd8b1dfe1343659f0b1626c968bbde))
|
||||||
|
* **health:** fix [#151](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/151) by offering route /health/interface/* ([c71814d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c71814d1ef1efc16c278136dfd6ebd86bd1d20db))
|
||||||
|
* **health:** fix [#153](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/153) and offer interface health route matching ([ce3852e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ce3852e3d365e62b32d181d58b7cbcc749e49373))
|
||||||
|
|
||||||
|
## [27.4.56](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.55...v27.4.56) (2023-12-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **firm:** improve supervisor filter by caching ([88f24fe](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/88f24fe6f199290a83af2d204ba9aa2a838d11b8))
|
||||||
|
* **firm:** improve supervisor filter yet once more ([c7b5a3c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c7b5a3c6cb70c314ecbfbe25969b4b6be1d43161))
|
||||||
|
* **users:** fix [#121](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/121) by providing last login column, which was the last part missing ([decc5af](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/decc5af6829998e2d0db79382bbd9a7bad7b5b09))
|
||||||
|
|
||||||
|
## [27.4.55](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.54...v27.4.55) (2023-12-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **build:** while the blank is necessary to prevent unnecessary migrations, it is not allowed either, see [#133](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/133) ([a4b2af7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a4b2af7f157444ead8c9df989741b266f7c2b4f2))
|
||||||
|
* **firm:** supervisor filter performance ([db77850](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/db77850c4f4cd1d68bfd38e02e0ae24584e1e556))
|
||||||
|
* **migration:** fix [#133](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/133) by removing old outdated migrations irrelevant to FRADrive ([d4f0d69](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d4f0d69428a4f7fc887cb6854cb59e3dea83b9bc))
|
||||||
|
* **migration:** ignore superfluous migration entries gracefully ([1d48b62](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1d48b627f6b8cf1b03e2ef63850c36c429c9d3d6))
|
||||||
|
* **school:** fix [#133](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/133) by adjusting default value ([2509358](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/25093588784381a19f34e5b091677b908420ddea))
|
||||||
|
|
||||||
|
## [27.4.54](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.53...v27.4.54) (2023-12-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **db:** prevent superfluous migrations ([b73557a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b73557a1eee4315911c6369032447f8d1836d964))
|
||||||
|
|
||||||
|
## [27.4.53](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.52...v27.4.53) (2023-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **admin:** minor fixes and translations for admin problem page ([30fae33](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/30fae33dedb1501e570e9edca288fea3c84ac84a))
|
||||||
|
* **avs:** background synch was only triggerd by manual synchs ([48ef25a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/48ef25aa8ffbbd96c1578ae85b76f090d9042595))
|
||||||
|
* **firm:** group multi select field supervisor ([fc0ca7b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fc0ca7b854a686cf395dadf81b7423e530fd26b8))
|
||||||
|
* **firm:** set supervisor field not all fields required ([9878956](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9878956716b04c7ae88989cb9b059d3edcb923dc))
|
||||||
|
* **firm:** supervisor filter ([3acb847](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3acb847915010d10358ea02000c231dbba7cba26))
|
||||||
|
* **form:** multiSelectField working with grouped options ([3aa8901](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3aa89019a8b4393da0eca715871a3793c1e3abb2))
|
||||||
|
* **print:** keep print jobs on user merge and lms id deletion ([a15862e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a15862ea72bc374af870ef3a23f86ae32c2c67a9))
|
||||||
|
|
||||||
|
## [27.4.52](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.51...v27.4.52) (2023-12-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **build:** redundant parenthesis ([50eda5f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/50eda5f65f7394fe519546609fe748490cb4dd72))
|
||||||
|
* **firm:** restrict firm access to company supervisors only ([0a06efd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0a06efd76c63180c996657c2c7d78efc5bddd83d))
|
||||||
|
* **firm:** supervisor changes led to inconsistent DB ([1d3345c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1d3345cbba1cb65ee49c6f62e145750545439642))
|
||||||
|
|
||||||
|
## [27.4.51](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.50...v27.4.51) (2023-11-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **build:** minor errors firm handler ([06bb44c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/06bb44cf715375b5dd0141a46f8e10924ad6cd9c))
|
||||||
|
* **cache:** remove risky caching for submissions ([4ae59fc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4ae59fc1fa658e1462139ddddd6dc80308d85872))
|
||||||
|
* **firm:** show default supervisors with no employees too ([0f9a7a8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0f9a7a8c53d216ca7a6d0a25462b19ab1fa00bb4))
|
||||||
|
|
||||||
|
## [27.4.50](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.49...v27.4.50) (2023-11-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** preserve unset pin passwords in update ([8c4f848](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8c4f848675e1125547d1fdfa05560affe4794118))
|
||||||
|
* **build:** fix whitespace in routes ([a24e44e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a24e44efc9a20d3934d96640bb9e21b3b6d55b96))
|
||||||
|
* **build:** minor ([954a239](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/954a23936a35ea6c32247d7e191312e63888c12d))
|
||||||
|
* **firm:** add sql indices for frequent filters to greatly enhance performance ([63e6d94](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/63e6d94df2fd1ce879cb59d14bc854f3c2556586))
|
||||||
|
* **firm:** firm messaging now works fine ([65cdc8d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/65cdc8ddfef19eb3a5578c536575f91ba9717a13))
|
||||||
|
* **firm:** foreign supervisor counts correct and sortable ([601ce7a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/601ce7abdf2a392d30f1ff799a2338968be795f1))
|
||||||
|
* **firm:** sending messages works, but not test messages ([42ff02d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/42ff02d27e431a8855db7bf3046a1b74d297e6da))
|
||||||
|
* **lms:** improve sorting for firm all ([3865bda](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3865bda64d488c161b55e1f6eb48ca1b742dff98))
|
||||||
|
* **lms:** LMS restart failing due to old LmsUser entry ([6761767](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6761767c6ca8cab62a22aa6f755e6231e07ab411))
|
||||||
|
|
||||||
|
## [27.4.49](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.48...v27.4.49) (2023-11-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **lms:** report log did not match qualification ([390ff31](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/390ff317ea3bb4ef8918c9cda858f5f228e4a882))
|
||||||
|
|
||||||
|
## [27.4.48](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.47...v27.4.48) (2023-11-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **lms:** mark as ended only if not seen for at least one day ([8165892](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8165892b2e4f945780bb8420cfc4eed50fdd294d))
|
||||||
|
|
||||||
|
## [27.4.47](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.46...v27.4.47) (2023-11-03)
|
||||||
|
|
||||||
|
## [27.4.46](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.45...v27.4.46) (2023-11-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **course:** grant qualifications now issues and unblocks ([5d8d8cf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5d8d8cf17e634ecb950a1c329c859fb93f94ef77))
|
||||||
|
* **users:** allow prefer postal setting for users with fraport department ([a9d56c5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a9d56c51dcc727f8637b09a0e849372e75032f5e))
|
||||||
|
|
||||||
|
## [27.4.45](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.44...v27.4.45) (2023-10-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **hoogle:** remove erroneous comment ([c011d88](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c011d887cece8338920355b540aa4b233e0b994f))
|
||||||
|
* **sap:** yet another fix for finding date intervals ([fde97b0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fde97b048ab04ab59c9e3f2a2f74bb2c1e996b22))
|
||||||
|
|
||||||
|
## [27.4.44](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.43...v27.4.44) (2023-10-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **sap:** combine immediate next day licence chnages for SAP ([f4adfdf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f4adfdf87270930d4ca6611f2a9956613fcace53))
|
||||||
|
* **sap:** combine immediate next day licence chnages for SAP ([cbb44f1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cbb44f106ad59e0a53ca04963ade5544120b7e21))
|
||||||
|
* **sap:** combineBlocks yet another bug squashed ([3924d14](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3924d14abd868305b42c9d04913536b4999dc45b))
|
||||||
|
* **sap:** compileBlocks ([b4a88ab](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b4a88abcf85783c350ad2bf3a5e973d13d1eb1f6))
|
||||||
|
|
||||||
|
## [27.4.43](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.42...v27.4.43) (2023-10-13)
|
||||||
|
|
||||||
|
## [27.4.42](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.41...v27.4.42) (2023-10-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **build:** Update ParticipantInvite.hs ([f888da3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f888da3ab0df45bb3c515ebb7cbb43569fdaa1fa))
|
||||||
|
* **build:** Update ParticipantInvite.hs ([fa4f9b2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fa4f9b24475261afc1e534541c8878a85e6a1b10))
|
||||||
|
* **build:** Update Utils.hs ([87f0b2e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/87f0b2edab2bcf696b7b776e47272ef2204c0b75))
|
||||||
|
|
||||||
|
## [27.4.41](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.40...v27.4.41) (2023-10-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **lms:** sorting and filtering lms status ([f48862e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f48862efbcb95e92203a200267e1bcc613af4af1))
|
||||||
|
* **lms:** sorting and filtering lms status works throughout now ([ae44703](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ae4470333e2b1b5c271b38092210c094822f4a19))
|
||||||
|
* **print:** apc ident aliases did not stop at first success ([b7d4f69](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b7d4f6913d8b1a70c1b7ef73782cf29861dc11a7))
|
||||||
|
|
||||||
|
## [27.4.40](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.39...v27.4.40) (2023-09-26)
|
||||||
|
|
||||||
|
## [27.4.39](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.38...v27.4.39) (2023-09-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **lms:** do not mark lms users with open status as ended ([a848126](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a84812640f02981875275c96e37338de4ab49996))
|
||||||
|
* **qualifications:** latest block could ignore itself ([bb708ca](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bb708ca540557b41d33996cfea9a390a457ed855))
|
||||||
|
|
||||||
|
## [27.4.38](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.37...v27.4.38) (2023-09-21)
|
||||||
|
|
||||||
|
## [27.4.37](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.36...v27.4.37) (2023-09-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **lms:** disable workaround for late lms success ([cb9e09d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cb9e09d071d22f41a92ab8140d7aaa643c748373))
|
||||||
|
|
||||||
|
## [27.4.36](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.34...v27.4.36) (2023-09-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **lms:** treat simultaneous blocks/unblocks correctly ([11752dc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/11752dc5ac96f36ebf9a4cad43fa4e4b55c1b21c))
|
||||||
|
|
||||||
|
## [27.4.35](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.34...t27.4.35) (2023-09-21)
|
||||||
|
|
||||||
|
## [27.4.34](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.33...t27.4.34) (2023-09-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **lms:** treat simultaneous blocks/unblocks correctly ([11752dc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/11752dc5ac96f36ebf9a4cad43fa4e4b55c1b21c))
|
||||||
|
|
||||||
|
## [27.4.33](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.32...t27.4.33) (2023-09-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **time:** midnight timezone conversion bug eliminated ([dfa07a9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/dfa07a95eb29f1fceec258a466e1e7c779ff6e5c))
|
||||||
|
|
||||||
|
## [27.4.32](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.31...t27.4.32) (2023-09-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **lms:** ensure lms uniqueness across all qualifications ([b85c8bd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b85c8bd74f8db526fb1cbb43ff12a24b93c07eb3))
|
||||||
|
* **lms:** simultaneous block/unblock lets unblock win in all situations ([ecd1a0f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ecd1a0fc210d1340bff5c79d8bb676a47654b509))
|
||||||
|
|
||||||
|
## [27.4.31](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.30...t27.4.31) (2023-09-13)
|
||||||
|
|
||||||
|
## [27.4.30](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.29...t27.4.30) (2023-09-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **lms:** reset e-learning more lenient ([8b0737e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8b0737e2aabc7153ae3a3df4f97f86ffc8592e7a))
|
||||||
|
|
||||||
|
## [27.4.29](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.28...t27.4.29) (2023-09-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **build:** v2 ([ac77aa1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ac77aa176a3c3977c4a802e5ed534fa2850528fe))
|
||||||
|
|
||||||
|
## [27.4.28](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.27...t27.4.28) (2023-09-07)
|
||||||
|
|
||||||
|
## [27.4.27](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.26...t27.4.27) (2023-09-07)
|
||||||
|
|
||||||
|
## [27.4.26](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.25...t27.4.26) (2023-09-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **lms:** accept success for no-status learners and print several more debug messages processing reports ([a7ed659](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a7ed659866de1d4a178bbe4e8f9cd8fbc629c724))
|
||||||
|
|
||||||
|
## [27.4.25](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.24...t27.4.25) (2023-09-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **build:** add missing file ([1fd24f6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1fd24f608dc9202fa98f52f7908f4be908a18efc))
|
||||||
|
|
||||||
|
## [27.4.24](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.23...t27.4.24) (2023-08-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **lms:** filter by status ([a74c3d8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a74c3d80cada4f9d224365727dab9676cc905f54))
|
||||||
|
* **lms:** negate learner locking condition ([a452b03](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a452b032c43dbdfd086ffa4793c83ecc32c450f8))
|
||||||
|
|
||||||
|
## [27.4.23](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.22...t27.4.23) (2023-08-29)
|
||||||
|
|
||||||
|
## [27.4.22](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.21...t27.4.22) (2023-08-29)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **build:** refix test commits somehow ([34ada53](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/34ada53de0cc5804468791854e824b730fcc84de))
|
||||||
|
|
||||||
|
## [27.4.21](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.20...t27.4.21) (2023-07-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **apc:** apc cannot distinguish ij from ji, partial fix only. Needs new font ([b4ba0a3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b4ba0a30dc7c513bb9e3c567ca771d5d75de4343))
|
||||||
|
* **block:** negate condition to test ([9cf7f39](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9cf7f3965aa95f0b8f2a1574dbad90c0257edafd))
|
||||||
|
* **qualification:** new block/unblock mechanism working now ([5397c7b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5397c7be353fc1b1e8310f66b49a9b93ee890253))
|
||||||
|
* **users:** fix [#112](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/112) and also add some convenience ([35096ac](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/35096ace01a2bc2a2d666794bb1ff92f52b3edec))
|
||||||
|
* **users:** fix [#112](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/112) working now ([88bf21c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/88bf21c9c5de3755ea6591c97dc1f99a928914d5))
|
||||||
|
|
||||||
|
## [27.4.20](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.19...t27.4.20) (2023-07-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **build:** prevent migration on non-existing table ([5bb49cd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5bb49cd88941e510a50759efaad88690f841ca47))
|
||||||
|
|
||||||
|
## [27.4.19](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.18-2...t27.4.19) (2023-07-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **build:** major qualfication block quirks fixed ([ab48e40](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ab48e40ac7e5024b7847b3995e6ae16d1c401c60))
|
||||||
|
* **build:** minor ([f9930f2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f9930f2a00d1e0f0af9b7f2af7c387bcc09cef5a))
|
||||||
|
* **db:** migration qualification block ([3d59527](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3d595271d979f29ed8bbc546f495e5ad1deae5ca))
|
||||||
|
* **job:** fix [#95](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/95) by implementing queued job deletion for admins ([5b9a554](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5b9a5545457dbe506d20f7362fb6e0d6bae4f7f4))
|
||||||
|
* **test:** LmsStatus is no longer a semigroup ([bf8cd4f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bf8cd4fa899bccd4a37906a4d899aca6ca25d726))
|
||||||
|
|
||||||
|
## [27.4.18](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.14...v27.4.18) (2023-07-17)
|
||||||
|
|
||||||
|
## [27.4.17](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.14...v27.4.17) (2023-07-17)
|
||||||
|
|
||||||
|
## [27.4.16](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.14...v27.4.16) (2023-07-17)
|
||||||
|
|
||||||
|
## [27.4.15](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.14...v27.4.15) (2023-07-17)
|
||||||
|
|
||||||
|
## [27.4.14](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.13...v27.4.14) (2023-07-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** eliminate call to undefined in Esqueleto.Internals ([240c6f8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/240c6f81f81d1872317da01411fa67ec97e3b16d))
|
||||||
|
* **job:** fix [#95](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/95) by implementing queued job deletion for admins ([5b9a554](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5b9a5545457dbe506d20f7362fb6e0d6bae4f7f4))
|
||||||
|
|
||||||
|
## [27.4.13](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.12...v27.4.13) (2023-07-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** background avs synch yielding undefined due to wrong monad ([2e59d3c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2e59d3c2ea4d5017be9b4e578b7da12c4da0e2fa))
|
||||||
|
* **lms:** add safeguard to LmsUserlist dispatch running twice, thus ending LMS prematurely ([a8df40d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a8df40d9f8943f2e0c4e219074486dbbf0eaf0fe))
|
||||||
|
* **lpr:** fix [#96](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/96) by various minor improvements to PrintCenter ([80c632d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/80c632df1ca4871c10cdac1141d87f92a7646cf7))
|
||||||
|
* **tutorial:** fix [#94](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/94) tutorial renaming (de) and template naming ([1ce8f75](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1ce8f75c2d192051929b1a74b17f4e6494961901))
|
||||||
|
|
||||||
|
## [27.4.12](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.11...v27.4.12) (2023-07-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **avs:** attempt to fix avs background jobs ([bbaa42e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bbaa42eefaaae88982b091973adb295cdc0e80ff))
|
||||||
|
* **avs:** avs background synchs and lms userlist result no longer block handler ([0beb0e4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0beb0e4011745ea51906e018c53548bb2f6d978e))
|
||||||
|
* **avs:** fix [#7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/7) by sequencing avs background jobs one after another ([6dc3d8d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6dc3d8d059e132d19c119c5f1de906342fdf6d2c))
|
||||||
|
* **notifications:** direct notifications now respect user triggers ([3e5f271](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3e5f271cacfcc5dbd95aa68a342f56db566f8dee))
|
||||||
|
|
||||||
## [27.4.11](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.10...v27.4.11) (2023-06-20)
|
## [27.4.11](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.10...v27.4.11) (2023-06-20)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -83,6 +83,7 @@ health-check-matching-cluster-config-timeout: "_env:HEALTHCHECK_MATCHING_CLUSTER
|
|||||||
|
|
||||||
synchronise-ldap-users-within: "_env:SYNCHRONISE_LDAP_WITHIN:1209600" # 14 Tage in Sekunden
|
synchronise-ldap-users-within: "_env:SYNCHRONISE_LDAP_WITHIN:1209600" # 14 Tage in Sekunden
|
||||||
synchronise-ldap-users-interval: "_env:SYNCHRONISE_LDAP_INTERVAL:3600" # jede Stunde
|
synchronise-ldap-users-interval: "_env:SYNCHRONISE_LDAP_INTERVAL:3600" # jede Stunde
|
||||||
|
synchronise-ldap-users-expire: "_env:SYNCHRONISE_LDAP_EXPIRE:15897600" # halbes Jahr in Sekunden
|
||||||
|
|
||||||
synchronise-avs-users-within: "_env:SYNCHRONISE_AVS_WITHIN:5702400" # alle 66 Tage
|
synchronise-avs-users-within: "_env:SYNCHRONISE_AVS_WITHIN:5702400" # alle 66 Tage
|
||||||
synchronise-avs-users-interval: "_env:SYNCHRONISE_AVS_INTERVAL:21600" # alle 6 Stunden
|
synchronise-avs-users-interval: "_env:SYNCHRONISE_AVS_INTERVAL:21600" # alle 6 Stunden
|
||||||
@ -90,8 +91,9 @@ synchronise-avs-users-interval: "_env:SYNCHRONISE_AVS_INTERVAL:21600" # alle 6
|
|||||||
study-features-recache-relevance-within: 172800
|
study-features-recache-relevance-within: 172800
|
||||||
study-features-recache-relevance-interval: 293
|
study-features-recache-relevance-interval: 293
|
||||||
|
|
||||||
# Enqueue at specified hour, dequeue 30min later
|
# Enqueue at specified hour, a few minutes later
|
||||||
# qualification-check-hour: 3
|
job-lms-qualifications-enqueue-hour: 16
|
||||||
|
job-lms-qualifications-dequeue-hour: 4
|
||||||
|
|
||||||
log-settings:
|
log-settings:
|
||||||
detailed: "_env:DETAILED_LOGGING:false"
|
detailed: "_env:DETAILED_LOGGING:false"
|
||||||
@ -156,10 +158,12 @@ lms-direct:
|
|||||||
deletion-days: "_env:LMSDELETIONDAYS:7"
|
deletion-days: "_env:LMSDELETIONDAYS:7"
|
||||||
|
|
||||||
avs:
|
avs:
|
||||||
host: "_env:AVSHOST:skytest.fra.fraport.de"
|
host: "_env:AVSHOST:skytest.fra.fraport.de"
|
||||||
port: "_env:AVSPORT:443"
|
port: "_env:AVSPORT:443"
|
||||||
user: "_env:AVSUSER:fradrive"
|
user: "_env:AVSUSER:fradrive"
|
||||||
pass: "_env:AVSPASS:"
|
pass: "_env:AVSPASS:\"0000\""
|
||||||
|
timeout: "_env:AVSTIMEOUT:42"
|
||||||
|
cache-expiry: "_env:AVSCACHEEXPIRY:420"
|
||||||
|
|
||||||
lpr:
|
lpr:
|
||||||
host: "_env:LPRHOST:fravm017173.fra.fraport.de"
|
host: "_env:LPRHOST:fravm017173.fra.fraport.de"
|
||||||
@ -275,8 +279,8 @@ user-defaults:
|
|||||||
max-favourites: 0
|
max-favourites: 0
|
||||||
max-favourite-terms: 2
|
max-favourite-terms: 2
|
||||||
theme: Default
|
theme: Default
|
||||||
date-time-format: "%d %b %y %R"
|
date-time-format: "%d.%m.%Y %R"
|
||||||
date-format: "%d %b %Y"
|
date-format: "%d.%m.%y"
|
||||||
time-format: "%R"
|
time-format: "%R"
|
||||||
download-files: false
|
download-files: false
|
||||||
warning-days: 1209600
|
warning-days: 1209600
|
||||||
|
|||||||
6
fixtest.sh
Executable file
6
fixtest.sh
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
if [[ ! -d .stack-work-test ]]; then
|
||||||
|
mv -vT .stack-work .stack-work-test
|
||||||
|
[[ -d .stack-work-build ]] && mv -vT .stack-work-build .stack-work
|
||||||
|
else
|
||||||
|
echo "Directory .stack-work-test exists already."
|
||||||
|
fi
|
||||||
@ -1,3 +1,3 @@
|
|||||||
SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>
|
SPDX-FileCopyrightText: 2022-2023 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|||||||
24
flake.nix
24
flake.nix
@ -112,8 +112,26 @@
|
|||||||
|
|
||||||
overlays = [
|
overlays = [
|
||||||
(final: prev: let
|
(final: prev: let
|
||||||
pkgs-recent = import nixpkgs-recent { inherit system; };
|
pkgs-recent = import nixpkgs-recent { inherit system; };
|
||||||
in { inherit (pkgs-recent) dockerTools node2nix stack glibcLocalesUtf8 tzdata chromium minio minio-client skopeo; inherit (pkgs-recent.stdenv) fetchurlBoot; })
|
in {
|
||||||
|
inherit (pkgs-recent) dockerTools node2nix glibcLocalesUtf8 tzdata chromium minio minio-client skopeo; inherit (pkgs-recent.stdenv) fetchurlBoot;
|
||||||
|
stack = pkgs.symlinkJoin {
|
||||||
|
inherit (pkgs-recent.stack) name;
|
||||||
|
paths = [pkgs-recent.stack];
|
||||||
|
nativeBuildInputs = [pkgs-recent.makeWrapper];
|
||||||
|
|
||||||
|
postBuild = ''
|
||||||
|
wrapProgram $out/bin/stack \
|
||||||
|
--prefix PATH : "${prev.lib.makeBinPath [pkgs-recent.nix]}" \
|
||||||
|
--add-flags "\
|
||||||
|
--nix \
|
||||||
|
--no-nix-pure \
|
||||||
|
--nix-shell-file=${./stack.nix} \
|
||||||
|
--nix-path=nixpkgs=${nixpkgs} \
|
||||||
|
"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
(import ./nix/maildev)
|
(import ./nix/maildev)
|
||||||
haskell-nix.overlay
|
haskell-nix.overlay
|
||||||
@ -127,7 +145,7 @@
|
|||||||
inherit (pkgs.lib) recursiveUpdate;
|
inherit (pkgs.lib) recursiveUpdate;
|
||||||
in {
|
in {
|
||||||
packages = haskellFlake.packages // {
|
packages = haskellFlake.packages // {
|
||||||
inherit (pkgs) uniworxNodeDependencies uniworxWellKnown uniworxFrontend uniworxDemoDocker uniworxDocker changelogJson;
|
inherit (pkgs) uniworxNodeDependencies uniworxWellKnown uniworxFrontend uniworxTestDocker uniworxDocker changelogJson;
|
||||||
};
|
};
|
||||||
|
|
||||||
apps = haskellFlake.apps // {
|
apps = haskellFlake.apps // {
|
||||||
|
|||||||
@ -301,7 +301,7 @@ export class ExamCorrect {
|
|||||||
users: [user],
|
users: [user],
|
||||||
status: STATUS.LOADING,
|
status: STATUS.LOADING,
|
||||||
};
|
};
|
||||||
if (results && results !== {}) rowInfo.results = results;
|
if (results && Object.keys(results).length > 0) rowInfo.results = results;
|
||||||
if (result !== undefined) rowInfo.result = result;
|
if (result !== undefined) rowInfo.result = result;
|
||||||
this._addRow(rowInfo);
|
this._addRow(rowInfo);
|
||||||
|
|
||||||
|
|||||||
12
is-clean.sh
12
is-clean.sh
@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>
|
# SPDX-FileCopyrightText: 2022-2023 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
@ -13,15 +13,17 @@ if [ -n "$(git status --porcelain)" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$(git rev-parse --abbrev-ref HEAD)" != "master" ]; then
|
branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||||
echo "Not on master" >&2
|
|
||||||
|
if [[ $branch != "master" && $branch != "test" ]]; then
|
||||||
|
echo "Not on master or test" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ourHash=$(git rev-parse HEAD)
|
ourHash=$(git rev-parse HEAD)
|
||||||
theirHash=$(git ls-remote origin -h refs/heads/master | awk '{ print $1; }')
|
theirHash=$(git ls-remote origin -h refs/heads/$branch | awk '{ print $1; }')
|
||||||
|
|
||||||
if [ "$theirHash" != "$ourHash" ]; then
|
if [ "$theirHash" != "$ourHash" ]; then
|
||||||
echo "Local HEAD is not up to date with remote master" >&2
|
echo "Local HEAD is not up to date with remote $branch" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
# SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
|
# SPDX-FileCopyrightText: 2022-24 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
FAQLoginExpired: Mein Passwort ist abgelaufen und muss erneuert werden
|
||||||
FAQNoCampusAccount: Ich habe keine Fraport AG Kennung (Büko-Login); kann ich trotzdem Zugang zum System erhalten?
|
FAQNoCampusAccount: Ich habe keine Fraport AG Kennung (Büko-Login); kann ich trotzdem Zugang zum System erhalten?
|
||||||
FAQForgottenPassword: Ich habe mein Passwort vergessen
|
FAQForgottenPassword: Ich habe mein Passwort vergessen
|
||||||
FAQCampusCantLogin: Ich kann mich mit meiner Fraport AG Kennung (Büko-Login) nicht anmelden
|
FAQCampusCantLogin: Ich kann mich mit meiner Fraport AG Kennung (Büko-Login) nicht anmelden
|
||||||
FAQCourseCorrectorsTutors: Wie kann ich Ausbilder oder Korrektoren für meine Kursart konfigurieren?
|
FAQNotLecturerHowToCreateCourses: Wie kann ich eine neue Kursart anlegen?
|
||||||
FAQNotLecturerHowToCreateCourses: Wie kann ich eine neue Kursart anlegen?
|
|
||||||
FAQExamPoints: Warum kann ich bei meiner Klausur keine Punkte eintragen?
|
|
||||||
FAQInvalidCredentialsAdAccountDisabled: Ich kann mich nicht anmelden und bekomme die Meldung „Benutzereintrag gesperrt“
|
|
||||||
@ -1,11 +1,9 @@
|
|||||||
# SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
|
# SPDX-FileCopyrightText: 2022-24 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
FAQLoginExpired: My password expired
|
||||||
FAQNoCampusAccount: I don't have Fraport AG credentials (Büko login); can I still get access?
|
FAQNoCampusAccount: I don't have Fraport AG credentials (Büko login); can I still get access?
|
||||||
FAQForgottenPassword: I have forgotten my password
|
FAQForgottenPassword: I have forgotten my password
|
||||||
FAQCampusCantLogin: I can't log in using my Fraport AG credentials (Büko login)
|
FAQCampusCantLogin: I can't log in using my Fraport AG credentials (Büko login)
|
||||||
FAQCourseCorrectorsTutors: How can I add instructors or correctors to my course?
|
FAQNotLecturerHowToCreateCourses: How can I create new courses?
|
||||||
FAQNotLecturerHowToCreateCourses: How can I create new courses?
|
|
||||||
FAQExamPoints: Why can't I enter achievements for my exam as points?
|
|
||||||
FAQInvalidCredentialsAdAccountDisabled: I can't log in and am instead given the message “Account disabled”
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
# SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>
|
# SPDX-FileCopyrightText: 2022-24 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ MailTestFormEmail: E-Mail-Adresse
|
|||||||
MailTestFormLanguages: Spracheinstellungen
|
MailTestFormLanguages: Spracheinstellungen
|
||||||
MailRerouteTo dev@Address: Alle Emails werden nicht an die eigentlichen Empfänger versendet, sondern umgeleitet zu _{dev}. Druckaufträge werden generiert, aber nicht zum tatsächlichen Druck gesendet.
|
MailRerouteTo dev@Address: Alle Emails werden nicht an die eigentlichen Empfänger versendet, sondern umgeleitet zu _{dev}. Druckaufträge werden generiert, aber nicht zum tatsächlichen Druck gesendet.
|
||||||
TestDownload: Download-Test
|
TestDownload: Download-Test
|
||||||
BearerTokenUsageWarning: Mit diesem Interface können quesi beliebige Rechte als Tokens kodiert und somit ohne wesentliche weitere Beschränkung frei übertragen werden. Benutzen Sie dieses Interface nur, wenn Sie von einem erfahrenen Entwickler/einer erfahrenen Entwicklerin über die Auswirkungen des konkreten Tokens, dass sie ausstellen möchten, beraten wurden!
|
BearerTokenUsageWarning: Mit diesem Interface können quasi beliebige Rechte als Tokens kodiert und somit ohne wesentliche weitere Beschränkung frei übertragen werden. Benutzen Sie dieses Interface nur, wenn Sie von einem erfahrenen Entwickler/einer erfahrenen Entwicklerin über die Auswirkungen des konkreten Tokens, dass sie ausstellen möchten, beraten wurden!
|
||||||
BearerTokenAuthorityGroups: Token-Authorität (Gruppen)
|
BearerTokenAuthorityGroups: Token-Authorität (Gruppen)
|
||||||
BearerTokenAuthorityGroupsTip: Die primären Benutzer:innen aller angegebenen Gruppen müssen Zugriff auf eine Route haben, damit das Token den Zugriff auf diese Route erlaubt.
|
BearerTokenAuthorityGroupsTip: Die primären Benutzer:innen aller angegebenen Gruppen müssen Zugriff auf eine Route haben, damit das Token den Zugriff auf diese Route erlaubt.
|
||||||
BearerTokenAuthorityGroupMissing: Gruppe wird benötigt
|
BearerTokenAuthorityGroupMissing: Gruppe wird benötigt
|
||||||
@ -67,6 +67,7 @@ BearerTokenExpiresTip: Wird der Ablaufzeitpunkt überschrieben und kein Ablaufze
|
|||||||
BearerTokenOverrideStart: Startzeitpunkt
|
BearerTokenOverrideStart: Startzeitpunkt
|
||||||
BearerTokenOverrideStartTip: Wird kein Startzeitpunkt angegeben, wird bei Verwendung des Tokens nur der Ablaufzeitpunkt überprüft.
|
BearerTokenOverrideStartTip: Wird kein Startzeitpunkt angegeben, wird bei Verwendung des Tokens nur der Ablaufzeitpunkt überprüft.
|
||||||
HeadingAdminTokens: Tokens ausstellen
|
HeadingAdminTokens: Tokens ausstellen
|
||||||
|
UserUnknown: Unbekannter Benutzer:in
|
||||||
|
|
||||||
#templates adminFeautures
|
#templates adminFeautures
|
||||||
StudyFeaturesDegrees: Abschlüsse
|
StudyFeaturesDegrees: Abschlüsse
|
||||||
@ -98,21 +99,64 @@ TestDownloadFromDatabase: Generierung während Download aus Datenbank
|
|||||||
|
|
||||||
ProblemsHeading: Problemübersicht
|
ProblemsHeading: Problemübersicht
|
||||||
ProblemsHeadingDrivers: Fahrberechtigungen
|
ProblemsHeadingDrivers: Fahrberechtigungen
|
||||||
|
ProblemsHeadingNotifications: Benachrichtigungen
|
||||||
|
ProblemsHeadingMisc: Allgemein
|
||||||
ProblemsAvsProblem: Synchronisation mit AVS/MoBaKo komplett fehlgeschlagen
|
ProblemsAvsProblem: Synchronisation mit AVS/MoBaKo komplett fehlgeschlagen
|
||||||
ProblemsDriverSynch n@Int: #{tshow n} Diskrepanzen zwischen AVS und FRADrive
|
ProblemsDriverSynch n@Int: #{n} #{pluralDE n "Diskrepanz" "Diskrepanzen"} zwischen AVS und FRADrive
|
||||||
ProblemsDriverSynch0: Alle Sperrungen von Vorfeld-Fahrberechtigungen 'F' sind im AVS eingetragen
|
ProblemsDriverSynch0: Alle Sperrungen von Vorfeld-Fahrberechtigungen 'F' sind im AVS eingetragen
|
||||||
ProblemsDriverSynch1down: Alle Sperrungen von Rollfeld-Fahrberechtigungen 'R' sind im AVS eingetragen
|
ProblemsDriverSynch1down: Alle Sperrungen von Rollfeld-Fahrberechtigungen 'R' sind im AVS eingetragen
|
||||||
ProblemsDriverSynch1up: Alle gültigen Vorfeld-Fahrberechtigungen 'F' sind im AVS eingetragen
|
ProblemsDriverSynch1up: Alle gültigen Vorfeld-Fahrberechtigungen 'F' sind im AVS eingetragen
|
||||||
ProblemsDriverSynch2: Alle gültigen Rollfeld-Fahrberechtigungen 'R' sind im AVS eingetragen
|
ProblemsDriverSynch2: Alle gültigen Rollfeld-Fahrberechtigungen 'R' sind im AVS eingetragen
|
||||||
ProblemsRDriversHaveFs: Alle Inhaber einer Rollfeld-Fahrberechtigung besitzen auch eine gültige Vorfeld-Fahrberechtigung
|
ProblemsRDriversHaveFs: Alle Inhaber einer Rollfeld-Fahrberechtigung besitzen auch eine gültige Vorfeld-Fahrberechtigung
|
||||||
ProblemsDriversHaveAvsIds: Alle Inhaber einer Fahrberechtigung konnten einer AVS Identifikationsnummer zugeordnet werden
|
ProblemsDriversHaveAvsIds: Alle Inhaber einer Fahrberechtigung konnten einer AVS Identifikationsnummer zugeordnet werden
|
||||||
ProblemsHeadingUsers: Allgemein
|
|
||||||
ProblemsUsersAreReachable: Für alle Benutzer ist eine E-Mail oder postalische Adresse bekannt
|
ProblemsUsersAreReachable: Für alle Benutzer ist eine E-Mail oder postalische Adresse bekannt
|
||||||
ProblemsNoStalePrintJobs n@Integer: Alle Briefversandaufträge der vergangenen #{show n} Tage wurden von der Druckerei bestätigt
|
ProblemsNoStalePrintJobs n@Integer: Alle Briefversandaufträge #{pluralDE n "des vergangenen Tages" ("der vergangenen "<> tshow n <> " Tage")} wurden von der Druckerei bestätigt
|
||||||
|
ProblemsNoBadAPCIds: Alle kürzlich empfangenen Druckauftragsbestätigungen waren gültig
|
||||||
ProblemsUnreachableHeading: Unerreichbare Benutzer
|
ProblemsUnreachableHeading: Unerreichbare Benutzer
|
||||||
ProblemsUnreachableBody: Benutzer ohne E-Mail oder Postadresse, welche z.B. bei ablaufenden Berechtigungen nicht benachrichtigt werden können:
|
ProblemsUnreachableBody: Benutzer ohne E-Mail oder Postadresse, welche z.B. bei ablaufenden Berechtigungen nicht benachrichtigt werden können:
|
||||||
|
ProblemsUnreachableButtons: Synchronisation für Unerreichbare starten
|
||||||
ProblemsRWithoutFHeading: Fahrer mit R ohne F
|
ProblemsRWithoutFHeading: Fahrer mit R ohne F
|
||||||
ProblemsRWithoutFBody: Diese Fahrer sind wegen einer ungültigen Vorfeld-Fahrberechtigung komplett gesperrt, obwohl eine gültige Rollfeld-Fahrberechtigung besteht:
|
ProblemsRWithoutFBody: Diese Fahrer sind wegen einer ungültigen Vorfeld-Fahrberechtigung komplett gesperrt, obwohl eine gültige Rollfeld-Fahrberechtigung besteht:
|
||||||
ProblemsNoAvsIdHeading: Fahrer ohne AVS-Id
|
ProblemsNoAvsIdHeading: Fahrer ohne AVS-Id
|
||||||
ProblemsNoAvsIdBody: Fahrer mit gültiger Fahrberechtigung in FRADrive, welche trotzdem nicht fahren dürfen, da die Fahrberechtigung aufgrund einer unbekannten AVS Id nicht an die Ausweisstelle übermittelt werden konnte:
|
ProblemsNoAvsIdBody: Fahrer mit gültiger Fahrberechtigung in FRADrive, welche trotzdem nicht fahren dürfen, da die Fahrberechtigung aufgrund einer unbekannten AVS Id nicht an die Ausweisstelle übermittelt werden konnte:
|
||||||
ProblemsAvsSynchHeading: Synchronisation AVS Fahrberechtigungen
|
ProblemsAvsSynchHeading: Synchronisation AVS Fahrberechtigungen
|
||||||
|
ProblemsAvsErrorHeading: Fehlermeldungen
|
||||||
|
ProblemsInterfaceSince: Berücksichtigt werden nur Erfolge und Fehler seit
|
||||||
|
ProblemAvsUsrHadR: Momentan gültiges R im AVS
|
||||||
|
|
||||||
|
AdminProblemSolved: Erledigt
|
||||||
|
AdminProblemSolver: Bearbeitet von
|
||||||
|
AdminProblemCreated: Erkannt
|
||||||
|
AdminProblemInfo: Problembeschreibung
|
||||||
|
AdminProblemsSolved n@Int: #{pluralDEeN n "Admin Problem"} als erledigt markiert
|
||||||
|
AdminProblemsReopened n@Int: #{pluralDEeN n "Admin Problem"} erneut eröffnet
|
||||||
|
AdminProblemNewCompany: Neue Firma über AVS automatisch erstellt; prüfen und ggf. Standardansprechpartner eintragen
|
||||||
|
AdminProblemSupervisorNewCompany b@Bool: Standardansprechpartner #{boolText mempty "mit Standardumleitung" b} wechselte zu neuer Firma
|
||||||
|
AdminProblemSupervisorLeftCompany b@Bool: Einziger Standardansprechpartner #{boolText mempty "mit Standardumleitung" b} dieses Fahrers wechselte zu neuer Firma
|
||||||
|
AdminProblemCompanySuperiorChange: Neuer firmenweiter Vorgesetzter.
|
||||||
|
AdminProblemCompanySuperiorNotFound t@Text: Neuer unbekannter firmenweiter Vorgesetzter mit E-Mail #{t}, keine Ansprechpartnerbeziehungen eingerichtet.
|
||||||
|
AdminProblemCompanySuperiorPrevious: Ehemaliger Vorgesetzter:
|
||||||
|
AdminProblemNewlyUnsupervised: Fahrer hat keinen Firmenansprechpartner mehr nach AVS Firmenwechsel zu Firma
|
||||||
|
AdminProblemUser: Betroffener
|
||||||
|
ProblemTableMarkSolved: Als erledigt markieren
|
||||||
|
ProblemTableMarkUnsolved: Erledigt Markierung löschen
|
||||||
|
|
||||||
|
InterfacesOk: Schnittstellen sind ok.
|
||||||
|
InterfacesFail n@Int: #{pluralDEeN n "Schnittstellenproblem"}!
|
||||||
|
InterfaceStatus !ident-ok: Status
|
||||||
|
InterfaceName: Schnittstelle
|
||||||
|
InterfaceLastSynch: Zuletzt
|
||||||
|
InterfaceSubtype: Betreffend
|
||||||
|
InterfaceWrite: Schreibend
|
||||||
|
InterfaceSuccess: Rückmeldung
|
||||||
|
InterfaceInfo: Nachricht
|
||||||
|
InterfaceFreshness: Maximale Zugriffsfrist
|
||||||
|
InterfaceFreshnessTooltip: Zeitspanne innerhalb der ein erneuter erfolgreicher Schnittstellenzugriff erfolgen muss, ohne Warnungen auszulösen
|
||||||
|
ConfigInterfacesHeading: Konfiguration Zugriffsfristen
|
||||||
|
|
||||||
|
IWTActAdd: Hinzufügen/Ändern
|
||||||
|
IWTActDelete: Entfernen
|
||||||
|
InterfaceWarningAdded: Schnittstellenwarnungszeit hinzugefügt oder geändert
|
||||||
|
InterfaceWarningDeleted n@Int: #{pluralDEeN n "Schnittstellenwarnungszeit"} gelöscht
|
||||||
|
InterfaceWarningDisabledEntirely: Alle Fehler ignorieren
|
||||||
|
InterfaceWarningDisabledInterval: Keine Zugriffsfrist
|
||||||
@ -1,4 +1,4 @@
|
|||||||
# SPDX-FileCopyrightText: 2022 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>
|
# SPDX-FileCopyrightText: 2022-24 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
@ -18,10 +18,10 @@ NoNameCandidatesInferred: No new name-mappings inferred
|
|||||||
AllNameIncidencesDeleted: Successfully deleted all name observations
|
AllNameIncidencesDeleted: Successfully deleted all name observations
|
||||||
AllParentIncidencesDeleted: Successfully deleted all parent-relation observations
|
AllParentIncidencesDeleted: Successfully deleted all parent-relation observations
|
||||||
AllStandaloneIncidencesDeleted: Successfully deleted all standalone observations
|
AllStandaloneIncidencesDeleted: Successfully deleted all standalone observations
|
||||||
IncidencesDeleted n: Successfully deleted #{show n} #{pluralEN n "observation" "observations"}
|
IncidencesDeleted n: Successfully deleted #{pluralENsN n "observation"}
|
||||||
RedundantParentCandidatesRemoved n: Successfully removed #{n} rendundant #{pluralEN n "parent-candidate" "parent-candidates"}
|
RedundantParentCandidatesRemoved n: Successfully removed #{n} rendundant #{pluralENs n "parent-candidate"}
|
||||||
RedundantStandaloneCandidatesRemoved n: Successfully removed #{n} rendundant #{pluralEN n "standalone-candidate" "standalone-candidates"}
|
RedundantStandaloneCandidatesRemoved n: Successfully removed #{n} rendundant #{pluralENs n "standalone-candidate"}
|
||||||
ParentCandidatesInferred n: Successfully inferred #{n} field #{pluralEN n "parent-relation" "parent-reliations"}
|
ParentCandidatesInferred n: Successfully inferred #{n} field #{pluralENs n "parent-relation"}
|
||||||
NoParentCandidatesInferred: No new parent-relations inferred
|
NoParentCandidatesInferred: No new parent-relations inferred
|
||||||
StudyDegreeChangeSuccess: Successfully updated degrees
|
StudyDegreeChangeSuccess: Successfully updated degrees
|
||||||
StudyTermsShort: Field shorthand
|
StudyTermsShort: Field shorthand
|
||||||
@ -67,6 +67,7 @@ BearerTokenExpiresTip: If no expiration time is given, the token will not expire
|
|||||||
BearerTokenOverrideStart: Start time
|
BearerTokenOverrideStart: Start time
|
||||||
BearerTokenOverrideStartTip: If no start time is given, only the expiration time will be checked when the token is used.
|
BearerTokenOverrideStartTip: If no start time is given, only the expiration time will be checked when the token is used.
|
||||||
HeadingAdminTokens: Issue tokens
|
HeadingAdminTokens: Issue tokens
|
||||||
|
UserUnknown: User unknown
|
||||||
|
|
||||||
#templates adminfeatures
|
#templates adminfeatures
|
||||||
StudyFeaturesDegrees: Degrees
|
StudyFeaturesDegrees: Degrees
|
||||||
@ -98,21 +99,64 @@ TestDownloadFromDatabase: Generate while streaming from database
|
|||||||
|
|
||||||
ProblemsHeading: Overview Problems
|
ProblemsHeading: Overview Problems
|
||||||
ProblemsHeadingDrivers: Driving Licences
|
ProblemsHeadingDrivers: Driving Licences
|
||||||
|
ProblemsHeadingNotifications: User communication
|
||||||
|
ProblemsHeadingMisc: Miscellaneous
|
||||||
ProblemsAvsProblem: Synchronisation with AVS/MoBaKo failed entirely
|
ProblemsAvsProblem: Synchronisation with AVS/MoBaKo failed entirely
|
||||||
ProblemsDriverSynch n: #{tshow n} mismatches between AVS and FRADrive
|
ProblemsDriverSynch n: #{tshow n} #{pluralEN n "mismatch" "mismatches"} between AVS and FRADrive
|
||||||
ProblemsDriverSynch0: All revocations of apron driving licences 'F' were successfully registered with AVS
|
ProblemsDriverSynch0: All revocations of apron driving licences 'F' were successfully registered with AVS
|
||||||
ProblemsDriverSynch1down: All revocations of maneuvering area driving licences 'R' were successfully registered with AVS
|
ProblemsDriverSynch1down: All revocations of maneuvering area driving licences 'R' were successfully registered with AVS
|
||||||
ProblemsDriverSynch1up: All valid apron driving licences 'F' were successfully registered with AVS
|
ProblemsDriverSynch1up: All valid apron driving licences 'F' were successfully registered with AVS
|
||||||
ProblemsDriverSynch2: All valid maneuvering area driving licences 'R' were successfully registered with AVS
|
ProblemsDriverSynch2: All valid maneuvering area driving licences 'R' were successfully registered with AVS
|
||||||
ProblemsRDriversHaveFs: All driving licence 'R' holders also have a valid 'F' licence
|
ProblemsRDriversHaveFs: All driving licence 'R' holders also have a valid 'F' licence
|
||||||
ProblemsDriversHaveAvsIds: All driving licence holder could be matched with their AVS id
|
ProblemsDriversHaveAvsIds: All driving licence holder could be matched with their AVS id
|
||||||
ProblemsHeadingUsers: Miscellaneous
|
|
||||||
ProblemsUsersAreReachable: Either Email or postal address is known for all users
|
ProblemsUsersAreReachable: Either Email or postal address is known for all users
|
||||||
ProblemsNoStalePrintJobs n: All requests for letter mailing within the last #{show n} days were acknowledged as printed by the airport printing center
|
ProblemsNoStalePrintJobs n: All requests for letter mailing within the last #{pluralENsN n "day"} were acknowledged as printed by the airport printing center
|
||||||
|
ProblemsNoBadAPCIds: All recently received print job ids from Airport Print Center were legit
|
||||||
ProblemsUnreachableHeading: Unreachable Users
|
ProblemsUnreachableHeading: Unreachable Users
|
||||||
ProblemsUnreachableBody: Users without Email nor postal address, who thus cannot be notified about expiring qualifications:
|
ProblemsUnreachableBody: Users without Email nor postal address, who thus cannot be notified about expiring qualifications:
|
||||||
|
ProblemsUnreachableButtons: Start synchronisation for unreachable users only
|
||||||
ProblemsRWithoutFHeading: Drivers having 'R' but not 'F'
|
ProblemsRWithoutFHeading: Drivers having 'R' but not 'F'
|
||||||
ProblemsRWithoutFBody: Drivers without apron driving licence are prohibited from driving, even if they own a valid maneuvering driving licence:
|
ProblemsRWithoutFBody: Drivers without apron driving licence are prohibited from driving, even if they own a valid maneuvering driving licence:
|
||||||
ProblemsNoAvsIdHeading: Drivers without AVS id
|
ProblemsNoAvsIdHeading: Drivers without AVS id
|
||||||
ProblemsNoAvsIdBody: Drivers having a valid apron driving licence within FRADrive only, but who may not drive since a missing AVS id prevents communication of the driving licence to AVS:
|
ProblemsNoAvsIdBody: Drivers having a valid apron driving licence within FRADrive only, but who may not drive since a missing AVS id prevents communication of the driving licence to AVS:
|
||||||
ProblemsAvsSynchHeading: Synchronisation AVS Driving Licences
|
ProblemsAvsSynchHeading: Synchronisation AVS Driving Licences
|
||||||
|
ProblemsAvsErrorHeading: Error Log
|
||||||
|
ProblemsInterfaceSince: Only considering successes and errors since
|
||||||
|
ProblemAvsUsrHadR: Currenlt R valid in AVS
|
||||||
|
|
||||||
|
AdminProblemSolved: Done
|
||||||
|
AdminProblemSolver: Solved by
|
||||||
|
AdminProblemCreated: Recognized
|
||||||
|
AdminProblemInfo: Problem
|
||||||
|
AdminProblemsSolved n: #{pluralENsN n "admin problem"} marked as solved
|
||||||
|
AdminProblemsReopened n: #{pluralENsN n "admin problem"} reopened
|
||||||
|
AdminProblemNewCompany: New company from AVS; verify and add default supervisors
|
||||||
|
AdminProblemSupervisorNewCompany b: Default company supervisor #{boolText mempty "with reroute" b} changed to new company
|
||||||
|
AdminProblemSupervisorLeftCompany b: Only default company supervisor #{boolText mempty "with reroute" b} for this user changed to new company
|
||||||
|
AdminProblemCompanySuperiorChange: New company wide superior.
|
||||||
|
AdminProblemCompanySuperiorNotFound t: Unable to set supervision for new unknown company wide superior having Email #{t}.
|
||||||
|
AdminProblemCompanySuperiorPrevious: Previous superior:
|
||||||
|
AdminProblemNewlyUnsupervised: Driver has no longer a company default supervisor after AVS update at new company
|
||||||
|
AdminProblemUser: Affected
|
||||||
|
ProblemTableMarkSolved: Mark done
|
||||||
|
ProblemTableMarkUnsolved: Reopen as undone
|
||||||
|
|
||||||
|
InterfacesOk: Interfaces are ok.
|
||||||
|
InterfacesFail n: #{pluralENsN n "interface problem"}!
|
||||||
|
InterfaceStatus: Status
|
||||||
|
InterfaceName: Interface
|
||||||
|
InterfaceLastSynch: Last
|
||||||
|
InterfaceSubtype: Affecting
|
||||||
|
InterfaceWrite: Write
|
||||||
|
InterfaceSuccess: Returned
|
||||||
|
InterfaceInfo: Message
|
||||||
|
InterfaceFreshness: Maximum usage period
|
||||||
|
InterfaceFreshnessTooltip: Time period within which the next successful interface access must occur to avoid a warning
|
||||||
|
ConfigInterfacesHeading: Configure interface usage warnings
|
||||||
|
|
||||||
|
IWTActAdd: Add/Edit
|
||||||
|
IWTActDelete: Delete
|
||||||
|
InterfaceWarningAdded: Interface warning time added/changed
|
||||||
|
InterfaceWarningDeleted n: #{pluralENsN n "interface warning time"} deleted
|
||||||
|
InterfaceWarningDisabledEntirely: Ignore all errors
|
||||||
|
InterfaceWarningDisabledInterval: No maximum usage period
|
||||||
@ -20,6 +20,8 @@ UnauthorizedTokenInvalidAuthorityValue: Ihr Authorisierungs-Token basiert auf Re
|
|||||||
UnauthorizedTokenInvalidImpersonation: Ihr Authorisierungs-Token enthält die Anweisung sich als ein Nutzer:in auszugeben, dies ist jedoch nicht allen Benutzer:innen, auf deren Rechten ihr Authorisierungs-Token basiert, erlaubt.
|
UnauthorizedTokenInvalidImpersonation: Ihr Authorisierungs-Token enthält die Anweisung sich als ein Nutzer:in auszugeben, dies ist jedoch nicht allen Benutzer:innen, auf deren Rechten ihr Authorisierungs-Token basiert, erlaubt.
|
||||||
UnauthorizedToken404: Authorisierungs-Tokens können nicht auf Fehlerseiten ausgewertet werden.
|
UnauthorizedToken404: Authorisierungs-Tokens können nicht auf Fehlerseiten ausgewertet werden.
|
||||||
UnauthorizedSupervisor: Sie sind kein Ansprechpartner:in für diesen Benutzer:in.
|
UnauthorizedSupervisor: Sie sind kein Ansprechpartner:in für diesen Benutzer:in.
|
||||||
|
UnauthorizedAnySupervisor: Sie sind kein Ansprechpartner:in.
|
||||||
|
UnauthorizedCompanySupervisor fsh@CompanyShorthand: Sie sind kein Standard Ansprechpartner:in für Firma #{fsh}.
|
||||||
UnauthorizedSiteAdmin: Sie sind nicht System-weiter Administrator:in.
|
UnauthorizedSiteAdmin: Sie sind nicht System-weiter Administrator:in.
|
||||||
UnauthorizedSchoolAdmin: Sie sind nicht als Administrator:in für diesen Bereich eingetragen.
|
UnauthorizedSchoolAdmin: Sie sind nicht als Administrator:in für diesen Bereich eingetragen.
|
||||||
UnauthorizedAdminEscalation: Sie sind nicht Administrator:in für alle Bereiche, für die dieser Nutzer/diese Nutzerin Administrator:in oder Veranstalter:in ist.
|
UnauthorizedAdminEscalation: Sie sind nicht Administrator:in für alle Bereiche, für die dieser Nutzer/diese Nutzerin Administrator:in oder Veranstalter:in ist.
|
||||||
|
|||||||
@ -20,6 +20,8 @@ UnauthorizedTokenInvalidAuthorityValue: The specification of the rights in which
|
|||||||
UnauthorizedTokenInvalidImpersonation: Your authorisation-token contains an instruction to impersonate an user. Not all users on whose rights your token is based however are permitted to do so.
|
UnauthorizedTokenInvalidImpersonation: Your authorisation-token contains an instruction to impersonate an user. Not all users on whose rights your token is based however are permitted to do so.
|
||||||
UnauthorizedToken404: Authorisation-tokens cannot be processed on error pages.
|
UnauthorizedToken404: Authorisation-tokens cannot be processed on error pages.
|
||||||
UnauthorizedSupervisor: You are not a supervisor for the requested user.
|
UnauthorizedSupervisor: You are not a supervisor for the requested user.
|
||||||
|
UnauthorizedAnySupervisor: You are not a supervisor.
|
||||||
|
UnauthorizedCompanySupervisor fsh: You are not a default supervisor for company #{fsh}.
|
||||||
UnauthorizedSiteAdmin: You are no system-wide administrator.
|
UnauthorizedSiteAdmin: You are no system-wide administrator.
|
||||||
UnauthorizedSchoolAdmin: You are no administrator for this department.
|
UnauthorizedSchoolAdmin: You are no administrator for this department.
|
||||||
UnauthorizedAdminEscalation: You aren't an administrator for all departments for which this user is an administrator.
|
UnauthorizedAdminEscalation: You aren't an administrator for all departments for which this user is an administrator.
|
||||||
|
|||||||
@ -2,17 +2,21 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
AvsPersonInfo: AVS Personendaten
|
AvsPersonInfo: AVS Personendaten
|
||||||
AvsPersonId: AVS Personen Id
|
AvsPersonId: AVS Personen Id
|
||||||
AvsPersonNo: AVS Personennummer
|
AvsPersonNo: AVS Personennummer
|
||||||
|
AvsPersonNoNotId: AVS Personennummer dient zur menschlichen Kommunikation mit der Ausweisstelle und darf nicht verwechselt werden mit der maschinell verwendeten AVS Personen Id
|
||||||
|
AvsPersonNoMismatch: AVS Personennummer hat sich geändert und wurde in FRADrive noch nicht aktualisiert
|
||||||
|
AvsPersonNoDiffers: Es sind derzeit zwei verschiedene AVS Personennummern zugeordnet. Bitte einen Administrator kontaktieren.
|
||||||
AvsCardNo: Ausweiskartennummer
|
AvsCardNo: Ausweiskartennummer
|
||||||
AvsFirstName: Vorname
|
AvsFirstName: Vorname
|
||||||
AvsLastName: Nachname
|
AvsLastName: Nachname
|
||||||
|
AvsPrimaryCompany: Primäre Firma
|
||||||
AvsInternalPersonalNo: Personalnummer (nur Fraport AG)
|
AvsInternalPersonalNo: Personalnummer (nur Fraport AG)
|
||||||
AvsVersionNo: Versionsnummer
|
AvsVersionNo: Versionsnummer
|
||||||
|
AvsQueryNeeded: Benötigt Verbindung zum AVS.
|
||||||
AvsQueryEmpty: Bitte mindestens ein Anfragefeld ausfüllen!
|
AvsQueryEmpty: Bitte mindestens ein Anfragefeld ausfüllen!
|
||||||
AvsQueryStatusInvalid t@Text: Nur numerische IDs eingeben, durch Komma getrennt! Erhalten: #{show t}
|
AvsQueryStatusInvalid t@Text: Nur numerische IDs eingeben, durch Komma getrennt! Erhalten: #{show t}
|
||||||
AvsLicence: Fahrberechtigung
|
AvsLicence: Fahrberechtigung
|
||||||
AvsPersonNoNotId: AVS Personennummer dient zur menschlichen Kommunikation mit der Ausweisstelle und darf nicht verwechselt werden mit der maschinell verwendeten AVS Personen Id
|
|
||||||
AvsTitleLicenceSynch: Abgleich Fahrberechtigungen zwischen AVS und FRADrive
|
AvsTitleLicenceSynch: Abgleich Fahrberechtigungen zwischen AVS und FRADrive
|
||||||
BtnAvsRevokeUnknown: Fahrberechtigungen im AVS sofort entziehen
|
BtnAvsRevokeUnknown: Fahrberechtigungen im AVS sofort entziehen
|
||||||
BtnAvsImportUnknown: AVS Daten unbekannter Personen importieren
|
BtnAvsImportUnknown: AVS Daten unbekannter Personen importieren
|
||||||
@ -27,11 +31,33 @@ RevokeFraDriveLicences alic@AvsLicence n@Int: _{alic} in FRADrive entzogen für
|
|||||||
RevokeUnknownLicencesOk: AVS Fahrberechtigungen unbekannter Fahrer wurden gesperrt
|
RevokeUnknownLicencesOk: AVS Fahrberechtigungen unbekannter Fahrer wurden gesperrt
|
||||||
RevokeUnknownLicencesFail: Nicht alle AVS Fahrberechtigungen unbekannter Fahrer konnten entzogen werden, siehe Log für Details
|
RevokeUnknownLicencesFail: Nicht alle AVS Fahrberechtigungen unbekannter Fahrer konnten entzogen werden, siehe Log für Details
|
||||||
AvsCommunicationError: AVS Schnittstelle lieferte einen unerwarteten Fehler.
|
AvsCommunicationError: AVS Schnittstelle lieferte einen unerwarteten Fehler.
|
||||||
|
AvsCommunicationTimeout: AVS Schnittstelle antwortete nicht.
|
||||||
LicenceTableChangeAvs: Im AVS ändern
|
LicenceTableChangeAvs: Im AVS ändern
|
||||||
LicenceTableGrantFDrive: In FRADrive erteilen
|
LicenceTableGrantFDrive: In FRADrive erteilen
|
||||||
LicenceTableRevokeFDrive: In FRADrive entziehen
|
LicenceTableRevokeFDrive: In FRADrive entziehen
|
||||||
TableAvsActiveCards: Gültige Ausweise
|
TableAvsActiveCards: Gültige Ausweise
|
||||||
|
TableAvsCardValid: Aktuell gültig
|
||||||
|
TableAvsCardIssueDate: Ausgestellt am
|
||||||
|
TableAvsCardValidTo: Gültig bis
|
||||||
|
AvsCardAreas: Ausweiszusätze
|
||||||
|
AvsCardColor: Ausweisfarbe
|
||||||
AvsCardColorGreen: Grün
|
AvsCardColorGreen: Grün
|
||||||
AvsCardColorBlue: Blau
|
AvsCardColorBlue: Blau
|
||||||
AvsCardColorRed: Rot
|
AvsCardColorRed: Rot
|
||||||
AvsCardColorYellow: Gelb
|
AvsCardColorYellow: Gelb
|
||||||
|
LastAvsSynchronisation: Letzte AVS-Synchronisation
|
||||||
|
LastAvsSyncedBefore: Letzte AVS-Synchronisation vor
|
||||||
|
LastAvsSynchError: Letzte AVS-Fehlermeldung
|
||||||
|
|
||||||
|
AvsInterfaceUnavailable: AVS Schnittstelle nicht richtig konfiguriert oder antwortet nicht
|
||||||
|
AvsUserUnassociated user@UserDisplayName: AVS Id unbekannt für Nutzer #{user}
|
||||||
|
AvsUserUnknownByAvs api@AvsPersonId: AVS kennt Id #{tshow api} nicht (mehr)
|
||||||
|
AvsUserAmbiguous api@AvsPersonId: AVS Id #{tshow api} ist nicht eindeutig
|
||||||
|
AvsStatusSearchEmpty: AVS lieferte keine Ausweisinformationen
|
||||||
|
AvsPersonSearchEmpty: AVS Suche lieferte leeres Ergebnis
|
||||||
|
AvsPersonSearchAmbiguous: AVS Suche lieferte mehrere uneindeutige Ergebnisse
|
||||||
|
AvsSetLicencesFailed reason@Text: Setzen der Fahrlizenz im AVS fehlgeschlagen. Grund: #{reason}
|
||||||
|
AvsIdMismatch api1@AvsPersonId api2@AvsPersonId: AVS Suche für Id #{tshow api1} lieferte stattdessen Id #{tshow api2}
|
||||||
|
AvsUserCreationFailed api@AvsPersonId: Für AVS Id #{tshow api} konnte kein neuer Benutzer angelegt werden, da es eine gemeinsame Id (z.B. Personalnummer) mit einem existierenden, aber verschiedenen Nutzer gibt.
|
||||||
|
AvsCardsEmpty: AVS Suche lieferte keinerlei Ausweiskarten
|
||||||
|
AvsCurrentData: Alle angezeigte Daten wurden kürzlich direkt über die AVS Schnittstelle abgerufen.
|
||||||
@ -1,18 +1,23 @@
|
|||||||
# SPDX-FileCopyrightText: 2022 Steffen Jost <jost@tcs.ifi.lmu.de>
|
# SPDX-FileCopyrightText: 2022 Steffen Jost <jost@tcs.ifi.lmu.de>
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
AvsPersonInfo: AVS Person Info
|
AvsPersonInfo: AVS person info
|
||||||
AvsPersonId: AVS Person Id
|
AvsPersonId: AVS person id
|
||||||
AvsPersonNo: AVS Person Number
|
AvsPersonNo: AVS person number
|
||||||
|
AvsPersonNoNotId: AVS person number is used in human communication only and must not be mistaken for the AVS personen id used in machine communications
|
||||||
|
AvsPersonNoMismatch: AVS person number has changed and was not yet updated in FRADrive
|
||||||
|
AvsPersonNoDiffers: There are currently two differing AVS person numbers associated with this user. Please contact an administrator to resolve this.
|
||||||
AvsCardNo: Card number
|
AvsCardNo: Card number
|
||||||
AvsFirstName: First name
|
AvsFirstName: First name
|
||||||
AvsLastName: Last name
|
AvsLastName: Last name
|
||||||
|
AvsPrimaryCompany: Primary company
|
||||||
AvsInternalPersonalNo: Personnel number (Fraport AG only)
|
AvsInternalPersonalNo: Personnel number (Fraport AG only)
|
||||||
AvsVersionNo: Version number
|
AvsVersionNo: Version number
|
||||||
|
AvsQueryNeeded: AVS connection required.
|
||||||
AvsQueryEmpty: At least one query field must be filled!
|
AvsQueryEmpty: At least one query field must be filled!
|
||||||
AvsQueryStatusInvalid t: Numeric IDs only, comma seperated! #{show t}
|
AvsQueryStatusInvalid t: Numeric IDs only, comma seperated! #{show t}
|
||||||
AvsLicence: Driving Licence
|
AvsLicence: Driving Licence
|
||||||
AvsPersonNoNotId: AVS person number is used in human communication only and must not be mistaken for the AVS personen id used in machine communications
|
|
||||||
AvsTitleLicenceSynch: Synchronisation driving licences between AVS and FRADrive
|
AvsTitleLicenceSynch: Synchronisation driving licences between AVS and FRADrive
|
||||||
BtnAvsRevokeUnknown: Revoke AVS driving licences for unknown persons immediately
|
BtnAvsRevokeUnknown: Revoke AVS driving licences for unknown persons immediately
|
||||||
BtnAvsImportUnknown: Import AVS data for unknown persons
|
BtnAvsImportUnknown: Import AVS data for unknown persons
|
||||||
@ -27,11 +32,33 @@ RevokeFraDriveLicences alic@AvsLicence n@Int: _{alic} revoked in FRADrive for #{
|
|||||||
RevokeUnknownLicencesOk: AVS driving licences of unknown drivers revoked
|
RevokeUnknownLicencesOk: AVS driving licences of unknown drivers revoked
|
||||||
RevokeUnknownLicencesFail: Not all AVS driving licences of unknown drivers could be revoked, see log for details
|
RevokeUnknownLicencesFail: Not all AVS driving licences of unknown drivers could be revoked, see log for details
|
||||||
AvsCommunicationError: AVS interface returned an unexpected error.
|
AvsCommunicationError: AVS interface returned an unexpected error.
|
||||||
|
AvsCommunicationTimeout: AVS interface returned no response within timeout limit.
|
||||||
LicenceTableChangeAvs: Change in AVS
|
LicenceTableChangeAvs: Change in AVS
|
||||||
LicenceTableGrantFDrive: Grant in FRADrive
|
LicenceTableGrantFDrive: Grant in FRADrive
|
||||||
LicenceTableRevokeFDrive: Revoke in FRADrive
|
LicenceTableRevokeFDrive: Revoke in FRADrive
|
||||||
TableAvsActiveCards: Valid Cards
|
TableAvsActiveCards: Valid Cards
|
||||||
|
TableAvsCardValid: Currently valid
|
||||||
|
TableAvsCardIssueDate: Issued
|
||||||
|
TableAvsCardValidTo: Valid to
|
||||||
|
AvsCardAreas: Card areas
|
||||||
|
AvsCardColor: Color
|
||||||
AvsCardColorGreen: Green
|
AvsCardColorGreen: Green
|
||||||
AvsCardColorBlue: Blue
|
AvsCardColorBlue: Blue
|
||||||
AvsCardColorRed: Red
|
AvsCardColorRed: Red
|
||||||
AvsCardColorYellow: Yellow
|
AvsCardColorYellow: Yellow
|
||||||
|
LastAvsSynchronisation: Last AVS synchronisation
|
||||||
|
LastAvsSyncedBefore: Last AVS synchronisation before
|
||||||
|
LastAvsSynchError: Last AVS Error
|
||||||
|
|
||||||
|
AvsInterfaceUnavailable: AVS interface was not configured correctly or does not respond
|
||||||
|
AvsUserUnassociated user: AVS id unknown for user #{user}
|
||||||
|
AvsUserUnknownByAvs api: AVS reports id #{tshow api} as unknown (or no longer known)
|
||||||
|
AvsUserAmbiguous api: Multiple matching users found for #{tshow api}
|
||||||
|
AvsStatusSearchEmpty: AVS returned no card information
|
||||||
|
AvsPersonSearchEmpty: AVS search returned empty result
|
||||||
|
AvsPersonSearchAmbiguous: AVS search returned more than one result
|
||||||
|
AvsSetLicencesFailed reason: Set driving licence within AVS failed. Reason: #{reason}
|
||||||
|
AvsIdMismatch api1 api2: AVS search for id #{tshow api1} returned id #{tshow api2} instead
|
||||||
|
AvsUserCreationFailed api@AvsPersonId: No new user could be created for AVS Id #{tshow api}, since an existing user shares at least one id presumed as unique
|
||||||
|
AvsCardsEmpty: AVS search returned no id cards
|
||||||
|
AvsCurrentData: All shown data has been recently received via the AVS interface.
|
||||||
@ -70,6 +70,10 @@ CourseInvalidInput: Eingaben bitte korrigieren.
|
|||||||
CourseEditTitle: Kursart editieren/anlegen
|
CourseEditTitle: Kursart editieren/anlegen
|
||||||
CourseEditOk tid@TermId ssh@SchoolId csh@CourseShorthand: Kursart #{tid}-#{ssh}-#{csh} wurde erfolgreich geändert.
|
CourseEditOk tid@TermId ssh@SchoolId csh@CourseShorthand: Kursart #{tid}-#{ssh}-#{csh} wurde erfolgreich geändert.
|
||||||
CourseEditDupShort tid@TermId ssh@SchoolId csh@CourseShorthand: Kursart #{tid}-#{ssh}-#{csh} konnte nicht geändert werden: Es gibt bereits einen andere Kursart mit dem selben Kürzel oder Titel in diesem Jahr und Bereich.
|
CourseEditDupShort tid@TermId ssh@SchoolId csh@CourseShorthand: Kursart #{tid}-#{ssh}-#{csh} konnte nicht geändert werden: Es gibt bereits einen andere Kursart mit dem selben Kürzel oder Titel in diesem Jahr und Bereich.
|
||||||
|
CourseEditQualificationFail: Eine Qualifikation konnte uas unbekanntem Grund nicht mit diesem Kurs assoziert werden.
|
||||||
|
CourseEditQualificationFailRights qsh@QualificationShorthand ssh@SchoolId: Qualifikation #{qsh} konnte nicht mit diesem Kurs assoziert werden, da Ihre Berechtigungen für Bereich #{ssh} dazu nicht ausreichen.
|
||||||
|
CourseEditQualificationFailExists: Diese Qualifikation ist bereits assoziert
|
||||||
|
CourseEditQualificationFailOrder: Diese Sortierpriorität existiert bereits
|
||||||
CourseLecturer: Kursverwalter:in
|
CourseLecturer: Kursverwalter:in
|
||||||
MailSubjectParticipantInvitation tid@TermId ssh@SchoolId csh@CourseShorthand: [#{tid}-#{ssh}-#{csh}] Einladung zur Kursartteilnahme
|
MailSubjectParticipantInvitation tid@TermId ssh@SchoolId csh@CourseShorthand: [#{tid}-#{ssh}-#{csh}] Einladung zur Kursartteilnahme
|
||||||
CourseParticipantInviteHeading courseName@Text: Einladung zum Kursartteilnahmer für #{courseName}
|
CourseParticipantInviteHeading courseName@Text: Einladung zum Kursartteilnahmer für #{courseName}
|
||||||
@ -81,21 +85,21 @@ CourseSubmissionGroup: Feste Abgabegruppe
|
|||||||
SubmissionGroupEmptyIsUnsetTip: Leer lassen um Benutzer:innen aus den jeweiligen Abgabegruppen ersatzlos zu entfernen
|
SubmissionGroupEmptyIsUnsetTip: Leer lassen um Benutzer:innen aus den jeweiligen Abgabegruppen ersatzlos zu entfernen
|
||||||
CourseParticipantsRegisterHeading: Kursartteilnehmer:innen hinzufügen
|
CourseParticipantsRegisterHeading: Kursartteilnehmer:innen hinzufügen
|
||||||
CourseParticipantsRegisterActionAddParticipants: Personen zur Kursart anmelden
|
CourseParticipantsRegisterActionAddParticipants: Personen zur Kursart anmelden
|
||||||
CourseParticipantsRegisterActionAddTutorialMembers: Personen zur Kursart und Kursgruppe anmelden
|
CourseParticipantsRegisterActionAddTutorialMembers: Personen zur Kursart und Kurs anmelden
|
||||||
CourseParticipantsRegisterUsersField: Zur Kursart anzumeldende Personen
|
CourseParticipantsRegisterUsersField: Zur Kursart anzumeldende Personen
|
||||||
CourseParticipantsRegisterUsersFieldTip: Bitte Ausweiskartennummer inklusive Punkt, Fraport Personalnummer oder Email angeben. Mehrere Personen bitte mit Komma oder Leerzeichen trennen.
|
CourseParticipantsRegisterUsersFieldTip: Bitte Ausweiskartennummer inklusive Punkt, Fraport Personalnummer oder Email angeben. Mehrere Personen bitte mit Komma oder Leerzeichen trennen.
|
||||||
CourseParticipantsRegisterTutorialOption: Kursartteilnehmer:innen zu Kursgruppe anmelden?
|
CourseParticipantsRegisterTutorialOption: Kursartteilnehmer:innen zu Kurs anmelden?
|
||||||
CourseParticipantsRegisterTutorialField: Kursgruppe
|
CourseParticipantsRegisterTutorialField: Kurs
|
||||||
CourseParticipantsRegisterTutorialFieldTip: Ist aktuell keine Kursgruppe mit diesem Namen vorhanden, wird eine neue erstellt. Ist bereits eine Kursgruppe mit diesem Namen vorhanden, werden die Kursartteilnehmenden dieser hinzugefügt.
|
CourseParticipantsRegisterTutorialFieldTip: Ist aktuell keine Kurs mit diesem Namen vorhanden, wird eine neue erstellt. Ist bereits eine Kurs mit diesem Namen vorhanden, werden die Kursartteilnehmenden dieser hinzugefügt.
|
||||||
CourseParticipantsRegisterNoneGiven: Es wurden keine anzumeldenden Personen angegeben!
|
CourseParticipantsRegisterNoneGiven: Es wurden keine anzumeldenden Personen angegeben!
|
||||||
CourseParticipantsRegisterNotFoundInAvs n@Int: Zu #{n} #{pluralDE n "Angabe konnte keine übereinstimmende Person" "Angaben konnten keine übereinstimmenden Personen"} im AVS gefunden werden
|
CourseParticipantsRegisterNotFoundInAvs n@Int: Zu #{n} #{pluralDE n "Angabe konnte keine übereinstimmende Person" "Angaben konnten keine übereinstimmenden Personen"} im AVS gefunden werden
|
||||||
CourseParticipantsRegisterTutorialFirstDayTip: Wenn ein neuer Kurs gemäß einer Vorlage erstellt wird, werden die Zeiten gemäß dem Starttag angepasst
|
CourseParticipantsRegisterTutorialFirstDayTip: Wenn ein neuer Kurs gemäß einer Vorlage erstellt wird, werden die Zeiten gemäß dem Starttag angepasst
|
||||||
|
|
||||||
CourseParticipantsInvited n@Int: #{n} #{pluralDE n "Einladung" "Einladungen"} per E-Mail verschickt
|
CourseParticipantsInvited n@Int: #{n} #{pluralDE n "Einladung" "Einladungen"} per E-Mail verschickt
|
||||||
CourseParticipantsAlreadyRegistered n@Int: #{n} #{pluralDE n "Teinehmer:in" "Teilnehmer:innen"} #{pluralDE n "ist" "sind"} bereits zur Kursart angemeldet
|
CourseParticipantsAlreadyRegistered n@Int: #{n} #{pluralDE n "Teinehmer:in" "Teilnehmer:innen"} #{pluralDE n "ist" "sind"} bereits zur Kursart angemeldet
|
||||||
CourseParticipantsAlreadyTutorialMember n@Int: #{n} #{pluralDE n "Teinehmer:in" "Teilnehmer:innen"} #{pluralDE n "ist" "sind"} bereits in dieser Kursgruppe angemeldet
|
CourseParticipantsAlreadyTutorialMember n@Int: #{n} #{pluralDE n "Teinehmer:in" "Teilnehmer:innen"} #{pluralDE n "ist" "sind"} bereits in dieser Kurs angemeldet
|
||||||
CourseParticipantsRegistered n@Int: #{n} #{pluralDE n "Teinehmer:in" "Teilnehmer:innen"} erfolgreich zur Kursart angemeldet
|
CourseParticipantsRegistered n@Int: #{n} #{pluralDE n "Teinehmer:in" "Teilnehmer:innen"} erfolgreich zur Kursart angemeldet
|
||||||
CourseParticipantsRegisteredTutorial n@Int: #{n} #{pluralDE n "Teinehmer:in" "Teilnehmer:innen"} erfolgreich zur Kursgruppe angemeldet
|
CourseParticipantsRegisteredTutorial n@Int: #{n} #{pluralDE n "Teinehmer:in" "Teilnehmer:innen"} erfolgreich zum Kurs angemeldet
|
||||||
CourseParticipantsRegisterConfirmationHeading: Teilnehmer:innen hinzufügen
|
CourseParticipantsRegisterConfirmationHeading: Teilnehmer:innen hinzufügen
|
||||||
CourseParticipantsRegisterUnnecessary: Alle angeforderten Anmeldungen sind bereits vorhanden. Es wurden keine Änderungen vorgenommen.
|
CourseParticipantsRegisterUnnecessary: Alle angeforderten Anmeldungen sind bereits vorhanden. Es wurden keine Änderungen vorgenommen.
|
||||||
CourseParticipantsRegisterConfirmInvalid: Ungültiges Bestätigungsformular!
|
CourseParticipantsRegisterConfirmInvalid: Ungültiges Bestätigungsformular!
|
||||||
|
|||||||
@ -70,8 +70,12 @@ CourseInvalidInput: Invalid input
|
|||||||
CourseEditTitle: Edit/Create course
|
CourseEditTitle: Edit/Create course
|
||||||
CourseEditOk tid ssh csh: Successfully edited course type #{tid}-#{ssh}-#{csh}
|
CourseEditOk tid ssh csh: Successfully edited course type #{tid}-#{ssh}-#{csh}
|
||||||
CourseEditDupShort tid ssh csh: Could not edit course type #{tid}-#{ssh}-#{csh}. Another course type with the same shorthand or title already exists for the given year and school.
|
CourseEditDupShort tid ssh csh: Could not edit course type #{tid}-#{ssh}-#{csh}. Another course type with the same shorthand or title already exists for the given year and school.
|
||||||
|
CourseEditQualificationFail: A qualifikation could not be associated with this course for unknown reasons.
|
||||||
|
CourseEditQualificationFailRights qsh ssh: Qualification #{qsh} could not be associated with this course, due to your insufficient rights for department #{ssh}.
|
||||||
|
CourseEditQualificationFailExists: This qualification is already associated
|
||||||
|
CourseEditQualificationFailOrder: This sort order priority is used already
|
||||||
CourseLecturer: Course administrator
|
CourseLecturer: Course administrator
|
||||||
MailSubjectParticipantInvitation tid@TermId ssh@SchoolId csh@CourseShorthand: [#{tid}-#{ssh}-#{csh}] Invitaion to join the course
|
MailSubjectParticipantInvitation tid ssh csh: [#{tid}-#{ssh}-#{csh}] Invitaion to join the course
|
||||||
CourseParticipantInviteHeading courseName: Invitation to enrol for #{courseName}
|
CourseParticipantInviteHeading courseName: Invitation to enrol for #{courseName}
|
||||||
CourseParticipantInviteExplanation: You were invited to be a participant of a course.
|
CourseParticipantInviteExplanation: You were invited to be a participant of a course.
|
||||||
CourseParticipantInviteField: Email addresses to invite
|
CourseParticipantInviteField: Email addresses to invite
|
||||||
|
|||||||
78
messages/uniworx/categories/firm/de-de-formal.msg
Normal file
78
messages/uniworx/categories/firm/de-de-formal.msg
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2023-24 Steffen Jost <s.jost@fraport.de>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
FirmSuperDefault: Standardansprechpartner
|
||||||
|
FirmSuperForeign: Firmenfremde Ansprechpartner
|
||||||
|
FirmSuperIrregular: Irreguläre Ansprechpartner
|
||||||
|
FirmAssociates: Firmenangehörige
|
||||||
|
FirmContact: Firmenkontakt
|
||||||
|
FirmEmail: Allgemeine Email
|
||||||
|
FirmAddress: Postanschrift
|
||||||
|
FirmDefaultPreferenceInfo: Diese Voreinstellungen gelten nur für neue Firmenangehörige
|
||||||
|
FirmAction: Firmenweite Aktion
|
||||||
|
FirmActionInfo: Betrifft alle Firmenangehörigen unter Ihrer Aufsicht.
|
||||||
|
FirmActNotify: Mitteilung versenden
|
||||||
|
FirmActResetSupervision: Ansprechpartner für alle Firmenangehörigen zurücksetzen
|
||||||
|
FirmActResetSuperKeep: Bisherige Ansprechpartner der Firmenangehörigen zusätzlich beibehalten?
|
||||||
|
FirmActRemoveSupers: Alle rein firmenbezogenen Ansprechpartnerbeziehungen für diese Personen entfernen?
|
||||||
|
FirmActResetMutualSupervision: Ansprechpartner beaufsichtigen sich gegenseitig
|
||||||
|
FirmActResetSupersKeepAll: Alle behalten
|
||||||
|
FirmActResetSupersRemoveAps: Nur Standardansprechpartner entfernen
|
||||||
|
FirmActResetSupersRemoveAll: Alle entfernen
|
||||||
|
FirmActAddSupervisors: Ansprechpartner hinzufügen
|
||||||
|
FirmActAddSupersEmpty: Es konnten keine Ansprechpartner hinzugefügt werden
|
||||||
|
FirmActAddSupersSet n@Int64 postal@(Maybe Bool): #{n} Standardansprechpartner geändert #{maybeBoolMessage postal "" "und auf Briefversand geschaltet" "und Benachrichtigungen per Email gesetzt"}, aber nicht nicht aktiviert.
|
||||||
|
RemoveSupervisors ndef@Int64: #{ndef} Standardansprechpartner entfernt.
|
||||||
|
FirmActChangeContactUser: Kontaktinformationen von allen Firmenangehörigen ändern
|
||||||
|
FirmActChangeContactFirm: Kontaktinformationen der Firma ändern
|
||||||
|
FirmActChangeContactFirmInfo: Firmenkontaktinformationen werden nur für neue Firmenangehörige verwendet, für die sonst keine Kontaktinformationen vorliegen.
|
||||||
|
FirmActChangeContactFirmResult: Firmenkontaktinformationen geändert. Betrifft nur neue Firmenangehörige ohne eigene Kontaktinformationen
|
||||||
|
FirmUserActNotify: Mitteilung versenden
|
||||||
|
FirmUserActResetSupervision: Ansprechpartner auf Firmenstandard zurücksetzen
|
||||||
|
FirmUserActSetSupervisor: Ansprechpartner ändern
|
||||||
|
FirmUserActChangeContact: Kontaktinformationen für ausgewählte Firmenangehörige ändern
|
||||||
|
FirmUserActChangeDetails: Firmenassoziation bearbeiten
|
||||||
|
FirmUserActRemove: Firmenassoziation entfernen
|
||||||
|
FirmUserActMkSuper: Zum Firmenansprechpartner ernennen
|
||||||
|
FirmUserActChangeDetailsResult n@Int64 t@Int64: Firmenassoziation von #{n}/#{t} #{pluralDE n "Firmenangehörigen" "Firmenangehörige"} wurden aktualisiert
|
||||||
|
FirmUserActChangeResult n@Int64 t@Int64: Benachrichtigungseinstellung für #{n}/#{t} #{pluralDE n "Firmenangehörigen" "Firmenangehörige"} wurden geändert
|
||||||
|
FirmUserActRemoveResult uc@Int64: #{uc} #{pluralDE uc "Firmenassoziation" "Firmenassoziationen"} entfernt.
|
||||||
|
FirmRemoveSupervision sup@Int64 sub@Int64: #{noneMoreDE sup "" (tshow sup <> " Ansprechpartnerbeziehungen wegen entferntem Ansprechpartner gelöscht. ")} #{noneOneMoreDE sub "Keine Ansprechpartnerbeziehung" "Eine Ansprechpartnerbeziehung" (tshow sup <> " Ansprechpartnerbeziehungen")} wegen entferntem Angesprochenem gelöscht.
|
||||||
|
FirmNewSupervisor: Neue individuelle Ansprechpartner hinzufügen
|
||||||
|
FirmSetSupervisor: Existierende Ansprechpartner hinzufügen
|
||||||
|
FirmSetSupersReport nusr@Int64 nspr@Int64 nrem@Int64: Für #{nusr} Firmenangehörige wurden #{nspr} individuelle Ansprechpartner eingetragen#{bool "." (" und " <> tshow nrem <> " individuelle Ansprechpartnerbeziehungen gelöscht.") (nrem >0)}
|
||||||
|
FirmResetSupervision rem@Int64 set@Int64: #{tshow set} Ansprechpartner gesetzt#{bool mempty (", " <> tshow rem <> " zuvor gelöscht") (rem > 0)}
|
||||||
|
FirmSuperActNotify: Mitteilung versenden
|
||||||
|
FirmSuperActSwitchSuper: Standard Firmenansprechpartner abändern
|
||||||
|
FirmSuperActSwitchSuperInfo: Betrifft keine firmenfremden Ansprechpartner und ändert keine aktiven individuellen Ansprechpartnerbeziehungen. Gegebenfalls im Anschluss die Funktion "Ansprechpartner auf Firmenstandard zurücksetzen" nutzen.
|
||||||
|
FirmSuperActRMSuperDef: Firmenansprechpartner entfernen
|
||||||
|
FirmSuperActRMSuperActive: Aktive Ansprechpartnerbeziehungen innerhalb dieser Firma beenden?
|
||||||
|
FirmsNotification: Firmen E-Mail versenden
|
||||||
|
FirmNotification fsh@CompanyShorthand: E-Mail an #{fsh} senden
|
||||||
|
FirmsNotificationTitle: Firmen benachrichtigen
|
||||||
|
FirmNotificationTitle fsh@CompanyShorthand: #{fsh} benachrichtigen
|
||||||
|
FilterSupervisor: Hat aktiven Ansprechpartner
|
||||||
|
FilterSupervisorCompany fsh@CompanyShorthand: Hat aktiven Ansprechpartner, #{fsh} der angehört
|
||||||
|
FilterSupervisorForeign fsh@CompanyShorthand: Hat aktiven Ansprechpartner, der selbst nicht #{fsh} angehört
|
||||||
|
FilterForeignSupervisor: Hat firmenfremde Ansprechpartner
|
||||||
|
FilterIsForeignSupervisee: Ist Ansprechpartner für Firmenfremde
|
||||||
|
FilterFirmExtern: Externe Firma
|
||||||
|
FilterFirmExternTooltip: Hat die Firma eine Postanschrift im AVS?
|
||||||
|
FilterFirmPrimary: Ist primäre Firma in FRADrive
|
||||||
|
FilterHasQualification: Hat Firmenangehörige mit aktuell gültiger Qualifikation
|
||||||
|
FirmSupervisorOf fsh@CompanyShorthand: Ansprechpartner #{fsh} angehörig
|
||||||
|
FirmSupervisorIndependent: Ansprechpartner ohne jegliche Firmenzugehörigkeit
|
||||||
|
FirmEmployeeOf fsh@CompanyShorthand: Firmenangehörige #{fsh}
|
||||||
|
NoCompanySelected: Bitte wählen Sie mindestens eine Firma aus.
|
||||||
|
TableIsDefaultSupervisor: Standardansprechpartner
|
||||||
|
TableSuperior: Vorgesetzter
|
||||||
|
TableIsDefaultReroute: Standardumleitung
|
||||||
|
FormFieldPostal: Benachrichtigungseinstellung
|
||||||
|
FormFieldPostalTip: Gilt für alle Benachrichtigungen an diese Person, nicht nur für Umleitungen an diesen Ansprechpartner
|
||||||
|
FirmSupervisionKeyData: Kennzahlen Ansprechpartner
|
||||||
|
CompanyUserPriority: Firmenpriorität
|
||||||
|
CompanyUserPriorityTip: Firmenpriorität ist lediglich relativ zu anderen Firmenassoziation der Person
|
||||||
|
CompanyUserUseCompanyAddress: Verwendet Firmenkontaktaddresse
|
||||||
|
CompanyUserUseCompanyAddressTip: sofern im Benutzer keine Postanschrift hinterlegt ist
|
||||||
|
CompanyUserUseCompanyPostalError: Postalische Adresse muss leer bleiben, damit die Firmenanschrift genutzt wird!
|
||||||
78
messages/uniworx/categories/firm/en-eu.msg
Normal file
78
messages/uniworx/categories/firm/en-eu.msg
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2023-24 Steffen Jost <s.jost@fraport.de>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
FirmSuperDefault: Default supervisor
|
||||||
|
FirmSuperForeign: External supervisor
|
||||||
|
FirmSuperIrregular: Irregular supervisor
|
||||||
|
FirmAssociates: Company associated users
|
||||||
|
FirmContact: Company Contact
|
||||||
|
FirmEmail: General company email
|
||||||
|
FirmAddress: Postal address
|
||||||
|
FirmDefaultPreferenceInfo: Default setting for new company associates only
|
||||||
|
FirmAction: Companywide action
|
||||||
|
FirmActionInfo: Affects alle company associates under your supervision.
|
||||||
|
FirmActNotify: Send message
|
||||||
|
FirmActResetSupervision: Reset supervisors for all company associates
|
||||||
|
FirmActResetSuperKeep: Additionally keep existing supervisors of company associates?
|
||||||
|
FirmActRemoveSupers: Terminate all company related supervisonships?
|
||||||
|
FirmActResetMutualSupervision: Supervisors supervise each other
|
||||||
|
FirmActResetSupersKeepAll: Keep all
|
||||||
|
FirmActResetSupersRemoveAps: Remove default supervisors only
|
||||||
|
FirmActResetSupersRemoveAll: Remove all
|
||||||
|
FirmActAddSupervisors: Add supervisors
|
||||||
|
FirmActAddSupersEmpty: No supervisors added
|
||||||
|
FirmActAddSupersSet n postal: #{n} default company supervisors changed #{maybeBoolMessage postal "" "and switched to postal notifications" "and switched to email notifications"}, but not yet activated.
|
||||||
|
RemoveSupervisors ndef: #{ndef} default supervisors removed.
|
||||||
|
FirmActChangeContactUser: Change contact data for all company associates
|
||||||
|
FirmActChangeContactFirm: Change company contact data
|
||||||
|
FirmActChangeContactFirmInfo: The company contact data is only used for new company associates that would habe no contact information of their own otherwise.
|
||||||
|
FirmActChangeContactFirmResult: Company contact data changed, affecting future company associates without contact information only
|
||||||
|
FirmUserActNotify: Send message
|
||||||
|
FirmUserActResetSupervision: Reset supervisors to company default
|
||||||
|
FirmUserActSetSupervisor: Change supervision
|
||||||
|
FirmUserActChangeContact: Change contact data for selected company associates
|
||||||
|
FirmUserActChangeDetails: Edit company association
|
||||||
|
FirmUserActRemove: Delete company association
|
||||||
|
FirmUserActMkSuper: Mark as company supervisor
|
||||||
|
FirmUserActChangeDetailsResult n t: #{n}/#{t} #{pluralENs n "company association"} updated
|
||||||
|
FirmUserActChangeResult n t: Notification settings changed for #{n}/#{t} company #{pluralENs n "associate"}
|
||||||
|
FirmUserActRemoveResult uc: #{pluralENsN uc "Company association"} deleted.
|
||||||
|
FirmRemoveSupervision sup sub: #{noneMoreEN sup "" ((pluralENsN sup "supervision") <> " removed due to eliminated supervisors.")} #{noneMoreEN sub "No supervision" (pluralENsN sub "supervision")} removed due to eliminated supervisees.
|
||||||
|
FirmNewSupervisor: Appoint new individual supervisors
|
||||||
|
FirmSetSupervisor: Add existing supervisors
|
||||||
|
FirmSetSupersReport nusr nspr nrem: #{nspr} individual supervisors set for #{nusr} company associates#{bool "." (" and " <> tshow nrem <> " other individual supervisions terminated.") (nrem >0)}
|
||||||
|
FirmResetSupervision rem set: #{tshow set} supervisors set#{bool mempty (", " <> tshow rem <> " deleted before") (rem > 0)}
|
||||||
|
FirmSuperActNotify: Send message
|
||||||
|
FirmSuperActSwitchSuper: Change default company supervisor
|
||||||
|
FirmSuperActSwitchSuperInfo: Does not affect company-external supervisors and does not change any active individual supervisions. Additionally use reset action, if desired.
|
||||||
|
FirmSuperActRMSuperDef: Remove default supervisor
|
||||||
|
FirmSuperActRMSuperActive: Terminate active supervisions within this company?
|
||||||
|
FirmsNotification: Send company notification e-mail
|
||||||
|
FirmNotification fsh: Send e-mail to #{fsh}
|
||||||
|
FirmsNotificationTitle: Company notification
|
||||||
|
FirmNotificationTitle fsh@CompanyShorthand: #{fsh} notification
|
||||||
|
FilterSupervisor: Has active supervisor
|
||||||
|
FilterSupervisorCompany fsh: Has active company supervisor belonging to #{fsh}
|
||||||
|
FilterSupervisorForeign fsh: Has active supervisor not belonging to #{fsh}
|
||||||
|
FilterForeignSupervisor: Has company-external supervisors
|
||||||
|
FilterIsForeignSupervisee: Supervisor for company external users
|
||||||
|
FilterFirmExtern: External company
|
||||||
|
FilterFirmExternTooltip: i.e. is a postal address registered within AVS?
|
||||||
|
FilterFirmPrimary: Is primary company in FRADrive
|
||||||
|
FilterHasQualification: Has company associates with currently valid qualification
|
||||||
|
FirmSupervisorOf fsh@CompanyShorthand: Supervisors belonging to #{fsh}
|
||||||
|
FirmSupervisorIndependent: Independent supervisors
|
||||||
|
FirmEmployeeOf fsh@CompanyShorthand: #{fsh} associated users
|
||||||
|
NoCompanySelected: Select at least one company, please.
|
||||||
|
TableIsDefaultSupervisor: Default supervisor
|
||||||
|
TableSuperior: Superior
|
||||||
|
TableIsDefaultReroute: Default reroute
|
||||||
|
FormFieldPostal: Notification type
|
||||||
|
FormFieldPostalTip: Affects all notifications to this person, not just reroutes to this supervisor
|
||||||
|
FirmSupervisionKeyData: Supervision key data
|
||||||
|
CompanyUserPriority: Company priority
|
||||||
|
CompanyUserPriorityTip: Company priority is relative to other company associations for a user
|
||||||
|
CompanyUserUseCompanyAddress: Use company postal address
|
||||||
|
CompanyUserUseCompanyAddressTip: if and only if the postal address of the user is empty
|
||||||
|
CompanyUserUseCompanyPostalError: Individual postal address must left empty for the company address to be used!
|
||||||
@ -3,12 +3,13 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
HealthReport: Instanz-Zustand
|
HealthReport: Instanz-Zustand
|
||||||
HealthMatchingClusterConfig: Cluster-geteilte Konfiguration ist aktuell
|
HealthCheckMatchingClusterConfig: Cluster-geteilte Konfiguration ist aktuell
|
||||||
HealthHTTPReachable: Cluster kann an der erwarteten URL über HTTP erreicht werden
|
HealthCheckHTTPReachable: Cluster kann an der erwarteten URL über HTTP erreicht werden
|
||||||
HealthLDAPAdmins: Anteil der Administrator:innen mit LDAP Authentifizierung, welche tatsächlich im LDAP-Verzeichnis gefunden werden können
|
HealthCheckLDAPAdmins: Anteil der Administrator:innen mit LDAP Authentifizierung, welche tatsächlich im LDAP-Verzeichnis gefunden werden können
|
||||||
HealthSMTPConnect: SMTP-Server kann erreicht werden
|
HealthCheckSMTPConnect: SMTP-Server kann erreicht werden
|
||||||
HealthWidgetMemcached: Memcached-Server liefert Widgets korrekt aus
|
HealthCheckWidgetMemcached: Memcached-Server liefert Widgets korrekt aus
|
||||||
HealthActiveJobExecutors: Anteil der job-workers, die neue Befehle annehmen
|
HealthCheckActiveJobExecutors: Anteil der job-workers, die neue Befehle annehmen
|
||||||
|
HealthCheckDoesFlush: Abgearbeitete Jobs werden aufgeräumt
|
||||||
InstanceIdentification: Instanz-Identifikation
|
InstanceIdentification: Instanz-Identifikation
|
||||||
InstanceId: Instanz-Nummer
|
InstanceId: Instanz-Nummer
|
||||||
ClusterId: Cluster-Nummer
|
ClusterId: Cluster-Nummer
|
||||||
@ -3,12 +3,13 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
HealthReport: Health report
|
HealthReport: Health report
|
||||||
HealthMatchingClusterConfig: Cluster config matches
|
HealthCheckMatchingClusterConfig: Cluster config matches
|
||||||
HealthHTTPReachable: Cluster can be reached under the expected URL via HTTP
|
HealthCheckHTTPReachable: Cluster can be reached under the expected URL via HTTP
|
||||||
HealthLDAPAdmins: Proportion of administrators with LDAP authentication that were actually found in the LDAP directory
|
HealthCheckLDAPAdmins: Proportion of administrators with LDAP authentication that were actually found in the LDAP directory
|
||||||
HealthSMTPConnect: SMTP server is reachable
|
HealthCheckSMTPConnect: SMTP server is reachable
|
||||||
HealthWidgetMemcached: Memcached server is serving widgets correctly
|
HealthCheckWidgetMemcached: Memcached server is serving widgets correctly
|
||||||
HealthActiveJobExecutors: Proportion of job workers accepting new jobs
|
HealthCheckActiveJobExecutors: Proportion of job workers accepting new jobs
|
||||||
|
HealthCheckDoesFlush: Executed jobs are removed
|
||||||
InstanceIdentification: Instance identification
|
InstanceIdentification: Instance identification
|
||||||
InstanceId: Instance id
|
InstanceId: Instance id
|
||||||
ClusterId: Cluster id
|
ClusterId: Cluster id
|
||||||
|
|||||||
@ -15,7 +15,6 @@ ResetPassword: FRADrive-Passwort ändern bzw. setzen
|
|||||||
MailSubjectChangeUserDisplayEmail: E-Mail-Adresse in FRADrive verwenden
|
MailSubjectChangeUserDisplayEmail: E-Mail-Adresse in FRADrive verwenden
|
||||||
MailIntroChangeUserDisplayEmail displayEmail@UserEmail: Der oben genannte Benutzer/Die oben genannte Benutzerin möchte „#{displayEmail}“ als E-Mail-Adresse in FRADrive verwenden. Wenn Sie diese Aktion nicht selbst ausgelöst haben, ignorieren Sie diese Mitteilung bitte!
|
MailIntroChangeUserDisplayEmail displayEmail@UserEmail: Der oben genannte Benutzer/Die oben genannte Benutzerin möchte „#{displayEmail}“ als E-Mail-Adresse in FRADrive verwenden. Wenn Sie diese Aktion nicht selbst ausgelöst haben, ignorieren Sie diese Mitteilung bitte!
|
||||||
MailTitleChangeUserDisplayEmail displayName@Text: #{displayName} möchte diese E-Mail-Adresse in FRADrive verwenden
|
MailTitleChangeUserDisplayEmail displayName@Text: #{displayName} möchte diese E-Mail-Adresse in FRADrive verwenden
|
||||||
CommCourseSubject: Kursartmitteilung
|
|
||||||
InvitationAcceptDecline: Einladung annehmen/ablehnen
|
InvitationAcceptDecline: Einladung annehmen/ablehnen
|
||||||
InvitationFromTip displayName@Text: Sie erhalten diese Einladung, weil #{displayName} ihren Versand in FRADrive ausgelöst hat.
|
InvitationFromTip displayName@Text: Sie erhalten diese Einladung, weil #{displayName} ihren Versand in FRADrive ausgelöst hat.
|
||||||
InvitationFromTipAnonymous: Sie erhalten diese Einladung, weil ein nicht eingeloggter Benutzer/eine nichteingeloggte Benutzerin ihren Versand in FRADrive ausgelöst hat.
|
InvitationFromTipAnonymous: Sie erhalten diese Einladung, weil ein nicht eingeloggter Benutzer/eine nichteingeloggte Benutzerin ihren Versand in FRADrive ausgelöst hat.
|
||||||
|
|||||||
@ -15,7 +15,6 @@ ResetPassword: Reselt FRADrive password
|
|||||||
MailSubjectChangeUserDisplayEmail: Set email address in FRADrive
|
MailSubjectChangeUserDisplayEmail: Set email address in FRADrive
|
||||||
MailIntroChangeUserDisplayEmail displayEmail: The user mentioned above wants to set “#{displayEmail}” as their own email address. If you have not caused this email to be sent, please ignore it!
|
MailIntroChangeUserDisplayEmail displayEmail: The user mentioned above wants to set “#{displayEmail}” as their own email address. If you have not caused this email to be sent, please ignore it!
|
||||||
MailTitleChangeUserDisplayEmail displayName: #{displayName} wants to set this email address as their own in FRADrive
|
MailTitleChangeUserDisplayEmail displayName: #{displayName} wants to set this email address as their own in FRADrive
|
||||||
CommCourseSubject: Course type message
|
|
||||||
InvitationAcceptDecline: Accept/Decline invitation
|
InvitationAcceptDecline: Accept/Decline invitation
|
||||||
InvitationFromTip displayName: You are receiving this invitation because #{displayName} has caused it to be sent from within FRADrive.
|
InvitationFromTip displayName: You are receiving this invitation because #{displayName} has caused it to be sent from within FRADrive.
|
||||||
InvitationFromTipAnonymous: You are receiving this invitiation because an user who didn't log in has caused it to be send from within FRADrive.
|
InvitationFromTipAnonymous: You are receiving this invitiation because an user who didn't log in has caused it to be send from within FRADrive.
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
PJActAcknowledge: Druck und Versand bestätigen
|
PJActAcknowledge: Druck und Versand bestätigen
|
||||||
PJActReprint: Erneut drucken über APC
|
PJActReprint: Erneut drucken über APC
|
||||||
|
PJActReprintIgnoreReroute: Drucken auch bei aktiver Mail-Umleitung erzwingen
|
||||||
PrintJobName: Bezeichnung
|
PrintJobName: Bezeichnung
|
||||||
PrintJobFilename: Dateiname
|
PrintJobFilename: Dateiname
|
||||||
PrintJobId !ident-ok: Id
|
PrintJobId !ident-ok: Id
|
||||||
@ -17,6 +18,7 @@ PrintJobAcknowledgeFailed: Keine Druckaufträge bestätigt aufgrund zwischenzeit
|
|||||||
PrintJobAcknowledgeQuestion n@Int d@Text: #{n} #{pluralDE n "Druckauftrag" "Druckaufräge"} vom #{d} als gedruckt und versendet bestätigen?
|
PrintJobAcknowledgeQuestion n@Int d@Text: #{n} #{pluralDE n "Druckauftrag" "Druckaufräge"} vom #{d} als gedruckt und versendet bestätigen?
|
||||||
PrintJobAcknowledgements: Versanddatum von Briefen an
|
PrintJobAcknowledgements: Versanddatum von Briefen an
|
||||||
PrintRecipient: Empfänger
|
PrintRecipient: Empfänger
|
||||||
|
PrintAffected: Betroffener
|
||||||
PrintSender !ident-ok: Sender
|
PrintSender !ident-ok: Sender
|
||||||
PrintCourse: Kursarten
|
PrintCourse: Kursarten
|
||||||
PrintQualification: Qualifikation
|
PrintQualification: Qualifikation
|
||||||
@ -24,4 +26,7 @@ PrintPDF !ident-ok: PDF
|
|||||||
PrintManualRenewal: Vorfeldführerschein Renewal-Brief testweise versenden
|
PrintManualRenewal: Vorfeldführerschein Renewal-Brief testweise versenden
|
||||||
PrintLmsUser: E‑Learning Id
|
PrintLmsUser: E‑Learning Id
|
||||||
PrintJobs: Druckaufräge
|
PrintJobs: Druckaufräge
|
||||||
PrintLetterType: Brieftypkürzel
|
PrintLetterType: Brieftypkürzel
|
||||||
|
|
||||||
|
MCActDummy: Platzhalter
|
||||||
|
CCActDummy: Platzhalter
|
||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
PJActAcknowledge: Acknowledge printing and mailing
|
PJActAcknowledge: Acknowledge printing and mailing
|
||||||
PJActReprint: Print again via APC
|
PJActReprint: Print again via APC
|
||||||
|
PJActReprintIgnoreReroute: Force printing to APC, even if mail-reroute-to option is active
|
||||||
PrintJobName: Description
|
PrintJobName: Description
|
||||||
PrintJobFilename: Filename
|
PrintJobFilename: Filename
|
||||||
PrintJobId: Id
|
PrintJobId: Id
|
||||||
@ -17,6 +18,7 @@ PrintJobAcknowledgeFailed: No print-jobs acknowledged, due to intermediate chang
|
|||||||
PrintJobAcknowledgeQuestion n d: Mark #{n} #{pluralENs n "print-job"} issued on #{d} as printed and mailed already?
|
PrintJobAcknowledgeQuestion n d: Mark #{n} #{pluralENs n "print-job"} issued on #{d} as printed and mailed already?
|
||||||
PrintJobAcknowledgements: Sent-dates for Letter to
|
PrintJobAcknowledgements: Sent-dates for Letter to
|
||||||
PrintRecipient: Recipient
|
PrintRecipient: Recipient
|
||||||
|
PrintAffected: Affetcted
|
||||||
PrintSender: Sender
|
PrintSender: Sender
|
||||||
PrintCourse: Course type
|
PrintCourse: Course type
|
||||||
PrintQualification: Qualification
|
PrintQualification: Qualification
|
||||||
@ -24,4 +26,7 @@ PrintPDF: PDF
|
|||||||
PrintManualRenewal: Manual sending of an apron driver's licence renewal letter
|
PrintManualRenewal: Manual sending of an apron driver's licence renewal letter
|
||||||
PrintLmsUser: E‑learning id
|
PrintLmsUser: E‑learning id
|
||||||
PrintJobs: Print jobs
|
PrintJobs: Print jobs
|
||||||
PrintLetterType: Letter type shorthand
|
PrintLetterType: Letter type shorthand
|
||||||
|
|
||||||
|
MCActDummy: Placeholder
|
||||||
|
CCActDummy: Placeholder
|
||||||
@ -8,32 +8,45 @@ QualificationDescription: Beschreibung
|
|||||||
QualificationValidIndicator: Gültigkeit
|
QualificationValidIndicator: Gültigkeit
|
||||||
QualificationValidDuration: Gültigkeitsdauer
|
QualificationValidDuration: Gültigkeitsdauer
|
||||||
QualificationAuditDuration: Aufbewahrung Audit Log
|
QualificationAuditDuration: Aufbewahrung Audit Log
|
||||||
|
QualificationAuditDurationTooltip n@Int: Optionaler Zeitraum zur Löschung von E‑Learning Daten. Hinweis: Der E‑Learning Server kann seine anonymisierten Daten schon früher löschen, aber spätestens #{n} Tage nach Abschluss.
|
||||||
|
QualificationAuditDurationReuseError: Diese Qualifikation nutzt das E‑Learning einer anderen Qualifikation, für die derzeit keinen Löschzeitraum konfiguriert wurde.
|
||||||
QualificationRefreshWithin: Erneurerungszeitraum
|
QualificationRefreshWithin: Erneurerungszeitraum
|
||||||
QualificationRefreshWithinTooltip: Optionaler Zeitraum vor Ablauf für automatischen Start des E‑Learnings und Versand einer Benachrichtigung per Brief oder Email
|
QualificationRefreshWithinTooltip: Optionaler Zeitraum vor Ablauf für eine Benachrichtigung per Email. Bei aktiviertem automatischem E‑Learning wird dieses gestartet und die Benachrichtigung erfolgt per Brief oder Email.
|
||||||
QualificationRefreshReminder: 2. Erinnerung
|
QualificationRefreshReminder: Zweite Erinnerung
|
||||||
QualificationRefreshReminderTooltip: Optionaler Zeitraum vor Ablauf zur Versendung einer zweiten Erinnerung per Brief oder Email mit identischen Zugangsdaten, sofern in diesem Zeitraum vor Ablauf noch keine Ablaufbenachrichtigung versendet wurde
|
QualificationRefreshReminderTooltip: Optionaler Zeitraum vor Ablauf zur Versendung einer zweiten Erinnerung per Brief oder Email mit identischen E‑Learning Zugangsdaten, sofern die Qualifikation noch gültig und das E‑Learning noch offen ist.
|
||||||
QualificationElearningStart: Wird das E‑Learning automatisch gestartet?
|
QualificationElearningStart: Wird das E‑Learning automatisch gestartet?
|
||||||
|
QualificationElearningRenew: Verlängert ein erfolgreiches E‑Learning die Qualifikation automatisch um die reguläre Gültigkeitsdauer?
|
||||||
|
QualificationElearningLimit: Ist die Anzahl der E‑Learning Versuche limitiert?
|
||||||
|
QualificationElearningLimitMax n@Int: Maximal #{n} Versuche
|
||||||
|
QualificationElearningNoLimit: Nicht limitiert
|
||||||
QualificationExpiryNotification: Ungültigkeitsbenachrichtigung?
|
QualificationExpiryNotification: Ungültigkeitsbenachrichtigung?
|
||||||
QualificationExpiryNotificationTooltip: Nutzer werden benachrichtigt, wenn die Qualifikation ungültig wird, sofern der jeweilige Nutzer in seinen Benutzereinstellungen diese Art Benachrichtigung aktiviert hat.
|
QualificationExpiryNotificationTooltip: Nutzer werden benachrichtigt, wenn die Qualifikation ungültig wird, sofern der jeweilige Nutzer in seinen Benutzereinstellungen diese Art Benachrichtigung aktiviert hat.
|
||||||
TableQualificationCountActive: Aktive
|
TableQualificationCountActive: Aktive
|
||||||
TableQualificationCountActiveTooltip: Anzahl Personen mit momentan gültiger Qualifikation
|
TableQualificationCountActiveTooltip: Anzahl Personen mit momentan gültiger Qualifikation
|
||||||
TableQualificationCountTotal: Gesamt
|
TableQualificationCountTotal: Gesamt
|
||||||
|
TableQualificationLmsReuses: LMS nutzt
|
||||||
|
TableQualificationLmsReusesTooltip: Diese Qualifikation hat kein eigenes E‑Learning, sondern wird über das E‑Learning der angegebenen Qualifikation abgewickelt.
|
||||||
TableQualificationIsAvsLicence: AVS
|
TableQualificationIsAvsLicence: AVS
|
||||||
TableQualificationIsAvsLicenceTooltip: Unter welchem Namen wird diese Qualifikation mit dem Ausweisverwaltungssystem (AVS) synchronisiert? Betrifft nur Benutzer mit AVS PersonenID.
|
TableQualificationIsAvsLicenceTooltip: Unter welchem Namen wird diese Qualifikation mit dem Ausweisverwaltungssystem (AVS) synchronisiert? Betrifft nur Benutzer mit AVS PersonenID.
|
||||||
TableQualificationSapExport: SAP
|
TableQualificationSapExport: SAP
|
||||||
TableQualificationSapExportTooltip: Wird die Qualifikation an das SAP übermittelt? Betrifft nur Benutzer mit Fraport Personalnummer.
|
TableQualificationSapExportTooltip: Wird die Qualifikation an das SAP übermittelt? Betrifft nur Benutzer mit Fraport Personalnummer.
|
||||||
LmsQualificationValidUntil: Gültig bis
|
LmsQualificationValidUntil: Gültig bis
|
||||||
TableQualificationLastRefresh: Zuletzt erneuert
|
TableQualificationLastRefresh: Zuletzt erneuert
|
||||||
TableQualificationLastNotified: Letzte Benachrichtigung
|
TableQualificationLastNotified: Letzte Benachrichtigung über erfolgte Gültigkeitsänderung
|
||||||
|
TableQualificationLastNotifiedTooltip: Hier werden ausschließlich Benachrichtigungen berücksichtigt, die über einen bereits erfolgten Ablauf/Entzug/Wiedererteilung informieren. Dies ignoriert insbesondere reguläre Verlängerung, z.B. durch E-Learning.
|
||||||
TableQualificationFirstHeld: Erstmalig
|
TableQualificationFirstHeld: Erstmalig
|
||||||
TableQualificationBlockedDue: Entzogen
|
TableQualificationBlockedDue: Entzug
|
||||||
TableQualificationBlockedTooltip: Wann wurde die Qualifikation vorübergehend außer Kraft gesetzt und warum wurde dies veranlasst?
|
TableQualificationBlockedTooltip: Wann wurde die Qualifikation vorübergehend außer Kraft gesetzt und warum wurde dies veranlasst?
|
||||||
TableQualificationBlockedTooltipSimple: Wann wurde die Qualifikation aus besonderem Grund wiederrufen?
|
TableQualificationBlockedTooltipSimple: Falls die Qualifikation aus besonderem Grund vorzeitig widerrufen wurde, so wird das Datum des Widerrufs angezeigt
|
||||||
|
InfoQualificationBlockStatus: Besteht aktuell ein Entzug? Falsch bedeutet, dass ein Entzug zuletzt aufgehoben wurde
|
||||||
|
InfoQualificationBlockFrom: Datum der letzten Änderungen eines Entzugs oder der Aufhebung eines Entzugs
|
||||||
TableQualificationNoRenewal: Auslaufend
|
TableQualificationNoRenewal: Auslaufend
|
||||||
TableQualificationNoRenewalTooltip: Es wird keine Benachrichtigung mehr versendet, wenn diese Qualifikation ablaufen sollte. Die Qualifikation kann noch weiterhin gültig sein.
|
TableQualificationNoRenewalTooltip: Es wird keine Benachrichtigung mehr versendet, wenn diese Qualifikation ablaufen sollte. Die Qualifikation kann noch weiterhin gültig sein.
|
||||||
QualificationScheduleRenewalTooltip: Wird eine Benachrichtigung versendet, falls diese Qualikation bald ablaufen sollte?
|
QualificationScheduleRenewalTooltip: Wird eine Benachrichtigung versendet, falls diese Qualikation bald ablaufen sollte?
|
||||||
QualificationUserNoRenewal: Läuft ohne Benachrichtigung aus
|
QualificationUserNoRenewal: Läuft ohne Benachrichtigung aus
|
||||||
QualificationUserNone: Für diese Person sind keine Qualifikationen registriert.
|
QualificationUserNone: Für diese Person sind keine Qualifikationen registriert.
|
||||||
|
QualificationGrantReason: Erteilungsbegründung
|
||||||
|
QualificationRenewReason: Verlängerungsbegründung
|
||||||
QualificationBlockReason: Entzugsbegründung
|
QualificationBlockReason: Entzugsbegründung
|
||||||
QualificationBlockNotify: Benachrichtigung verschicken
|
QualificationBlockNotify: Benachrichtigung verschicken
|
||||||
QualificationBlockRemoveSupervisor: Alle Ansprechpartner löschen
|
QualificationBlockRemoveSupervisor: Alle Ansprechpartner löschen
|
||||||
@ -41,11 +54,14 @@ QualificationExpired: Ungültig seit
|
|||||||
LmsUser: Inhaber
|
LmsUser: Inhaber
|
||||||
LmsURL: Link E‑Learning
|
LmsURL: Link E‑Learning
|
||||||
TableLmsEmail: E‑Mail
|
TableLmsEmail: E‑Mail
|
||||||
TableLmsIdent: E-Learning Benutzer
|
TableLmsIdent: E‑Learning Benutzer
|
||||||
TableLmsElearning: E‑Learning
|
TableLmsElearning: E‑Learning
|
||||||
|
TableLmsElearningRenews: Automatische Verlängerung
|
||||||
|
TableLmsElearningLimit: Maximale Versuche
|
||||||
TableLmsPin: E‑Learning Passwort
|
TableLmsPin: E‑Learning Passwort
|
||||||
TableLmsResetPin: E-Learning Passwort zurücksetzen?
|
TableLmsResetPin: E‑Learning Passwort zurücksetzen?
|
||||||
TableLmsDatePin: E-Learning Passwort erstellt
|
TableLmsDatePin: E‑Learning Passwort erstellt
|
||||||
|
TableLmsDate: Datum
|
||||||
TableLmsDelete: Löschen?
|
TableLmsDelete: Löschen?
|
||||||
TableLmsStaff: Interner Mitarbeiter?
|
TableLmsStaff: Interner Mitarbeiter?
|
||||||
TableLmsStarted: Begonnen
|
TableLmsStarted: Begonnen
|
||||||
@ -57,11 +73,12 @@ TableLmsStatus: Status E‑Learning
|
|||||||
TableLmsStatusTooltip mbMonth@(Maybe Int): Zeigt #{maybeToMessage "bis zu " (fmap (flip pluralDEeN "Monat") mbMonth) " nach Abschluss"} den letzten Zustand eines E‑Learnings an:
|
TableLmsStatusTooltip mbMonth@(Maybe Int): Zeigt #{maybeToMessage "bis zu " (fmap (flip pluralDEeN "Monat") mbMonth) " nach Abschluss"} den letzten Zustand eines E‑Learnings an:
|
||||||
TableLmsStatusDay: Datum letzte Statusänderung E‑Learning
|
TableLmsStatusDay: Datum letzte Statusänderung E‑Learning
|
||||||
TableLmsSuccess: Bestanden
|
TableLmsSuccess: Bestanden
|
||||||
TableLmsFailed: Gesperrt
|
TableLmsLock: Gesperrt
|
||||||
|
TableLmsResetTries: E‑Learning Versuche zurücksetzen
|
||||||
LmsStatusBlocked: Durchgefallen wegen zu vieler Fehlversuche
|
LmsStatusBlocked: Durchgefallen wegen zu vieler Fehlversuche
|
||||||
LmsStatusExpired: Durchgefallen nach Fristablauf
|
LmsStatusExpired: Durchgefallen nach Fristablauf
|
||||||
LmsStatusSuccess: E#{nonBreakableDash}Learning bestanden
|
LmsStatusSuccess: E#{nonBreakableDash}Learning bestanden
|
||||||
LmsStatusPlanned: E#{nonBreakableDash}Learning wird gerade eröffnet (nur für Admin sichtbar)
|
LmsStatusPlanned: E#{nonBreakableDash}Learning wird gerade noch eröffnet (nur für Admin sichtbar)
|
||||||
LmsStatusDelay: Hinweis: Statusänderung können in seltenen Fällen mehrere Stunden bis zur Anzeige benötigen.
|
LmsStatusDelay: Hinweis: Statusänderung können in seltenen Fällen mehrere Stunden bis zur Anzeige benötigen.
|
||||||
FilterLmsValid: Aktuell gültig
|
FilterLmsValid: Aktuell gültig
|
||||||
FilterLmsRenewal: Erneuerung anstehend
|
FilterLmsRenewal: Erneuerung anstehend
|
||||||
@ -69,19 +86,20 @@ FilterLmsNotified: Benachrichtigt
|
|||||||
FilterLmsNotificationDue: Benachrichtigung erforderlich
|
FilterLmsNotificationDue: Benachrichtigung erforderlich
|
||||||
CsvColumnLmsIdent: E‑Learning Identifikator, einzigartig pro Qualifikation und Teilnehmer
|
CsvColumnLmsIdent: E‑Learning Identifikator, einzigartig pro Qualifikation und Teilnehmer
|
||||||
CsvColumnLmsPin: Passwort E#{nonBreakableDash}Learning Zugang
|
CsvColumnLmsPin: Passwort E#{nonBreakableDash}Learning Zugang
|
||||||
CsvColumnLmsResetPin: Wird das E-Learning Passwort bei der nächsten Synchronisation zurückgesetzt?
|
CsvColumnLmsResetPin: Wird das E‑Learning Passwort bei der nächsten Synchronisation zurückgesetzt?
|
||||||
CsvColumnLmsDelete: Wird der Identifikator in der E‑Learning Plattform bei der nächsten Synchronisation gelöscht?
|
CsvColumnLmsDelete: Wird der Identifikator in der E‑Learning Plattform bei der nächsten Synchronisation gelöscht?
|
||||||
CsvColumnLmsStaff: Handelt es sich um einen internen Mitarbeiter? (Aus historischen Gründen, wird momentan ignoriert.)
|
CsvColumnLmsStaff: Handelt es sich um einen internen Mitarbeiter? (Aus historischen Gründen, wird momentan ignoriert.)
|
||||||
CsvColumnLmsSuccess: Zeitstempel der erfolgreichen Teilnahme (UTC)
|
CsvColumnLmsSuccess: Zeitstempel der erfolgreichen Teilnahme
|
||||||
CsvColumnLmsFailed: User was blocked by LMS, usually due to too many attempts
|
CsvColumnLmsDate: Datum des E‑Learning Ereignisses
|
||||||
LmsUserlistInsert: Neuer LMS User
|
CsvColumnLmsResetTries: Anzahl der bisher verbrauchten E‑Learning Prüfungsversuche zurücksetzen
|
||||||
LmsUserlistUpdate: LMS User aktualisierung
|
CsvColumnLmsLock: E‑Learning Login gesperrt
|
||||||
LmsResultInsert: Neues LMS Ergebnis
|
CsvColumnLmsResult !ident-ok: LMS Status
|
||||||
LmsResultUpdate: LMS Ergebnis aktualisierung
|
LmsReportInsert: Neues LMS Ereignis
|
||||||
LmsResultCsvExceptionDuplicatedKey: CSV Import fand uneindeutigen Schlüssel
|
LmsReportUpdate: LMS Ereignis Aktualisierung
|
||||||
LmsUserlistCsvExceptionDuplicatedKey: CSV Import fand uneindeutigen Schlüssel
|
LmsReportCsvExceptionDuplicatedKey: CSV-Import LmsReport fand uneindeutigen Schlüssel
|
||||||
LmsDirectUpload: Direkter Upload für automatisierte Systeme
|
LmsDirectUpload: Direkter Upload für automatisierte Systeme
|
||||||
LmsErrorNoRefreshElearning: Fehler: E‑Learning wird nicht automatisch gestartet, da die Zeitspanne für den Erneurerungszeitraum nicht festgelegt wurde.
|
LmsErrorNoRefreshElearning: Fehler: E‑Learning wird nicht automatisch gestartet, da die Zeitspanne für den Erneurerungszeitraum nicht festgelegt wurde!
|
||||||
|
LmsErrorNoRenewElearning: Fehler: Erfoglreiches E‑Learning verlängert die Qualifikation nicht automatisch, da die Gültigkeitsdauer nicht festgelegt wurde!
|
||||||
MailSubjectQualificationRenewal qname@Text: Qualifikation #{qname} muss demnächst erneuert werden
|
MailSubjectQualificationRenewal qname@Text: Qualifikation #{qname} muss demnächst erneuert werden
|
||||||
MailSubjectQualificationExpiry qname@Text: Qualifikation #{qname} läuft demnächst ab
|
MailSubjectQualificationExpiry qname@Text: Qualifikation #{qname} läuft demnächst ab
|
||||||
MailSubjectQualificationExpired qname@Text: Qualifikation #{qname} ist ab sofort ungültig
|
MailSubjectQualificationExpired qname@Text: Qualifikation #{qname} ist ab sofort ungültig
|
||||||
@ -99,25 +117,33 @@ QualificationActUnblock: Entzug aufheben
|
|||||||
QualificationActRenew: Qualifikation regulär verlängern
|
QualificationActRenew: Qualifikation regulär verlängern
|
||||||
QualificationActGrant: Qualifikation vergeben
|
QualificationActGrant: Qualifikation vergeben
|
||||||
QualificationActGrantWarning: Diese Funktion ist nur für seltene Ausnahmefälle vorgesehen! Ein Entzug wird ggf. aufgehoben.
|
QualificationActGrantWarning: Diese Funktion ist nur für seltene Ausnahmefälle vorgesehen! Ein Entzug wird ggf. aufgehoben.
|
||||||
|
QualificationActStartELearning: E‑Learning für gültige Inhaber (neu) starten
|
||||||
|
QualificationActStartELearningStatus l@QualificationShorthand n@Int m@Int: E‑Learning #{l} für #{n}/#{m} Teilnehmer (neu) gestartet. Hinweis: Es kann länger dauern, bis das LMS tatsächlich startet.
|
||||||
QualificationStatusBlock l@QualificationShorthand n@Int m@Int: #{n}/#{m} #{l} entzogen
|
QualificationStatusBlock l@QualificationShorthand n@Int m@Int: #{n}/#{m} #{l} entzogen
|
||||||
QualificationStatusUnblock l@QualificationShorthand n@Int m@Int: #{n}/#{m} #{l} reaktiviert
|
QualificationStatusUnblock l@QualificationShorthand n@Int m@Int: #{n}/#{m} #{l} reaktiviert
|
||||||
|
LmsInactive: Aktuell kein E‑Learning aktiv
|
||||||
LmsRenewalInstructions: Weitere Anweisungen zur Verlängerung finden Sie im angehängten PDF. Um Missbrauch zu verhindern wurde das PDF mit dem im FRADrive hinterlegten PDF-Passwort des Prüflings verschlüsselt. Falls kein PDF-Passwort manuell hinterlegt wurde, ist das PDF-Passwort die Flughafen Ausweisnummer, inklusive Punkt und der Ziffer danach.
|
LmsRenewalInstructions: Weitere Anweisungen zur Verlängerung finden Sie im angehängten PDF. Um Missbrauch zu verhindern wurde das PDF mit dem im FRADrive hinterlegten PDF-Passwort des Prüflings verschlüsselt. Falls kein PDF-Passwort manuell hinterlegt wurde, ist das PDF-Passwort die Flughafen Ausweisnummer, inklusive Punkt und der Ziffer danach.
|
||||||
LmsNoRenewal: Leider kann diese Qualifikation nicht alleine durch E‑Learning verlängert werden.
|
LmsNoRenewal: Leider kann diese Qualifikation nicht alleine durch E‑Learning verlängert werden. Bitte setzen Sie sich mit uns in Verbindung, wenn Sie die Qualifikation verlängern möchten und noch nicht wissen, wie Sie das tun können. Ignorieren Sie diese automatisch generierte Erinnerung, falls Sie sich bereits um die Verlängerung gekümmert haben
|
||||||
LmsRenewalReminder: Erinnerung
|
LmsRenewalReminder: Erinnerung
|
||||||
LmsActNotify: Benachrichtigung E‑Learning erneut per Post oder E-Mail versenden
|
LmsActNotify: Benachrichtigung E‑Learning erneut per Post oder E-Mail versenden
|
||||||
LmsActRenewPin: Neues zufällige E‑Learning Passwort zuweisen
|
LmsActRenewPin: Neues zufällige E‑Learning Passwort zuweisen
|
||||||
LmsActRenewNotify: Neue zufällige E‑Learning Passwort zuweisen und Benachrichtigung per Post oder E-Mail versenden
|
LmsActRenewNotify: Neue zufällige E‑Learning Passwort zuweisen und Benachrichtigung per Post oder E-Mail versenden
|
||||||
LmsActRestart: E-Learning neu starten
|
LmsActReset: E‑Learning Fehlversuche zurücksetzen und entsperren
|
||||||
LmsActRestartWarning: Das vorhandene E-Learning wird sofort komplett gelöscht! Für Inhaber einer gültigen Fahrlizenz werden später Benutzer und Passwort neu vergeben und es wird eine neue Benachrichtigung versendet werden.
|
LmsActResetInfo: E‑Learning Login, Passwort und Fortschritt bleiben unverändert, eine neue Benachrichtigung ist nicht notwendig. Nur möglich für bereits gesperrte Lerner. Es kann bis zu 2 Stunden dauern, bis das LMS die Anfrage umgesetzt hat.
|
||||||
LmsActRestartFeedback n@Int m@Int: #{n}/#{m} E-Learning wurden neu gestartet.
|
LmsActResetFeedback n@Int m@Int: Für #{n}/#{m} E‑Learning Nutzer wurden alle Fehlversuche zurückgesetzt.
|
||||||
|
LmsActRestart: E‑Learning komplett neu starten
|
||||||
|
LmsActRestartWarning: Das vorhandene E‑Learning wird komplett gelöscht! Für Inhaber einer gültigen Lizenz werden später Benutzer und Passwort neu vergeben und es sollte eine neue Benachrichtigung versendet werden. Hinweis: Es kann mehrere Stunden dauern, bis das LMS diese Anfrage umgesetzt hat.
|
||||||
|
LmsActRestartFeedback n@Int m@Int: #{n}/#{m} E‑Learning Nutzer wurden komplett neu gestartet mit neuem Login und Passwort.
|
||||||
LmsActRestartExtend: Gültig bis ggf. erhöhen für die nächsten # Tage
|
LmsActRestartExtend: Gültig bis ggf. erhöhen für die nächsten # Tage
|
||||||
LmsActRestartUnblock: Entzug ggf. aufheben
|
LmsActRestartUnblock: Entzug ggf. aufheben
|
||||||
|
LmsStateOpen: E‑Learning offen
|
||||||
|
LmsStatusLocked: E‑Learning gesperrt, wird ggf. bald geöffnet
|
||||||
|
LmsStatusUnlocked: E‑Learning offen, wird ggf. bald gesperrt
|
||||||
|
LmsStatusResetTries: Fehlversuche werden demnächst zurückgesetzt
|
||||||
LmsStatusNotificationSent: Anmeldedaten wurden an Prüfling oder Ansprechpartner per Post oder E#{nonBreakableDash}Mail versendet; E#{nonBreakableDash}Learning ist derzeit offen
|
LmsStatusNotificationSent: Anmeldedaten wurden an Prüfling oder Ansprechpartner per Post oder E#{nonBreakableDash}Mail versendet; E#{nonBreakableDash}Learning ist derzeit offen
|
||||||
LmsNotificationSend n@Int: E‑Learning Benachrichtigungen an #{n} #{pluralDE n "Prüfling" "Prüflinge"} werden per Post oder E-Mail versendet.
|
LmsNotificationSend n@Int: E‑Learning Benachrichtigungen an #{n} #{pluralDE n "Prüfling" "Prüflinge"} werden per Post oder E-Mail versendet.
|
||||||
LmsPinRenewal n@Int: E‑Learning Passwort ausgetauscht für #{n} #{pluralDE n "Prüfling" "Prüflinge"}.
|
LmsPinRenewal n@Int: E‑Learning Passwort ausgetauscht für #{n} #{pluralDE n "Prüfling" "Prüflinge"}.
|
||||||
LmsActionFailed n@Int: Aktion nicht durchgeführt für #{n} #{pluralDE n "Person" "Personen"}, da diese derzeit nicht an einer Prüfung teilnehmen.
|
LmsActionFailed n@Int: Aktion nicht durchgeführt für #{n} #{pluralDE n "Person" "Personen"}, da diese derzeit nicht an einer Prüfung teilnehmen.
|
||||||
LmsStarted: E‑Learning eröffnet
|
LmsStarted: E‑Learning eröffnet
|
||||||
LmsAutomaticQueuing n@Natural: Die folgenden Funktionen werden normalerweise einmal pro Tag um #{show n} Uhr ausgeführt.
|
|
||||||
LmsManualQueuing: Die folgenden Funktionen sollten einmal pro Tag ausgeführt werden.
|
|
||||||
BtnLmsEnqueue: Nutzer mit ablaufenden Qualifikationen zum E‑Learning anmelden und benachrichtigen
|
BtnLmsEnqueue: Nutzer mit ablaufenden Qualifikationen zum E‑Learning anmelden und benachrichtigen
|
||||||
BtnLmsDequeue: Nutzer mit beendetem E‑Learning ggf. benachrichtigen und aufräumen
|
BtnLmsDequeue: Nutzer mit beendetem E‑Learning aufräumen und ggf. benachrichtigen
|
||||||
|
|||||||
@ -7,33 +7,46 @@ QualificationName: Qualification
|
|||||||
QualificationDescription: Description
|
QualificationDescription: Description
|
||||||
QualificationValidIndicator: Validity
|
QualificationValidIndicator: Validity
|
||||||
QualificationValidDuration: Validity period
|
QualificationValidDuration: Validity period
|
||||||
QualificationAuditDuration: Audit log keept
|
QualificationAuditDuration: Audit log retention period
|
||||||
|
QualificationAuditDurationTooltip n@Int: Optional period for deletion of e‑learning data. Note that the e‑learning server may delete its anonymised data earlier, at most #{n} days after closing.
|
||||||
|
QualificationAuditDurationReuseError: This qualification reuses the e‑learning from another qualification, which has no audit duration configured.
|
||||||
QualificationRefreshWithin: Refresh within
|
QualificationRefreshWithin: Refresh within
|
||||||
QualificationRefreshWithinTooltip: Optional period before expiry to start e‑learning and send a notification by post or email
|
QualificationRefreshWithinTooltip: Optional period before expiry to send a notification by email. If e‑learning is set to start automatically, it will be started and e‑learning credentials are send with this notification by post or email.
|
||||||
QualificationRefreshReminder: 2. Reminder
|
QualificationRefreshReminder: Second reminder
|
||||||
QualificationRefreshReminderTooltip: Optional period before expiry to send a second notification by post or email once more, provided that no renewal notification was sent in this period before expiry
|
QualificationRefreshReminderTooltip: Optional period before expiry to send a second notification by post or email once more, including the existing credentials, provided that the e‑learning is still undecided and the qualification has not yet expired.
|
||||||
QualificationElearningStart: Is e‑learning automatically started?
|
QualificationElearningStart: Is e‑learning automatically started?
|
||||||
|
QualificationElearningRenew: Does successful e‑learning automatically extend a qualification by the default validity period?
|
||||||
|
QualificationElearningLimit: Is the number of e‑learning attempts limited?
|
||||||
|
QualificationElearningLimitMax n: #{n} attempts maximum
|
||||||
|
QualificationElearningNoLimit: No limit
|
||||||
QualificationExpiryNotification: Invalidity notification?
|
QualificationExpiryNotification: Invalidity notification?
|
||||||
QualificationExpiryNotificationTooltip: Qualification holder are notfied upon invalidity, provided they have activated such notification in their user settings.
|
QualificationExpiryNotificationTooltip: Qualification holder are notfied upon invalidity, provided they have activated such notification in their user settings.
|
||||||
TableQualificationCountActive: Active
|
TableQualificationCountActive: Active
|
||||||
TableQualificationCountActiveTooltip: Number of currently valid qualification holders
|
TableQualificationCountActiveTooltip: Number of currently valid qualification holders
|
||||||
TableQualificationCountTotal: Total
|
TableQualificationCountTotal: Total
|
||||||
TableQualificationIsAvsLicence: AVS Driving License
|
TableQualificationLmsReuses: Reuse LMS
|
||||||
|
TableQualificationLmsReusesTooltip: This qualification reuses the e‑learning of the given qualification, instead of having a separate e‑learning of its own.
|
||||||
|
TableQualificationIsAvsLicence: AVS driving license
|
||||||
TableQualificationIsAvsLicenceTooltip: Under which name is this qualification synchronized with AVS, if any? Only applies to qualification holders having an AVS PersonID.
|
TableQualificationIsAvsLicenceTooltip: Under which name is this qualification synchronized with AVS, if any? Only applies to qualification holders having an AVS PersonID.
|
||||||
TableQualificationSapExport: Sent to SAP
|
TableQualificationSapExport: Sent to SAP
|
||||||
TableQualificationSapExportTooltip: Is this qualification transmitted to SAP? Only applies to qualification holder having a Fraport AG personnel number.
|
TableQualificationSapExportTooltip: Is this qualification transmitted to SAP? Only applies to qualification holder having a Fraport AG personnel number.
|
||||||
LmsQualificationValidUntil: Valid until
|
LmsQualificationValidUntil: Valid until
|
||||||
TableQualificationLastRefresh: Last renewed
|
TableQualificationLastRefresh: Last renewed
|
||||||
TableQualificationLastNotified: Last notified
|
TableQualificationLastNotified: Last notified about validity change
|
||||||
|
TableQualificationLastNotifiedTooltip: The date of the last notification about any already effective change in validity due to revocation or reissue. This does not entail regular validity extensions, e.g. due to e-learning.
|
||||||
TableQualificationFirstHeld: First held
|
TableQualificationFirstHeld: First held
|
||||||
TableQualificationBlockedDue: Revoked
|
TableQualificationBlockedDue: Revocations
|
||||||
TableQualificationBlockedTooltip: Why and when was this qualification temporarily suspended?
|
TableQualificationBlockedTooltip: Why and when was this qualification temporarily suspended?
|
||||||
TableQualificationBlockedTooltipSimple: When was this qualification revoked due to extraordinary reasons?
|
TableQualificationBlockedTooltipSimple: If a date is shown, this qualification has been revoked on that date due to extraordinary reasons
|
||||||
TableQualificationNoRenewal: Discontinued
|
TableQualificationNoRenewal: Discontinued
|
||||||
|
InfoQualificationBlockStatus: Is the qualification currently revoked? False indicates, that a revocation had been lifted
|
||||||
|
InfoQualificationBlockFrom: Date of last revocation or lifting of a revocation
|
||||||
TableQualificationNoRenewalTooltip: No renewal notifications will be send for this qualification upon expiry. The qualification may still be valid.
|
TableQualificationNoRenewalTooltip: No renewal notifications will be send for this qualification upon expiry. The qualification may still be valid.
|
||||||
QualificationScheduleRenewalTooltip: Will there be a notification, if this qualification is about to expire soon?
|
QualificationScheduleRenewalTooltip: Will there be a notification, if this qualification is about to expire soon?
|
||||||
QualificationUserNoRenewal: Expires without further notification
|
QualificationUserNoRenewal: Expires without further notification
|
||||||
QualificationUserNone: No registered qualifications for this person.
|
QualificationUserNone: No registered qualifications for this person.
|
||||||
|
QualificationGrantReason: Reason for granting
|
||||||
|
QualificationRenewReason: Reason for renewal
|
||||||
QualificationBlockReason: Reason for revoking
|
QualificationBlockReason: Reason for revoking
|
||||||
QualificationBlockNotify: Send notification
|
QualificationBlockNotify: Send notification
|
||||||
QualificationBlockRemoveSupervisor: Remove all supervisors
|
QualificationBlockRemoveSupervisor: Remove all supervisors
|
||||||
@ -44,8 +57,11 @@ TableLmsEmail: Email
|
|||||||
TableLmsIdent: E‑learning user
|
TableLmsIdent: E‑learning user
|
||||||
TableLmsPin: E‑learning password
|
TableLmsPin: E‑learning password
|
||||||
TableLmsElearning: E‑learning
|
TableLmsElearning: E‑learning
|
||||||
|
TableLmsElearningRenews: Automatic renewal
|
||||||
|
TableLmsElearningLimit: Max attempts
|
||||||
TableLmsResetPin: Reset E‑learning password?
|
TableLmsResetPin: Reset E‑learning password?
|
||||||
TableLmsDatePin: E‑learning password created
|
TableLmsDatePin: E‑learning password created
|
||||||
|
TableLmsDate: Date
|
||||||
TableLmsDelete: Delete?
|
TableLmsDelete: Delete?
|
||||||
TableLmsStaff: Staff?
|
TableLmsStaff: Staff?
|
||||||
TableLmsStarted: Started
|
TableLmsStarted: Started
|
||||||
@ -57,11 +73,12 @@ TableLmsStatus: Status e‑learning
|
|||||||
TableLmsStatusTooltip mbMonth: Shows #{maybeToMessage "for up to " (fmap (flip pluralENsN "month") mbMonth) " after closure"} the last e#{nonBreakableDash}learning status change:
|
TableLmsStatusTooltip mbMonth: Shows #{maybeToMessage "for up to " (fmap (flip pluralENsN "month") mbMonth) " after closure"} the last e#{nonBreakableDash}learning status change:
|
||||||
TableLmsStatusDay: Date of last e‑learning status change
|
TableLmsStatusDay: Date of last e‑learning status change
|
||||||
TableLmsSuccess: Completed
|
TableLmsSuccess: Completed
|
||||||
TableLmsFailed: Blocked
|
TableLmsLock: Locked
|
||||||
|
TableLmsResetTries: Reset e‑learning attempts
|
||||||
LmsStatusBlocked: Failed after too many attempts
|
LmsStatusBlocked: Failed after too many attempts
|
||||||
LmsStatusExpired: Failed due to expiry
|
LmsStatusExpired: Failed due to expiry
|
||||||
LmsStatusSuccess: Passed
|
LmsStatusSuccess: Passed
|
||||||
LmsStatusPlanned: E#{nonBreakableDash}learning is about to be opened (visible to Admins only)
|
LmsStatusPlanned: E#{nonBreakableDash}learning is about to be opened soon (visible to Admins only)
|
||||||
LmsStatusDelay: Note that status changes may occassionaly require more than a hour to be displayed here.
|
LmsStatusDelay: Note that status changes may occassionaly require more than a hour to be displayed here.
|
||||||
FilterLmsValid: Currently valid
|
FilterLmsValid: Currently valid
|
||||||
FilterLmsRenewal: Renewal due
|
FilterLmsRenewal: Renewal due
|
||||||
@ -70,18 +87,19 @@ FilterLmsNotificationDue: Notification due
|
|||||||
CsvColumnLmsIdent: E#{nonBreakableDash}learning identifier, unique for each qualification and user
|
CsvColumnLmsIdent: E#{nonBreakableDash}learning identifier, unique for each qualification and user
|
||||||
CsvColumnLmsPin: Password e#{nonBreakableDash}learning access
|
CsvColumnLmsPin: Password e#{nonBreakableDash}learning access
|
||||||
CsvColumnLmsResetPin: Will the e#{nonBreakableDash}learning password be reset upon next synchronisation?
|
CsvColumnLmsResetPin: Will the e#{nonBreakableDash}learning password be reset upon next synchronisation?
|
||||||
CsvColumnLmsDelete: Will the identifier be deleted from the E‑learning platfrom upon next synchronisation?
|
CsvColumnLmsDelete: Will the identifier be deleted from the e‑learning platfrom upon next synchronisation?
|
||||||
CsvColumnLmsStaff: Is the user an internal staff member? (Legacy, currently ignored)
|
CsvColumnLmsStaff: Is the user an internal staff member? (Legacy, currently ignored)
|
||||||
CsvColumnLmsSuccess: Timestamp of successful completion (UTC)
|
CsvColumnLmsSuccess: Timestamp of successful completion (UTC)
|
||||||
CsvColumnLmsFailed: Blockier durch LMS, üblicherweise wegen zu vieler Fehlversuche
|
CsvColumnLmsResetTries: Reset number of used up e‑learning exam attempts
|
||||||
LmsUserlistInsert: New LMS user
|
CsvColumnLmsDate: Date of e‑learning event
|
||||||
LmsUserlistUpdate: Update of LMS user
|
CsvColumnLmsResult: LMS Status
|
||||||
LmsResultInsert: New LMS result
|
CsvColumnLmsLock: E‑learning login is not permitted
|
||||||
LmsResultUpdate: Update of LMS result
|
LmsReportInsert: New LMS event
|
||||||
LmsResultCsvExceptionDuplicatedKey: CSV import with ambiguous key
|
LmsReportUpdate: Update of LMS event
|
||||||
LmsUserlistCsvExceptionDuplicatedKey: CSV import with ambiguous key
|
LmsReportCsvExceptionDuplicatedKey: CSV Import LmsReport with ambiguous key
|
||||||
LmsDirectUpload: Direct upload for automated systems
|
LmsDirectUpload: Direct upload for automated systems
|
||||||
LmsErrorNoRefreshElearning: Error: E‑learning will not be started automatically due to refresh-within time period not being set.
|
LmsErrorNoRefreshElearning: Error: E‑learning will not be started automatically due to refresh-within time period not being set!
|
||||||
|
LmsErrorNoRenewElearning: Error: E‑learning will not automatically extend validity due to validity duration not being set!
|
||||||
MailSubjectQualificationRenewal qname: Qualification #{qname} must be renewed shortly
|
MailSubjectQualificationRenewal qname: Qualification #{qname} must be renewed shortly
|
||||||
MailSubjectQualificationExpiry qname: Qualification #{qname} expires soon
|
MailSubjectQualificationExpiry qname: Qualification #{qname} expires soon
|
||||||
MailSubjectQualificationExpired qname: Qualification #{qname} is no longer valid
|
MailSubjectQualificationExpired qname: Qualification #{qname} is no longer valid
|
||||||
@ -99,25 +117,33 @@ QualificationActUnblock: Clear revocation
|
|||||||
QualificationActRenew: Renew qualification
|
QualificationActRenew: Renew qualification
|
||||||
QualificationActGrant: Grant qualification
|
QualificationActGrant: Grant qualification
|
||||||
QualificationActGrantWarning: Use with caution in rare exceptional cases only! Any revocation will be undone.
|
QualificationActGrantWarning: Use with caution in rare exceptional cases only! Any revocation will be undone.
|
||||||
|
QualificationActStartELearning: Manually (re)start e‑learning for valid qualification holders
|
||||||
|
QualificationActStartELearningStatus l n m: E‑learning #{l} (re)started for #{n}/#{m} users. Note: It may take a while, until the e‑learning is activated.
|
||||||
QualificationStatusBlock l n m: #{n}/#{m} #{l} revoked
|
QualificationStatusBlock l n m: #{n}/#{m} #{l} revoked
|
||||||
QualificationStatusUnblock l n m: #{n}/#{m} #{l} reactivated
|
QualificationStatusUnblock l n m: #{n}/#{m} #{l} reactivated
|
||||||
|
LmsInactive: Currently no active e‑learning
|
||||||
LmsRenewalInstructions: Instruction on how to accomplish the renewal are enclosed in the attached PDF. In order to avoid misuse, the PDF is encrypted with the FRADrive PDF-password of the examinee. If no PDF-password had been chosen yet, then the password is the Fraport id card number of the examinee, including the punctuation mark and the digit thereafter.
|
LmsRenewalInstructions: Instruction on how to accomplish the renewal are enclosed in the attached PDF. In order to avoid misuse, the PDF is encrypted with the FRADrive PDF-password of the examinee. If no PDF-password had been chosen yet, then the password is the Fraport id card number of the examinee, including the punctuation mark and the digit thereafter.
|
||||||
LmsNoRenewal: Unfortunately, this particular qualification cannot be renewed through e‑learning only.
|
LmsNoRenewal: Unfortunately, this particular qualification cannot be renewed through e‑learning only. Please contact us, if you do not yet know how to renew this qualification. Ignore this automatically generated reminder email, if you have made arrangements for the renewal of this qualification already.
|
||||||
LmsRenewalReminder: Reminder
|
LmsRenewalReminder: Reminder
|
||||||
LmsActNotify: Resend e‑learning notification by post or email
|
LmsActNotify: Resend e‑learning notification by post or email
|
||||||
LmsActRenewPin: Randomly replace e‑learning password
|
LmsActRenewPin: Randomly replace e‑learning password
|
||||||
LmsActRenewNotify: Randomly replace e‑learning password and re-send notification by post or email
|
LmsActRenewNotify: Randomly replace e‑learning password and re-send notification by post or email
|
||||||
LmsActRestart: Restart e-learning
|
LmsActReset: Reset and unlock e‑learning
|
||||||
LmsActRestartWarning: The existing e-learning will be erased immediately! For drivers with a valid licence, user and password will later be generated anew and a notification will be queued as usual.
|
LmsActResetInfo: E‑learning login, password and progress remain unchanged; a notification is thus not necessary. This is only possible for already failed learners. Note that the reset procedure may take up to 2 hours.
|
||||||
|
LmsActResetFeedback n@Int m@Int: For #{n}/#{m} learners all failures were erased, preserving login credentials.
|
||||||
|
LmsActRestart: Restart e‑learning
|
||||||
|
LmsActRestartWarning: The existing e‑learning will be erased immediately! For drivers with a valid licence, user and password will later be generated anew and a notification will be queued as usual, which may take several hours.
|
||||||
LmsActRestartExtend: Ensure validity for the next # days
|
LmsActRestartExtend: Ensure validity for the next # days
|
||||||
LmsActRestartUnblock: Undo any revocations
|
LmsActRestartUnblock: Undo any revocations
|
||||||
LmsActRestartFeedback n@Int m@Int: #{n}/#{m} e-learnings were restarted.
|
LmsActRestartFeedback n@Int m@Int: #{n}/#{m} e-learnings were completely restarted with new login credentials.
|
||||||
|
LmsStateOpen: E‑learning open
|
||||||
|
LmsStatusLocked: E‑learning locked, may be opened soon
|
||||||
|
LmsStatusUnlocked: E‑learning still open, may be locked soon
|
||||||
|
LmsStatusResetTries: Failed attempts will be soon reset
|
||||||
LmsStatusNotificationSent: E‑learning password has been sent to examinee or supervisor by letter post or by email; e‑learning is currently open
|
LmsStatusNotificationSent: E‑learning password has been sent to examinee or supervisor by letter post or by email; e‑learning is currently open
|
||||||
LmsNotificationSend n: E‑learning notifications will be sent to #{n} #{pluralENs n "examinee"} by letter post or by email.
|
LmsNotificationSend n: E‑learning notifications will be sent to #{n} #{pluralENs n "examinee"} by letter post or by email.
|
||||||
LmsPinRenewal n: E‑learning password replaced randomly for #{n} #{pluralENs n "examinee"}.
|
LmsPinRenewal n: E‑learning password replaced randomly for #{n} #{pluralENs n "examinee"}.
|
||||||
LmsActionFailed n: No action for #{n} #{pluralENs n "person"}, since there was no ongoing examination.
|
LmsActionFailed n: No action for #{n} #{pluralENs n "person"}, since there was no ongoing examination.
|
||||||
LmsStarted: E‑learning open since
|
LmsStarted: E‑learning open since
|
||||||
LmsAutomaticQueuing n@Natural: The following functions are executed daily at #{show n} o'clock.
|
BtnLmsEnqueue: Enqueue users with expiring qualifications for e‑learning and notify them
|
||||||
LmsManualQueuing: The following functions should be executed daily.
|
BtnLmsDequeue: Dequeue users with finished e‑learning and notify failed users
|
||||||
BtnLmsEnqueue: Enqueue users with expiring qualifications for e‑learning and notify them.
|
|
||||||
BtnLmsDequeue: Dequeue users with finished e‑learning and notify, if appropriate.
|
|
||||||
|
|||||||
@ -103,4 +103,4 @@ MailSupervisorNoCopy: Warnung: Diese Nachricht wurde nicht an den eigentlichen E
|
|||||||
MailSupervisedNote: Hinweis
|
MailSupervisedNote: Hinweis
|
||||||
MailSupervisedBody: Eine Kopie dieser Nachricht wurde auch an folgende in FRADrive eingetragene Ansprechpartner gesendet:
|
MailSupervisedBody: Eine Kopie dieser Nachricht wurde auch an folgende in FRADrive eingetragene Ansprechpartner gesendet:
|
||||||
MailSupervisorReroute: Benachrichtigungsumleitung
|
MailSupervisorReroute: Benachrichtigungsumleitung
|
||||||
MailSupervisorRerouteTooltip: Alle Benachrichtigungen werden stattdessen an alle Ansprechpartner mit Benachrichtigungsumleitung gesandt
|
MailSupervisorRerouteTooltip: Alle Benachrichtigungen werden stattdessen an diese Ansprechpartner mit Benachrichtigungsumleitung gesandt
|
||||||
@ -103,4 +103,4 @@ MailSupervisorNoCopy: Warning: This message was not sent to the original recipie
|
|||||||
MailSupervisedNote: Please note
|
MailSupervisedNote: Please note
|
||||||
MailSupervisedBody: A copy of this message has been sent to all supervisors registered for you in FRADrive, namely:
|
MailSupervisedBody: A copy of this message has been sent to all supervisors registered for you in FRADrive, namely:
|
||||||
MailSupervisorReroute: Reroute notifications
|
MailSupervisorReroute: Reroute notifications
|
||||||
MailSupervisorRerouteTooltip: All notification will be sent to all supervisors with notification rerouting instead
|
MailSupervisorRerouteTooltip: All notification will be rerouted to these supervisors instead
|
||||||
@ -37,7 +37,8 @@ PDFPassword: Passwort zur Verschlüsselung von PDF Anhängen an Email Benachrich
|
|||||||
PDFPasswordTip: Achtung, dieses Passwort ist für FRADrive Administratoren einsehbar und wird unverschlüsselt gespeichert!
|
PDFPasswordTip: Achtung, dieses Passwort ist für FRADrive Administratoren einsehbar und wird unverschlüsselt gespeichert!
|
||||||
PDFPasswordInvalid c@Char: Bitte ein nicht-triviales Passwort für PDF Email Anhänge eintragen! Ungültiges Zeichen: #{char2Text c}
|
PDFPasswordInvalid c@Char: Bitte ein nicht-triviales Passwort für PDF Email Anhänge eintragen! Ungültiges Zeichen: #{char2Text c}
|
||||||
PDFPasswordTooShort n@Int: Bitte ein PDF Passwort mit mindestens #{show n} Zeichen wählen oder Post-Versand aktivieren
|
PDFPasswordTooShort n@Int: Bitte ein PDF Passwort mit mindestens #{show n} Zeichen wählen oder Post-Versand aktivieren
|
||||||
PrefersPostal: Sollen Benachrichtigung möglichst per Post versendet werden anstatt per Email?
|
PrefersPostal: Bevorzugte Benachrichtigung
|
||||||
|
PrefersPostalExp: Sollen Benachrichtigung möglichst per Post versendet werden anstatt per Email?
|
||||||
PostalTip: Postversand kann in Rechnung gestellt werden und ist derzeit nur für Benachrichtigungen über Erneuerung und Ablauf von Qualifikation, wie z.B. Führerscheine, verfügbar.
|
PostalTip: Postversand kann in Rechnung gestellt werden und ist derzeit nur für Benachrichtigungen über Erneuerung und Ablauf von Qualifikation, wie z.B. Führerscheine, verfügbar.
|
||||||
PostAddress: Postalische Adresse
|
PostAddress: Postalische Adresse
|
||||||
PostAddressTip: Mindestens eine Zeile mit Straße und Hausnummer und eine Zeile mit Postleitzahl und Ort. Kein Empfängername, denn dieser wird später automatisch hinzugefügt.
|
PostAddressTip: Mindestens eine Zeile mit Straße und Hausnummer und eine Zeile mit Postleitzahl und Ort. Kein Empfängername, denn dieser wird später automatisch hinzugefügt.
|
||||||
@ -94,8 +95,6 @@ TokensLastReset: Tokens zuletzt invalidiert
|
|||||||
ProfileNever: Nie
|
ProfileNever: Nie
|
||||||
ProfileLdapPrimaryKey: LDAP-Primärschlüssel
|
ProfileLdapPrimaryKey: LDAP-Primärschlüssel
|
||||||
ProfileLastLdapSynchronisation: Letzte LDAP-Synchronisation
|
ProfileLastLdapSynchronisation: Letzte LDAP-Synchronisation
|
||||||
ProfileLastAvsSynchronisation: Letzte AVS-Synchronisation
|
|
||||||
ProfileLastAvsSynchError: Letzte AVS-Fehlermeldung
|
|
||||||
|
|
||||||
NotificationSettingsUpdate: Benachrichtigungs-Einstellungen erfolgreich gespeichert
|
NotificationSettingsUpdate: Benachrichtigungs-Einstellungen erfolgreich gespeichert
|
||||||
NotificationSettingsHeading displayName@Text: Benachrichtigungs-Einstellungen für #{displayName}
|
NotificationSettingsHeading displayName@Text: Benachrichtigungs-Einstellungen für #{displayName}
|
||||||
|
|||||||
@ -37,7 +37,8 @@ PDFPassword: Password to lock PDF email attachments
|
|||||||
PDFPasswordTip: Please note that this password is displayed to FRADrive admins and is saved unencrypted
|
PDFPasswordTip: Please note that this password is displayed to FRADrive admins and is saved unencrypted
|
||||||
PDFPasswordInvalid c: Please supply a sensible password for encrypting PDF email attachments! Invalid character #{char2Text c}
|
PDFPasswordInvalid c: Please supply a sensible password for encrypting PDF email attachments! Invalid character #{char2Text c}
|
||||||
PDFPasswordTooShort n: Please provide a password with at least #{show n} characters or choose postal mail
|
PDFPasswordTooShort n: Please provide a password with at least #{show n} characters or choose postal mail
|
||||||
PrefersPostal: Should notifications preferably send by post instead of email?
|
PrefersPostal: Notification preference
|
||||||
|
PrefersPostalExp: Should notifications preferably send by post instead of email?
|
||||||
PostalTip: Mailing may incur a fee and is currently only avaulable for qualification expiry notifications, such as driving lincence renewal.
|
PostalTip: Mailing may incur a fee and is currently only avaulable for qualification expiry notifications, such as driving lincence renewal.
|
||||||
PostAddress: Postal address
|
PostAddress: Postal address
|
||||||
PostAddressTip: Should contain at least one line with street and house number and another line featuring zip code and town. Omit a recipient name, since it will be added later.
|
PostAddressTip: Should contain at least one line with street and house number and another line featuring zip code and town. Omit a recipient name, since it will be added later.
|
||||||
@ -94,8 +95,6 @@ TokensLastReset: Tokens last reset
|
|||||||
ProfileNever: Never
|
ProfileNever: Never
|
||||||
ProfileLdapPrimaryKey: LDAP primary key
|
ProfileLdapPrimaryKey: LDAP primary key
|
||||||
ProfileLastLdapSynchronisation: Last LDAP synchronisation
|
ProfileLastLdapSynchronisation: Last LDAP synchronisation
|
||||||
ProfileLastAvsSynchronisation: Last AVS synchronisation
|
|
||||||
ProfileLastAvsSynchError: Last AVS Error
|
|
||||||
|
|
||||||
NotificationSettingsUpdate: Successfully updated notification settings
|
NotificationSettingsUpdate: Successfully updated notification settings
|
||||||
NotificationSettingsHeading displayName: Notification settings for #{displayName}
|
NotificationSettingsHeading displayName: Notification settings for #{displayName}
|
||||||
|
|||||||
@ -25,10 +25,14 @@ PersonalInfoTutorialsWip: Die Anzeige von Kurse, zu denen Sie angemeldet sind wi
|
|||||||
ProfileGroupSubmissionDates: Bei Gruppenabgaben wird kein Datum angezeigt, wenn Sie die Gruppenabgabe nie selbst hochgeladen haben.
|
ProfileGroupSubmissionDates: Bei Gruppenabgaben wird kein Datum angezeigt, wenn Sie die Gruppenabgabe nie selbst hochgeladen haben.
|
||||||
ProfileCorrectorRemark: Die oberhalb angezeigte Tabelle zeigt nur prinzipielle Einteilungen als Korrektor zu einem Übungsblatt. Auch ohne Einteilung können Korrekturen einzeln zugewiesen werden, welche hier dann nicht aufgeführt werden.
|
ProfileCorrectorRemark: Die oberhalb angezeigte Tabelle zeigt nur prinzipielle Einteilungen als Korrektor zu einem Übungsblatt. Auch ohne Einteilung können Korrekturen einzeln zugewiesen werden, welche hier dann nicht aufgeführt werden.
|
||||||
ProfileCorrections: Auflistung aller zugewiesenen Korrekturen
|
ProfileCorrections: Auflistung aller zugewiesenen Korrekturen
|
||||||
Remarks: Hinweise
|
Remarks: Hinweis:
|
||||||
|
|
||||||
ProfileSupervisor: Übergeordnete Ansprechpartner
|
ProfileNoSupervisor: Keine übergeordneten Ansprechpartner vorhanden
|
||||||
ProfileSupervisee: Ist Ansprechpartner für
|
ProfileSupervisor n@Int m@Int: #{n} #{pluralDE n "übergeordneter" "übergeordnete"} Ansprechpartner#{noneMoreDE m "" (", davon " <> tshow m <> " mit Benachrichtigungsumleitung")}
|
||||||
|
ProfileSupervisorRemark n@Int m@Int l@Int: #{m} von #{n} #{pluralDE m "übergeordneter" "übergeordnete"} Ansprechpartner mit Benachrichtigungsumleitung#{noneMoreDE l "" (", davon " <> tshow l <> " mit postalischer Benachrichtigung")}
|
||||||
|
ProfileNoSupervisee: Ist kein Ansprechpartner für irgendjemand
|
||||||
|
ProfileSupervisee n@Int m@Int: Ist Ansprechpartner für #{n} #{pluralDE n "Person" "Personen"}#{noneMoreDE m "" (", davon " <> tshow m <> " mit Benachrichtigungsumleitung")}
|
||||||
|
ProfileSuperviseeRemark n@Int m@Int: Dieser Nutzer ist Ansprechpartner für #{n} #{pluralDE n "Person" "Personen"}#{noneMoreDE m "" (", davon " <> tshow m <> " mit Benachrichtigungsumleitung")}
|
||||||
|
|
||||||
UserTelephone: Telefon
|
UserTelephone: Telefon
|
||||||
UserMobile: Mobiltelefon
|
UserMobile: Mobiltelefon
|
||||||
|
|||||||
@ -25,10 +25,14 @@ PersonalInfoTutorialsWip: The feature to display courses you have registered for
|
|||||||
ProfileGroupSubmissionDates: No date is shown for group submissions if you have never uploaded the submission yourself.
|
ProfileGroupSubmissionDates: No date is shown for group submissions if you have never uploaded the submission yourself.
|
||||||
ProfileCorrectorRemark: The table above only shows registration as a corrector in principle. Even without registration corrections can be assigned individually and are not listed.
|
ProfileCorrectorRemark: The table above only shows registration as a corrector in principle. Even without registration corrections can be assigned individually and are not listed.
|
||||||
ProfileCorrections: List of all assigned corrections
|
ProfileCorrections: List of all assigned corrections
|
||||||
Remarks: Remarks
|
Remarks: Remark:
|
||||||
|
|
||||||
ProfileSupervisor: Supervised by
|
ProfileNoSupervisor: Is not supervised by anynone
|
||||||
ProfileSupervisee: Supervises
|
ProfileSupervisor n m: #{pluralENsN n "supervisor"} #{noneMoreEN m "" ("with " <> tshow m <> " active notification rerouting")}
|
||||||
|
ProfileSupervisorRemark n@Int m@Int l@Int: #{m} of #{n} #{pluralENs m "supervisor"} with active notification rerouting#{noneMoreEN l "" (", and " <> tshow l <> "of these prefer postal notifications")}
|
||||||
|
ProfileNoSupervisee: Does not supervise anynone
|
||||||
|
ProfileSupervisee n m: Supervises #{pluralENsN n "person"} #{noneMoreEN m "" ("with " <> tshow m <> " active notification rerouting")}
|
||||||
|
ProfileSuperviseeRemark n m: This person supervises #{pluralENsN n "person"}#{noneMoreEN m "" (" with " <> tshow m <> " having active notifications rerouting to this user")}
|
||||||
|
|
||||||
UserTelephone: Phone
|
UserTelephone: Phone
|
||||||
UserMobile: Mobile
|
UserMobile: Mobile
|
||||||
|
|||||||
@ -22,6 +22,7 @@ AdminUserPostAddress: Postalische Anschrift
|
|||||||
AdminUserPrefersPostal: Briefe anstatt Email bevorzugt
|
AdminUserPrefersPostal: Briefe anstatt Email bevorzugt
|
||||||
AdminUserPinPassword: Passwort zur Verschlüsselung von PDF Anhängen in Emails
|
AdminUserPinPassword: Passwort zur Verschlüsselung von PDF Anhängen in Emails
|
||||||
AdminUserNoPassword: Kein Passwort gesetzt
|
AdminUserNoPassword: Kein Passwort gesetzt
|
||||||
|
AdminUserPinPassNotIncluded: Hinweis: Das Passwort wird hier zur Bequemlichkeit zusätzlich angezeigt und ist selbstverständlich nicht im originalem Inhalt enthalten.
|
||||||
AdminUserAssimilate: Diesen Benutzer assimilieren von
|
AdminUserAssimilate: Diesen Benutzer assimilieren von
|
||||||
UserAdded: Benutzer erfolgreich angelegt
|
UserAdded: Benutzer erfolgreich angelegt
|
||||||
UserCollision: Benutzer konnte wegen Eindeutigkeit nicht angelegt werden
|
UserCollision: Benutzer konnte wegen Eindeutigkeit nicht angelegt werden
|
||||||
@ -37,9 +38,10 @@ AuthPWHashAlreadyConfigured: Nutzer:in meldet sich bereits mit FRADrive spezifis
|
|||||||
AuthPWHashConfigured: Nutzer:in meldet sich nun mit FRADrive spezifischer Kennung an
|
AuthPWHashConfigured: Nutzer:in meldet sich nun mit FRADrive spezifischer Kennung an
|
||||||
UsersCourseSchool: Bereich
|
UsersCourseSchool: Bereich
|
||||||
ActionNoUsersSelected: Keine Benutzer:innen ausgewählt
|
ActionNoUsersSelected: Keine Benutzer:innen ausgewählt
|
||||||
SynchroniseAvsUserQueued n@Int: AVS-Synchronisation von #{n} #{pluralDE n "Benutzer:in" "Benutzer:innen"} angestoßen
|
SynchroniseAvsUserQueued n@Int: AVS-Synchronisation von #{n} #{pluralDE n "Benutzer:in" "Benutzer:innen"} zwingend angestoßen, die Ausführung wird mehrere Minuten benötigen!
|
||||||
SynchroniseLdapUserQueued n@Int: LDAP-Synchronisation von #{n} #{pluralDE n "Benutzer:in" "Benutzer:innen"} angestoßen
|
SynchroniseAvsAllUsersQueued n@Int64: AVS-Synchronisation von allen #{n} #{pluralDE n "Benutzer:in" "Benutzer:innen"} angestoßen, welche heute noch nicht synchronisiert wurden, die Ausführung wird eine Weile brauchen!
|
||||||
SynchroniseLdapAllUsersQueued: LDAP-Synchronisation von allen Benutzer:innen angestoßen
|
SynchroniseLdapUserQueued n@Int: LDAP-Synchronisation von #{n} #{pluralDE n "Benutzer:in" "Benutzer:innen"} angestoßen, die Ausführung wird mehrere Minuten benötigen!
|
||||||
|
SynchroniseLdapAllUsersQueued: LDAP-Synchronisation von allen Benutzer:innen angestoßen, die Ausführung kann eine Weile brauchen!
|
||||||
UserListTitle: Komprehensive Benutzerliste
|
UserListTitle: Komprehensive Benutzerliste
|
||||||
AccessRightsSaved: Berechtigungen erfolgreich verändert
|
AccessRightsSaved: Berechtigungen erfolgreich verändert
|
||||||
AccessRightsNotChanged: Berechtigungen wurden nicht verändert
|
AccessRightsNotChanged: Berechtigungen wurden nicht verändert
|
||||||
@ -89,11 +91,19 @@ NewPasswordLink: Neues Passwort setzen
|
|||||||
UserAccountDeleteWarning: Achtung, dies löscht den kompletten Benutzer/die komplette Benutzerin unwiderruflich und mit allen assoziierten Daten aus der Datenbank. Prüfungsdaten müssen jedoch langfristig gespeichert bleiben!
|
UserAccountDeleteWarning: Achtung, dies löscht den kompletten Benutzer/die komplette Benutzerin unwiderruflich und mit allen assoziierten Daten aus der Datenbank. Prüfungsdaten müssen jedoch langfristig gespeichert bleiben!
|
||||||
UserAvsSync: AVS-Synchronisieren
|
UserAvsSync: AVS-Synchronisieren
|
||||||
UserLdapSync: LDAP-Synchronisieren
|
UserLdapSync: LDAP-Synchronisieren
|
||||||
AllUsersLdapSync: Alle LDAP-Synchronisieren
|
|
||||||
UserHijack: Sitzung übernehmen
|
UserHijack: Sitzung übernehmen
|
||||||
UserAddSupervisor: Ansprechpartner hinzufügen
|
UserAddSupervisor: Ansprechpartner hinzufügen
|
||||||
UserSetSupervisor: Ansprechpartner ersetzen
|
UserSetSupervisor: Ansprechpartner ersetzen
|
||||||
UserRemoveSupervisor: Alle Ansprechpartner entfernen
|
UserRemoveSupervisor: Alle Ansprechpartner entfernen
|
||||||
|
UserRemoveSubordinates: Alle Ansprechpartnerbeziehungen zu Untergebenen beenden
|
||||||
|
UserIsSupervisor: Ist Ansprechpartner
|
||||||
|
UserAvsSwitchCompany: Als Primärfirma verwenden
|
||||||
|
UserAvsSwitchCompanyField: Primärfirma auswählen
|
||||||
|
UserAvsCompanySwitched c@CompanyShorthand: Primärfirma gewechselt zu #{tshow c}
|
||||||
|
AllUsersLdapSync: Alle LDAP-Synchronisieren
|
||||||
|
AllUsersAvsSync: Alle AVS-Synchronisieren
|
||||||
|
ThisUserLdapSync: LDAP Synchronisation
|
||||||
|
ThisUserAvsSync: AVS Synchronisation
|
||||||
AuthKindLDAP: Fraport AG Kennung
|
AuthKindLDAP: Fraport AG Kennung
|
||||||
AuthKindPWHash: FRADrive Kennung
|
AuthKindPWHash: FRADrive Kennung
|
||||||
AuthKindNoLogin: Kein Login möglich
|
AuthKindNoLogin: Kein Login möglich
|
||||||
@ -101,3 +111,9 @@ Name !ident-ok: Name
|
|||||||
UsersChangeSupervisorsSuccess usr@Int spr@Int: #{tshow spr} Ansprechpartner für #{tshow usr} Benutzer gesetzt.
|
UsersChangeSupervisorsSuccess usr@Int spr@Int: #{tshow spr} Ansprechpartner für #{tshow usr} Benutzer gesetzt.
|
||||||
UsersChangeSupervisorsWarning usr@Int spr@Int bad@Int: Nur _{MsgUsersChangeSupervisorsSuccess usr spr} #{tshow bad} Ansprechpartner #{pluralDE bad "wurde" "wurden"} nicht gefunden!
|
UsersChangeSupervisorsWarning usr@Int spr@Int bad@Int: Nur _{MsgUsersChangeSupervisorsSuccess usr spr} #{tshow bad} Ansprechpartner #{pluralDE bad "wurde" "wurden"} nicht gefunden!
|
||||||
UsersRemoveSupervisors usr@Int: Alle Ansprechpartner für #{tshow usr} Benutzer gelöscht.
|
UsersRemoveSupervisors usr@Int: Alle Ansprechpartner für #{tshow usr} Benutzer gelöscht.
|
||||||
|
UsersRemoveSubordinates usr@Int: Alle Ansprechpartnerbeziehungen für #{tshow usr} #{pluralDE usr "ehemaligen" "ehemalige"} Ansprechpartner gelöscht.
|
||||||
|
UserCompanyReason: Begründung der Firmenassoziation
|
||||||
|
UserCompanyReasonTooltip: Optionale Notiz für besondere Fälle. Kann ggf. autmatische Entfernung bei AVS Firmenwechsel verhindern.
|
||||||
|
UserSupervisorReason: Begründung Ansprechpartner
|
||||||
|
UserSupervisorReasonTooltip: Optionale Notiz für besondere Fälle. Kann ggf. autmatische Entfernung bei AVS Firmenwechsel verhindern.
|
||||||
|
AdminUserAllNotifications: Alle Benachrichtigungen and diesen Benutzer
|
||||||
@ -22,6 +22,7 @@ AdminUserPostAddress: Postal Address
|
|||||||
AdminUserPrefersPostal: Prefers postal letters over email
|
AdminUserPrefersPostal: Prefers postal letters over email
|
||||||
AdminUserPinPassword: Password used for PDF attachments to emails
|
AdminUserPinPassword: Password used for PDF attachments to emails
|
||||||
AdminUserNoPassword: No password set
|
AdminUserNoPassword: No password set
|
||||||
|
AdminUserPinPassNotIncluded: Note: the password is shown here only for convenience, but is not contained in the original content, of course.
|
||||||
AdminUserAssimilate: Assimilate user by another user
|
AdminUserAssimilate: Assimilate user by another user
|
||||||
UserAdded: Successfully added user
|
UserAdded: Successfully added user
|
||||||
UserCollision: Could not create user due to uniqueness constraint
|
UserCollision: Could not create user due to uniqueness constraint
|
||||||
@ -37,9 +38,10 @@ AuthPWHashAlreadyConfigured: User already logs in using their FRADrive specific
|
|||||||
AuthPWHashConfigured: User now logs in using their FRADrive specific account
|
AuthPWHashConfigured: User now logs in using their FRADrive specific account
|
||||||
UsersCourseSchool: Department
|
UsersCourseSchool: Department
|
||||||
ActionNoUsersSelected: No users selected
|
ActionNoUsersSelected: No users selected
|
||||||
SynchroniseAvsUserQueued n: Triggered AVS synchronisation of #{n} #{pluralEN n "user" "users"}.
|
SynchroniseAvsUserQueued n: Triggered forced AVS synchronisation of #{n} #{pluralEN n "user" "users"}, which may take several minutes to complete.
|
||||||
SynchroniseLdapUserQueued n: Triggered LDAP synchronisation of #{n} #{pluralEN n "user" "users"}.
|
SynchroniseAvsAllUsersQueued n: Triggered AVS synchronisation of all #{n} #{pluralEN n "user" "users"} that were not already synchronised today, which may take quite a while to complete.
|
||||||
SynchroniseLdapAllUsersQueued: Triggered LDAP synchronisation of all users
|
SynchroniseLdapUserQueued n: Triggered LDAP synchronisation of #{n} #{pluralEN n "user" "users"}, which may take several minutes to complete.
|
||||||
|
SynchroniseLdapAllUsersQueued: Triggered LDAP synchronisation of all users, which may take quite a while to complete.
|
||||||
UserListTitle: Comprehensive list of users
|
UserListTitle: Comprehensive list of users
|
||||||
AccessRightsSaved: Successfully updated permissions
|
AccessRightsSaved: Successfully updated permissions
|
||||||
AccessRightsNotChanged: Permissions left unchanged
|
AccessRightsNotChanged: Permissions left unchanged
|
||||||
@ -89,15 +91,29 @@ NewPasswordLink: Set password
|
|||||||
UserAccountDeleteWarning: Caution, this permanently deletes users and all of their associated data. Exam results must be stored long term!
|
UserAccountDeleteWarning: Caution, this permanently deletes users and all of their associated data. Exam results must be stored long term!
|
||||||
UserAvsSync: Synchronise with AVS
|
UserAvsSync: Synchronise with AVS
|
||||||
UserLdapSync: Synchronise with LDAP
|
UserLdapSync: Synchronise with LDAP
|
||||||
AllUsersLdapSync: Synchronise all with LDAP
|
|
||||||
UserHijack: Hijack session
|
UserHijack: Hijack session
|
||||||
UserAddSupervisor: Add supervisor
|
UserAddSupervisor: Add supervisor
|
||||||
UserSetSupervisor: Replace supervisors
|
UserSetSupervisor: Replace supervisors
|
||||||
UserRemoveSupervisor: Set to unsupervised
|
UserRemoveSupervisor: Set to unsupervised
|
||||||
|
UserRemoveSubordinates: Remove all subordinates
|
||||||
|
UserIsSupervisor: Is supervisor
|
||||||
|
UserAvsSwitchCompany: Use as primary company
|
||||||
|
UserAvsSwitchCompanyField: Select primary company
|
||||||
|
UserAvsCompanySwitched c: Primary company switched to #{tshow c}
|
||||||
|
AllUsersLdapSync: Synchronise all with LDAP
|
||||||
|
AllUsersAvsSync: Synchronise all with AVS
|
||||||
|
ThisUserLdapSync: Synchronise user with LDAP
|
||||||
|
ThisUserAvsSync: Synchronise user with AVS
|
||||||
AuthKindLDAP: Fraport AG account
|
AuthKindLDAP: Fraport AG account
|
||||||
AuthKindPWHash: FRADrive account
|
AuthKindPWHash: FRADrive account
|
||||||
AuthKindNoLogin: No login
|
AuthKindNoLogin: No login
|
||||||
Name: Name
|
Name: Name
|
||||||
UsersChangeSupervisorsSuccess usr spr: #{pluralENsN spr "supervisor"} for #{pluralENsN usr "user"} set.
|
UsersChangeSupervisorsSuccess usr spr: #{pluralENsN spr "supervisor"} for #{pluralENsN usr "user"} set.
|
||||||
UsersChangeSupervisorsWarning usr spr bad: Only _{MsgUsersChangeSupervisorsSuccess usr spr} #{pluralENsN bad "supervisors"} could not be identified!
|
UsersChangeSupervisorsWarning usr spr bad: Only _{MsgUsersChangeSupervisorsSuccess usr spr} #{pluralENsN bad "supervisors"} could not be identified!
|
||||||
UsersRemoveSupervisors usr: Removed all supervisors for #{pluralENsN usr "user"}.
|
UsersRemoveSupervisors usr: Removed all supervisors for #{pluralENsN usr "user"}.
|
||||||
|
UsersRemoveSubordinates usr: Removed all subordinates for #{pluralENsN usr "previous supervisor"}.
|
||||||
|
UserCompanyReason: Reason for company association
|
||||||
|
UserCompanyReasonTooltip: Optional note for special cases. In some case this may prevent automatic removel upon AVS user company changes.
|
||||||
|
UserSupervisorReason: Reason for supervision
|
||||||
|
UserSupervisorReasonTooltip: Optional note for special cases. In some case this may prevent automatic removel upon AVS user company changes.
|
||||||
|
AdminUserAllNotifications: All notification sent to this user
|
||||||
@ -4,23 +4,31 @@
|
|||||||
|
|
||||||
#messages or constructors that are used all over the code
|
#messages or constructors that are used all over the code
|
||||||
|
|
||||||
Logo !ident-ok: Uni2work
|
Logo !ident-ok: FRADrive
|
||||||
EmailInvitationWarning: Diese Adresse konnte keinem Uni2work-Benutzer/keiner Uni2work-Benutzerin zugeordnet werden. Es wird eine Einladung per E-Mail versandt.
|
EmailInvitationWarning: Diese Adresse konnte keinem FRADrive-Benutzer/-Benutzerin zugeordnet werden. Es wird eine Einladung per E-Mail versandt.
|
||||||
BoolIrrelevant !ident-ok: —
|
BoolIrrelevant !ident-ok: —
|
||||||
FieldPrimary: Hauptfach
|
FieldPrimary: Hauptfach
|
||||||
FieldSecondary: Nebenfach
|
FieldSecondary: Nebenfach
|
||||||
MultiEmailFieldTip: Es sind mehrere, Komma-separierte, E-Mail-Adressen möglich
|
MultiEmailFieldTip: Es sind mehrere, Komma-separierte, E-Mail-Adressen möglich
|
||||||
|
MultiSelectTip: Mehrfachauswahl und Abwählen mit Strg-Klick
|
||||||
WeekDay: Wochentag
|
WeekDay: Wochentag
|
||||||
|
Hours: Stunden
|
||||||
LdapIdentificationOrEmail: Fraport AG-Kennung / E-Mail-Adresse
|
LdapIdentificationOrEmail: Fraport AG-Kennung / E-Mail-Adresse
|
||||||
Months num@Int64: #{num} #{pluralDE num "Monat" "Monate"}
|
Months num@Int64: #{num} #{pluralDE num "Monat" "Monate"}
|
||||||
Days num@Int64: #{num} #{pluralDE num "Tag" "Tage"}
|
Days num@Int64: #{num} #{pluralDE num "Tag" "Tage"}
|
||||||
|
NoAutomaticUpdateTip: Dieser Wert wurde manuell editiert und wird daher nicht mehr automatisch durch as AVS aktualisiert.
|
||||||
|
AddressIsLinkedTip: Verlinkte Postaddresse: Für diesen Benutzer ist keine individuelle Postadresse gespeichert, die Adresse wurde stattdessen aus der Firmenzugehörigkeit abgeleitet.
|
||||||
|
|
||||||
ClusterVolatileQuickActionsEnabled: Schnellzugriffsmenü aktiv
|
ClusterVolatileQuickActionsEnabled: Schnellzugriffsmenü aktiv
|
||||||
|
|
||||||
AvsNoLicence: Keine Fahrberechtigung
|
AvsNoLicence: Keine Fahrberechtigung
|
||||||
AvsLicenceVorfeld: Vorfeld Fahrberechtigung
|
AvsLicenceVorfeld: Vorfeld Fahrberechtigung
|
||||||
AvsLicenceRollfeld: Rollfeld Fahrberechtigung
|
AvsLicenceRollfeld: Rollfeld Fahrberechtigung
|
||||||
|
AvsNoLicenceGuest: Keine Fahrberechtigung (Gast, Fahrberechtigungserwerb nicht möglich)
|
||||||
|
|
||||||
PaginationSize: Einträge pro Seite
|
PaginationSize: Einträge pro Seite
|
||||||
PaginationPage: Angzeigte Seite
|
PaginationPage: Angzeigte Seite
|
||||||
PaginationError: Paginierung Parameter dürfen nicht negativ sein
|
PaginationError: Paginierung Parameter dürfen nicht negativ sein
|
||||||
|
|
||||||
|
NullDeletes: Zum Löschen NULL eingeben.
|
||||||
|
SortPriority: Sortierungspriorität
|
||||||
@ -4,23 +4,31 @@
|
|||||||
|
|
||||||
#messages or constructors that are used all over the Code
|
#messages or constructors that are used all over the Code
|
||||||
|
|
||||||
Logo: Uni2work
|
Logo: FRADrive
|
||||||
EmailInvitationWarning: This address could not be matched to any Uni2work user. An invitation will be sent via email.
|
EmailInvitationWarning: This address could not be matched to any FRADrive user. An invitation will be sent via email.
|
||||||
BoolIrrelevant: —
|
BoolIrrelevant: —
|
||||||
FieldPrimary: Major
|
FieldPrimary: Major
|
||||||
FieldSecondary: Minor
|
FieldSecondary: Minor
|
||||||
MultiEmailFieldTip: Multiple emails addresses may be specified (comma-separated)
|
MultiEmailFieldTip: Multiple emails addresses may be specified (comma-separated)
|
||||||
|
MultiSelectTip: Multiple selection and desection via Ctrl-Click
|
||||||
WeekDay: Day of the week
|
WeekDay: Day of the week
|
||||||
|
Hours: Hours
|
||||||
LdapIdentificationOrEmail: Fraport AG-Kennung / email address
|
LdapIdentificationOrEmail: Fraport AG-Kennung / email address
|
||||||
Months num: #{num} #{pluralEN num "Month" "Months"}
|
Months num: #{num} #{pluralEN num "Month" "Months"}
|
||||||
Days num: #{num} #{pluralEN num "Day" "Days"}
|
Days num: #{num} #{pluralEN num "Day" "Days"}
|
||||||
|
NoAutomaticUpdateTip: This particular value receives no automatic AVS updates, since it has been edited manually.
|
||||||
|
AddressIsLinkedTip: Linked postal address: No individual postal address is stored for this user, instead a postal address was inferred from the user's company association.
|
||||||
|
|
||||||
ClusterVolatileQuickActionsEnabled: Quick actions enabled
|
ClusterVolatileQuickActionsEnabled: Quick actions enabled
|
||||||
|
|
||||||
AvsNoLicence: No driving licence
|
AvsNoLicence: No driving licence
|
||||||
AvsLicenceVorfeld: Apron driving licence
|
AvsLicenceVorfeld: Apron driving licence
|
||||||
AvsLicenceRollfeld: Maneuvering area driving licence
|
AvsLicenceRollfeld: Maneuvering area driving licence
|
||||||
|
AvsNoLicenceGuest: No driving licence (Guest account, cannot acquire a diriving licence)
|
||||||
|
|
||||||
PaginationSize: Rows per Page
|
PaginationSize: Rows per Page
|
||||||
PaginationPage: Page to show
|
PaginationPage: Page to show
|
||||||
PaginationError: Pagination parameter must not be negative
|
PaginationError: Pagination parameter must not be negative
|
||||||
|
|
||||||
|
NullDeletes: Enter NULL to delete.
|
||||||
|
SortPriority: Sort order priority
|
||||||
@ -66,6 +66,7 @@ BreadcrumbFaq !ident-ok: FAQ
|
|||||||
BreadcrumbSheetPersonalisedFiles: Personalisierte Dateien herunterladen
|
BreadcrumbSheetPersonalisedFiles: Personalisierte Dateien herunterladen
|
||||||
BreadcrumbCourseSheetPersonalisedFiles: Vorlage für personalisierte Übungsblatt-Dateien herunterladen
|
BreadcrumbCourseSheetPersonalisedFiles: Vorlage für personalisierte Übungsblatt-Dateien herunterladen
|
||||||
BreadcrumbAdminCrontab !ident-ok: Crontab
|
BreadcrumbAdminCrontab !ident-ok: Crontab
|
||||||
|
BreadcrumbAdminJobs !ident-ok: Jobs
|
||||||
BreadcrumbError: Fehler
|
BreadcrumbError: Fehler
|
||||||
BreadcrumbUpload !ident-ok: Upload
|
BreadcrumbUpload !ident-ok: Upload
|
||||||
BreadcrumbUserAdd: Benutzer:in anlegen
|
BreadcrumbUserAdd: Benutzer:in anlegen
|
||||||
|
|||||||
@ -66,6 +66,7 @@ BreadcrumbFaq: FAQ
|
|||||||
BreadcrumbSheetPersonalisedFiles: Download personalised sheet files
|
BreadcrumbSheetPersonalisedFiles: Download personalised sheet files
|
||||||
BreadcrumbCourseSheetPersonalisedFiles: Download template for personalised sheet files
|
BreadcrumbCourseSheetPersonalisedFiles: Download template for personalised sheet files
|
||||||
BreadcrumbAdminCrontab: Crontab
|
BreadcrumbAdminCrontab: Crontab
|
||||||
|
BreadcrumbAdminJobs !ident-ok: Jobs
|
||||||
BreadcrumbError: Error
|
BreadcrumbError: Error
|
||||||
BreadcrumbUpload: Upload
|
BreadcrumbUpload: Upload
|
||||||
BreadcrumbUserAdd: Add user
|
BreadcrumbUserAdd: Add user
|
||||||
|
|||||||
@ -23,6 +23,7 @@ MenuPayments: Zahlungsbedingungen
|
|||||||
|
|
||||||
MenuInstance: Instanz-Identifikation
|
MenuInstance: Instanz-Identifikation
|
||||||
MenuHealth: Instanz-Zustand
|
MenuHealth: Instanz-Zustand
|
||||||
|
MenuHealthInterface: Schnittstellen Zustand
|
||||||
MenuHelp: Hilfe
|
MenuHelp: Hilfe
|
||||||
MenuProfile: Anpassen
|
MenuProfile: Anpassen
|
||||||
MenuLogin !ident-ok: Login
|
MenuLogin !ident-ok: Login
|
||||||
@ -38,6 +39,7 @@ MenuTermShow: Jahr
|
|||||||
MenuSubmissionDelete: Abgabe löschen
|
MenuSubmissionDelete: Abgabe löschen
|
||||||
MenuUsers: Benutzer:in
|
MenuUsers: Benutzer:in
|
||||||
MenuUserAdd: Benutzer:in anlegen
|
MenuUserAdd: Benutzer:in anlegen
|
||||||
|
MenuUserEdit: Benutzer:in editieren
|
||||||
MenuUserNotifications: Benachrichtigungs-Einstellungen
|
MenuUserNotifications: Benachrichtigungs-Einstellungen
|
||||||
MenuUserPassword: Passwort
|
MenuUserPassword: Passwort
|
||||||
MenuAdminTest: Admin-Demo
|
MenuAdminTest: Admin-Demo
|
||||||
@ -107,6 +109,7 @@ MenuFaq !ident-ok: FAQ
|
|||||||
MenuSheetPersonalisedFiles: Personalisierte Dateien herunterladen
|
MenuSheetPersonalisedFiles: Personalisierte Dateien herunterladen
|
||||||
MenuCourseSheetPersonalisedFiles: Vorlage für personalisierte Übungsblatt-Dateien herunterladen
|
MenuCourseSheetPersonalisedFiles: Vorlage für personalisierte Übungsblatt-Dateien herunterladen
|
||||||
MenuAdminCrontab !ident-ok: Crontab
|
MenuAdminCrontab !ident-ok: Crontab
|
||||||
|
MenuAdminJobs: Job Warteschlange
|
||||||
MenuGlossary: Begriffsverzeichnis
|
MenuGlossary: Begriffsverzeichnis
|
||||||
MenuVersion: Versionsgeschichte
|
MenuVersion: Versionsgeschichte
|
||||||
MenuCourseNewsNew: Neue Kursartnachricht
|
MenuCourseNewsNew: Neue Kursartnachricht
|
||||||
@ -118,22 +121,39 @@ MenuLanguage: Sprache
|
|||||||
MenuQualifications: Qualifikationen
|
MenuQualifications: Qualifikationen
|
||||||
MenuLms !ident-ok: E‑Learning
|
MenuLms !ident-ok: E‑Learning
|
||||||
MenuLmsEdit: Bearbeiten E‑Learning
|
MenuLmsEdit: Bearbeiten E‑Learning
|
||||||
MenuLmsUser: Benutzer Qualifikationen
|
MenuLmsUser: Benutzerqualifikationen
|
||||||
MenuLmsUsers: Export E‑Learning Benutzer
|
MenuLmsUserSchool: Bereichs Benutzerqualifikationen
|
||||||
MenuLmsUserlist: Melden E‑Learning Benutzer
|
MenuLmsUserAll: Alle Benutzerqualifikationen
|
||||||
MenuLmsResult: Melden Ergebnisse E‑Learning
|
MenuLmsUsers: Veralteter Export E‑Learning Benutzer
|
||||||
MenuLmsUpload: Hochladen
|
MenuLmsUpload: Hochladen
|
||||||
MenuLmsDirectUpload: Direkter Upload
|
MenuLmsDirectUpload: Direkter Upload
|
||||||
MenuLmsDirectDownload: Direkter Download
|
MenuLmsDirectDownload: Direkter Download
|
||||||
MenuLmsFake: Testnutzer generieren
|
MenuLmsFake: Testnutzer generieren
|
||||||
|
MenuLmsLearners: Export Benutzer E‑Learning
|
||||||
|
MenuLmsReport: Ergebnisse E‑Learning
|
||||||
|
|
||||||
|
MenuFirms: Firmen
|
||||||
|
MenuFirmUsers: Angehörige
|
||||||
|
MenuFirmSupervisors: Ansprechpartner
|
||||||
|
MenuFirmsComm: Mitteilung
|
||||||
|
|
||||||
|
MenuInterfaces: Schnittstellen
|
||||||
MenuSap: SAP Schnittstelle
|
MenuSap: SAP Schnittstelle
|
||||||
|
|
||||||
MenuAvs: AVS Schnittstelle
|
MenuAvs: AVS Schnittstelle
|
||||||
|
MenuAvsSynchError: AVS Problemübersicht
|
||||||
MenuLdap: LDAP Schnittstelle
|
MenuLdap: LDAP Schnittstelle
|
||||||
MenuApc: Druckerei
|
MenuApc: Druck
|
||||||
MenuPrintSend: Manueller Briefversand
|
MenuPrintSend: Manueller Briefversand
|
||||||
MenuPrintDownload: Brief herunterladen
|
MenuPrintDownload: Brief herunterladen
|
||||||
|
MenuPrintLog: LPR Schnittstelle
|
||||||
|
MenuPrintAck: Druckbestätigung
|
||||||
|
|
||||||
|
MenuCommCenter: Benachrichtigungen
|
||||||
|
MenuMailCenter: E‑Mails
|
||||||
|
MenuMailHtml !ident-ok: Html
|
||||||
|
MenuMailPlain !ident-ok: Text
|
||||||
|
MenuMailAttachment: Anhang
|
||||||
|
|
||||||
MenuApiDocs: API-Dokumentation (Englisch)
|
MenuApiDocs: API-Dokumentation (Englisch)
|
||||||
MenuSwagger !ident-ok: OpenAPI 2.0 (Swagger)
|
MenuSwagger !ident-ok: OpenAPI 2.0 (Swagger)
|
||||||
|
|||||||
@ -23,6 +23,7 @@ MenuPayments: Payment Terms
|
|||||||
|
|
||||||
MenuInstance: Instance identification
|
MenuInstance: Instance identification
|
||||||
MenuHealth: Instance health
|
MenuHealth: Instance health
|
||||||
|
MenuHealthInterface: Interface health
|
||||||
MenuHelp: Support
|
MenuHelp: Support
|
||||||
MenuProfile: Settings
|
MenuProfile: Settings
|
||||||
MenuLogin: Login
|
MenuLogin: Login
|
||||||
@ -38,6 +39,7 @@ MenuTermShow: Semesters
|
|||||||
MenuSubmissionDelete: Delete submission
|
MenuSubmissionDelete: Delete submission
|
||||||
MenuUsers: User
|
MenuUsers: User
|
||||||
MenuUserAdd: Add user
|
MenuUserAdd: Add user
|
||||||
|
MenuUserEdit: Edit user
|
||||||
MenuUserNotifications: Notification settings
|
MenuUserNotifications: Notification settings
|
||||||
MenuUserPassword: Password
|
MenuUserPassword: Password
|
||||||
MenuAdminTest: Admin-demo
|
MenuAdminTest: Admin-demo
|
||||||
@ -69,7 +71,6 @@ MenuCourseDelete: Delete course
|
|||||||
MenuSubmissionNew: Create submission
|
MenuSubmissionNew: Create submission
|
||||||
MenuSubmissionOwn: Submission
|
MenuSubmissionOwn: Submission
|
||||||
MenuCorrectors: Correctors
|
MenuCorrectors: Correctors
|
||||||
|
|
||||||
MenuSheetEdit: Edit exercise sheet
|
MenuSheetEdit: Edit exercise sheet
|
||||||
MenuSheetDelete: Delete exercise sheet
|
MenuSheetDelete: Delete exercise sheet
|
||||||
MenuSheetClone: Clone exercise sheet
|
MenuSheetClone: Clone exercise sheet
|
||||||
@ -108,6 +109,7 @@ MenuFaq: FAQ
|
|||||||
MenuSheetPersonalisedFiles: Download personalised sheet files
|
MenuSheetPersonalisedFiles: Download personalised sheet files
|
||||||
MenuCourseSheetPersonalisedFiles: Download template for personalised sheet files
|
MenuCourseSheetPersonalisedFiles: Download template for personalised sheet files
|
||||||
MenuAdminCrontab: Crontab
|
MenuAdminCrontab: Crontab
|
||||||
|
MenuAdminJobs: Job queue
|
||||||
MenuGlossary: Glossary
|
MenuGlossary: Glossary
|
||||||
MenuVersion: Version history
|
MenuVersion: Version history
|
||||||
MenuCourseNewsNew: Add course type news
|
MenuCourseNewsNew: Add course type news
|
||||||
@ -117,24 +119,41 @@ MenuCourseEventEdit: Edit course type occurrence
|
|||||||
MenuLanguage: Language
|
MenuLanguage: Language
|
||||||
|
|
||||||
MenuQualifications: Qualifications
|
MenuQualifications: Qualifications
|
||||||
MenuLms: E‑Learning
|
MenuLms: E‑learning
|
||||||
MenuLmsEdit: Edit E‑Learning
|
MenuLmsEdit: Edit e‑learning
|
||||||
MenuLmsUser: User Qualifications
|
MenuLmsUser: User Qualifications
|
||||||
MenuLmsUsers: Download E‑Learning Users
|
MenuLmsUserSchool: Institute User Qualifications
|
||||||
MenuLmsUserlist: Upload E‑Learning Users
|
MenuLmsUserAll: All User Qualifications
|
||||||
MenuLmsResult: Upload E‑Learning Results
|
MenuLmsUsers: Legacy download e‑learning users
|
||||||
MenuLmsUpload: Upload
|
MenuLmsUpload: Upload
|
||||||
MenuLmsDirectUpload: Direct Upload
|
MenuLmsDirectUpload: Direct Upload
|
||||||
MenuLmsDirectDownload: Direct Download
|
MenuLmsDirectDownload: Direct Download
|
||||||
MenuLmsFake: Generate test users
|
MenuLmsFake: Generate Test Users
|
||||||
|
MenuLmsLearners: E‑learning Users
|
||||||
|
MenuLmsReport: E‑learning Results
|
||||||
|
|
||||||
|
MenuFirms: Companies
|
||||||
|
MenuFirmUsers: Associates
|
||||||
|
MenuFirmSupervisors: Supervisors
|
||||||
|
MenuFirmsComm: Messaging
|
||||||
|
|
||||||
|
MenuInterfaces: Interfaces
|
||||||
MenuSap: SAP Interface
|
MenuSap: SAP Interface
|
||||||
|
|
||||||
MenuAvs: AVS Interface
|
MenuAvs: AVS Interface
|
||||||
|
MenuAvsSynchError: AVS Problem Overview
|
||||||
MenuLdap: LDAP Interface
|
MenuLdap: LDAP Interface
|
||||||
MenuApc: Printing
|
MenuApc: Print
|
||||||
MenuPrintSend: Send Letter
|
MenuPrintSend: Send Letter
|
||||||
MenuPrintDownload: Download Letter
|
MenuPrintDownload: Download Letter
|
||||||
|
MenuPrintLog: LPR Interface
|
||||||
|
MenuPrintAck: Acknowledge Printing
|
||||||
|
|
||||||
|
MenuCommCenter: Notifications
|
||||||
|
MenuMailCenter: Email
|
||||||
|
MenuMailHtml: Html
|
||||||
|
MenuMailPlain: Text
|
||||||
|
MenuMailAttachment: Attachment
|
||||||
|
|
||||||
MenuApiDocs: API documentation
|
MenuApiDocs: API documentation
|
||||||
MenuSwagger: OpenAPI 2.0 (Swagger)
|
MenuSwagger: OpenAPI 2.0 (Swagger)
|
||||||
|
|||||||
@ -16,7 +16,7 @@ TableTerm !ident-ok: Jahr
|
|||||||
TableCourseSchool: Bereich
|
TableCourseSchool: Bereich
|
||||||
TableSubmissionGroup: Feste Abgabegruppe
|
TableSubmissionGroup: Feste Abgabegruppe
|
||||||
TableNoSubmissionGroup: Keine feste Abgabegruppe
|
TableNoSubmissionGroup: Keine feste Abgabegruppe
|
||||||
TableMatrikelNr: AVS Nr
|
TableMatrikelNr: AVS Personennummer
|
||||||
TableSex: Geschlecht
|
TableSex: Geschlecht
|
||||||
TableBirthday: Geburtsdatum
|
TableBirthday: Geburtsdatum
|
||||||
TableSchool: Bereich
|
TableSchool: Bereich
|
||||||
@ -73,8 +73,46 @@ TableDiffDaysTooltip: Zeitspanne nach ISO 8601. Beispiel: "P2Y3M4D" ist eine Zei
|
|||||||
TableExamOfficeLabel: Label-Name
|
TableExamOfficeLabel: Label-Name
|
||||||
TableExamOfficeLabelStatus: Label-Farbe
|
TableExamOfficeLabelStatus: Label-Farbe
|
||||||
TableExamOfficeLabelPriority: Label-Priorität
|
TableExamOfficeLabelPriority: Label-Priorität
|
||||||
|
TableQualification: Qualifikation
|
||||||
TableQualifications: Qualifikationen
|
TableQualifications: Qualifikationen
|
||||||
TableCompany: Firma
|
TableCompany: Firma
|
||||||
|
TableCompanyFilter: Firma oder Nummer
|
||||||
|
TableCompanyShort: Firmenkürzel
|
||||||
TableCompanies: Firmen
|
TableCompanies: Firmen
|
||||||
|
TablePrimeCompany: Primäre Firma
|
||||||
|
TableCompanyNo: Firmennummer
|
||||||
TableCompanyNos: Firmennummern
|
TableCompanyNos: Firmennummern
|
||||||
|
TableCompanyUser: Firmenangehöriger
|
||||||
|
TableCompanyNrUsers: Firmenangehörige
|
||||||
|
TableCompanyNrSecondaryUsers: Sekundäre Firmenangehörige
|
||||||
|
TableCompanyReason: Notiz
|
||||||
|
TableCompanyNrSupers: Ansprechpartner
|
||||||
|
TableCompanyNrEmpSupervised: Firmenangehörige mit Ansprechpartner
|
||||||
|
TableCompanyNrEmpRerouted: Firmenangehörige mit Umleitung
|
||||||
|
TableCompanyNrEmpRerPost: Firmenangehörige mit postalischer Umleitung
|
||||||
|
TableCompanyNrSupersActive: Mitarbeiter mit Ansprechpartner
|
||||||
|
TableCompanyNrSupersDefault: Standard Ansprechpartner
|
||||||
|
TableCompanyNrForeignSupers: Firmenfremde Ansprechpartner
|
||||||
|
TableCompanyNrRerouteDefault: Standard Umleitungen
|
||||||
|
TableCompanyNrRerouteActive: Aktive Umleitungen
|
||||||
|
TableRerouteActive: Umleitung
|
||||||
|
TableCompanyPostalPreference: Benachrichtigungspräferenz neue Firmenangehörige
|
||||||
TableSupervisor: Ansprechpartner
|
TableSupervisor: Ansprechpartner
|
||||||
|
TableSupervisee: Ansprechpartner für
|
||||||
|
TableReason: Begründung
|
||||||
|
TableCreationTime: Erstellungszeit
|
||||||
|
TableJob !ident-ok: Job
|
||||||
|
TableJobContent !ident-ok: Parameter
|
||||||
|
TableJobLockTime: Bearbeitung seit
|
||||||
|
TableJobLockInstance: Bearbeiter
|
||||||
|
TableJobCreationInstance: Ersteller
|
||||||
|
ActJobDelete: Job entfernen
|
||||||
|
ActJobDeleteForce n@Int: Auch vor #{pluralDEnN n "Minute"} gesperrte Jobs entfernen
|
||||||
|
TableJobActDeleteFeedback n@Int m@Int: #{n}/#{m} Jobs entfernt
|
||||||
|
TableFilterComma: Es können mehrere alternative Suchkriterien mit Komma getrennt angegeben werden, wovon mindestens eines erfüllt werden muss.
|
||||||
|
TableFilterCommaPlus: Mehrere alternative Suchkriterien mit Komma trennen. Mindestens ein Suchkriterium muss erfüllt werden, zusätzlich zu allen Suchkriterien mit vorangestelltem Plus-Symbol.
|
||||||
|
TableFilterCommaPlusShort: Unterstützt mehrere Kriterien mit Komma-Plus, siehe oben.
|
||||||
|
TableFilterCommaName: Mehrere Namen mit Komma trennen.
|
||||||
|
TableFilterCommaNameNr: Mehrere Namen oder Nummern mit Komma trennen. Nummern werden nur exakt gesucht.
|
||||||
|
TableUserEdit: Benutzer bearbeiten
|
||||||
|
TableRows: Zeilen
|
||||||
@ -16,7 +16,7 @@ TableTerm: Year
|
|||||||
TableCourseSchool: Department
|
TableCourseSchool: Department
|
||||||
TableSubmissionGroup: Registered submission group
|
TableSubmissionGroup: Registered submission group
|
||||||
TableNoSubmissionGroup: No registered submission group
|
TableNoSubmissionGroup: No registered submission group
|
||||||
TableMatrikelNr: AVS No
|
TableMatrikelNr: AVS person no
|
||||||
TableSex: Sex
|
TableSex: Sex
|
||||||
TableBirthday: Birthday
|
TableBirthday: Birthday
|
||||||
TableSchool: Department
|
TableSchool: Department
|
||||||
@ -73,8 +73,46 @@ TableDiffDaysTooltip: Duration given according to ISO 8601. Example: "P2Y3M4D" i
|
|||||||
TableExamOfficeLabel: Label name
|
TableExamOfficeLabel: Label name
|
||||||
TableExamOfficeLabelStatus: Label colour
|
TableExamOfficeLabelStatus: Label colour
|
||||||
TableExamOfficeLabelPriority: Label priority
|
TableExamOfficeLabelPriority: Label priority
|
||||||
|
TableQualification: Qualification
|
||||||
TableQualifications: Qualifications
|
TableQualifications: Qualifications
|
||||||
TableCompany: Company
|
TableCompany: Company
|
||||||
|
TableCompanyFilter: Company/Nr
|
||||||
|
TableCompanyShort: Company shorthand
|
||||||
TableCompanies: Companies
|
TableCompanies: Companies
|
||||||
|
TablePrimeCompany: Primary company
|
||||||
|
TableCompanyNo: Company number
|
||||||
TableCompanyNos: Company numbers
|
TableCompanyNos: Company numbers
|
||||||
|
TableCompanyUser: Associate
|
||||||
|
TableCompanyNrUsers: Associates
|
||||||
|
TableCompanyNrSecondaryUsers: Secondary Associates
|
||||||
|
TableCompanyReason: Note
|
||||||
|
TableCompanyNrSupers: Supervisors
|
||||||
|
TableCompanyNrEmpSupervised: Supervised employees
|
||||||
|
TableCompanyNrEmpRerouted: Employees having reroute
|
||||||
|
TableCompanyNrEmpRerPost: Employees having postal reroute
|
||||||
|
TableCompanyNrSupersActive: Associates having supervisors
|
||||||
|
TableCompanyNrSupersDefault: Default supervisors
|
||||||
|
TableCompanyNrForeignSupers: External Supervisors
|
||||||
|
TableCompanyNrRerouteDefault: Default reroutes
|
||||||
|
TableCompanyNrRerouteActive: Active reroutes
|
||||||
|
TableRerouteActive: Reroute
|
||||||
|
TableCompanyPostalPreference: Default notification preference
|
||||||
TableSupervisor: Supervisor
|
TableSupervisor: Supervisor
|
||||||
|
TableSupervisee: Supervisor for
|
||||||
|
TableReason: Reason
|
||||||
|
TableCreationTime: Creation
|
||||||
|
TableJob !ident-ok: Job
|
||||||
|
TableJobContent !ident-ok: Parameters
|
||||||
|
TableJobLockTime: Lock time
|
||||||
|
TableJobLockInstance: Worker
|
||||||
|
TableJobCreationInstance: Creator
|
||||||
|
ActJobDelete: Delete job
|
||||||
|
ActJobDeleteForce n: Also delete jobs locked #{pluralENsN n "minute"} ago
|
||||||
|
TableJobActDeleteFeedback n@Int m@Int: #{n}/#{m} queued jobs deleted
|
||||||
|
TableFilterComma: Separate multiple alternative filter criteria by comma, at least one of which must be fulfilled.
|
||||||
|
TableFilterCommaPlus: Separate multiple alternative filter criteria by comma, at least one of which must be fulfilled in addition to all criteria preceded by a plus symbol.
|
||||||
|
TableFilterCommaPlusShort: Support multiple criteria with comma/plus, see above.
|
||||||
|
TableFilterCommaName: Separate names by comma.
|
||||||
|
TableFilterCommaNameNr: Separate names and numbers by comma. Numbers have to match exact.
|
||||||
|
TableUserEdit: Edit user
|
||||||
|
TableRows: Rows
|
||||||
@ -13,15 +13,19 @@ RGCourseUnacceptedApplicants: Nicht akzeptierte Bewerber:innen
|
|||||||
RecipientToggleAll: Alle/Keine
|
RecipientToggleAll: Alle/Keine
|
||||||
CommCourseTestSubject customSubject@Text !ident-ok: [TEST] #{customSubject}
|
CommCourseTestSubject customSubject@Text !ident-ok: [TEST] #{customSubject}
|
||||||
UtilCommCourseSubject: Kursartmitteilung
|
UtilCommCourseSubject: Kursartmitteilung
|
||||||
|
UtilCommFirmSubject: Firmenmitteilung
|
||||||
CommRecipients: Empfänger:innen
|
CommRecipients: Empfänger:innen
|
||||||
CommRecipientsTip: Sie selbst erhalten immer eine Kopie der Nachricht
|
CommRecipientsTip: Sie selbst erhalten immer eine Kopie der Nachricht
|
||||||
CommRecipientsList: Die an Sie selbst verschickte Kopie der Nachricht wird, zu Archivierungszwecken, eine vollständige Liste aller Empfänger:innen enthalten. Die Empfängerliste wird im CSV-Format an die E-Mail angehängt. Andere Empfänger:innen erhalten die Liste nicht. Bitte entfernen Sie dementsprechend den Anhang bevor Sie die E-Mail weiterleiten oder anderweitig mit Dritten teilen.
|
CommRecipientsList: Die an Sie selbst verschickte Kopie der Nachricht wird, zu Archivierungszwecken, eine vollständige Liste aller Empfänger:innen enthalten. Die Empfängerliste wird im CSV-Format an die E-Mail angehängt. Andere Empfänger:innen erhalten die Liste nicht. Bitte entfernen Sie dementsprechend den Anhang bevor Sie die E-Mail weiterleiten oder anderweitig mit Dritten teilen.
|
||||||
UtilEMail: E-Mail
|
UtilEMail: E-Mail
|
||||||
|
UtilPostal: Brief
|
||||||
|
UtilUnchanged: Nicht verändern
|
||||||
UtilMultiEmailFieldTip: Es sind mehrere, Komma-separierte, E-Mail-Adressen möglich
|
UtilMultiEmailFieldTip: Es sind mehrere, Komma-separierte, E-Mail-Adressen möglich
|
||||||
RGTutorialParticipants tutn@TutorialName: Kursteilnehmer:innen (#{tutn})
|
RGTutorialParticipants tutn@TutorialName: Kursteilnehmer:innen (#{tutn})
|
||||||
RGExamRegistered examn@ExamName: Angemeldet zur Prüfung „#{examn}“
|
RGExamRegistered examn@ExamName: Angemeldet zur Prüfung „#{examn}“
|
||||||
RGSheetSubmittor shn@SheetName: Abgebende für das Übungsblatt „#{shn}“
|
RGSheetSubmittor shn@SheetName: Abgebende für das Übungsblatt „#{shn}“
|
||||||
CommSubject: Betreff
|
CommSubject: Betreff
|
||||||
|
CommContent: Inhalt
|
||||||
CommAttachments: Anhänge
|
CommAttachments: Anhänge
|
||||||
CommAttachmentsTip: Im Allgemeinen ist es vorzuziehen Dateien, die Sie mit den Empfängern teilen möchten, als Material hochzuladen (und ggf. in der Nachricht zu verlinken). So ist die Datei für die Empfänger dauerhaft abrufbar und auch Personen, die sich z.B. erst später zur Kursart anmelden, haben Zugriff auf die Datei.
|
CommAttachmentsTip: Im Allgemeinen ist es vorzuziehen Dateien, die Sie mit den Empfängern teilen möchten, als Material hochzuladen (und ggf. in der Nachricht zu verlinken). So ist die Datei für die Empfänger dauerhaft abrufbar und auch Personen, die sich z.B. erst später zur Kursart anmelden, haben Zugriff auf die Datei.
|
||||||
CommSuccess n@Int: Nachricht wurde an #{n} Empfänger versandt
|
CommSuccess n@Int: Nachricht wurde an #{n} Empfänger versandt
|
||||||
@ -79,6 +83,7 @@ MultiUserFieldInvitationExplanationAlways: Es wird an alle Adressen, die Sie hie
|
|||||||
AmbiguousEmail: E-Mail-Adresse nicht eindeutig
|
AmbiguousEmail: E-Mail-Adresse nicht eindeutig
|
||||||
InvalidEmailAddress: E-Mail-Adresse ist ungültig
|
InvalidEmailAddress: E-Mail-Adresse ist ungültig
|
||||||
InvalidEmailAddressWith e@Text: E-Mail-Adresse #{show e} ist ungültig
|
InvalidEmailAddressWith e@Text: E-Mail-Adresse #{show e} ist ungültig
|
||||||
|
MailFileAttachment: Dateianhang
|
||||||
UtilExamResultGrade: Note
|
UtilExamResultGrade: Note
|
||||||
UtilExamResultPass: Bestanden/Nicht Bestanden
|
UtilExamResultPass: Bestanden/Nicht Bestanden
|
||||||
UtilExamResultNoShow: Nicht erschienen
|
UtilExamResultNoShow: Nicht erschienen
|
||||||
@ -93,6 +98,10 @@ RoomReferenceLinkLink !ident-ok: Link
|
|||||||
RoomReferenceLinkLinkPlaceholder !ident-ok: URL
|
RoomReferenceLinkLinkPlaceholder !ident-ok: URL
|
||||||
RoomReferenceLinkInstructions: Anweisungen
|
RoomReferenceLinkInstructions: Anweisungen
|
||||||
RoomReferenceLinkInstructionsPlaceholder: Anweisungen
|
RoomReferenceLinkInstructionsPlaceholder: Anweisungen
|
||||||
|
UtilNoneSet: Keine angegeben
|
||||||
|
UtilEmptyChoice: Auswahl war leer
|
||||||
|
UtilEmptyNoChangeTip: Eine leere Eingabe belässt den vorherigen Wert unverändert.
|
||||||
|
MultiNoSelection: Keine Auswahl
|
||||||
|
|
||||||
#invitation.hs
|
#invitation.hs
|
||||||
InvitationAction: Aktion
|
InvitationAction: Aktion
|
||||||
|
|||||||
@ -13,15 +13,19 @@ RGCourseUnacceptedApplicants: Applicants not accepted
|
|||||||
RecipientToggleAll: All/None
|
RecipientToggleAll: All/None
|
||||||
CommCourseTestSubject customSubject: [TEST] #{customSubject}
|
CommCourseTestSubject customSubject: [TEST] #{customSubject}
|
||||||
UtilCommCourseSubject: Course type message
|
UtilCommCourseSubject: Course type message
|
||||||
|
UtilCommFirmSubject: Company message
|
||||||
CommRecipients: Recipients
|
CommRecipients: Recipients
|
||||||
CommRecipientsTip: You always receive a copy of the message
|
CommRecipientsTip: You always receive a copy of the message
|
||||||
CommRecipientsList: For archival purposes the copy of the message sent to you will contain a complete list of all recipients. The list of recipients will be attached to the email in CSV-format. Other recipients do not receive the list. Thus, please remove the attachment before you forward the email or otherwise share it with third parties.
|
CommRecipientsList: For archival purposes the copy of the message sent to you will contain a complete list of all recipients. The list of recipients will be attached to the email in CSV-format. Other recipients do not receive the list. Thus, please remove the attachment before you forward the email or otherwise share it with third parties.
|
||||||
UtilEMail: Email
|
UtilEMail: Email
|
||||||
|
UtilPostal: Postal
|
||||||
|
UtilUnchanged: No change
|
||||||
UtilMultiEmailFieldTip: Multiple emails addresses may be specified (comma-separated)
|
UtilMultiEmailFieldTip: Multiple emails addresses may be specified (comma-separated)
|
||||||
RGTutorialParticipants tutn: Course participants (#{tutn})
|
RGTutorialParticipants tutn: Course participants (#{tutn})
|
||||||
RGExamRegistered examn: Registered for exam “#{examn}”
|
RGExamRegistered examn: Registered for exam “#{examn}”
|
||||||
RGSheetSubmittor shn: Submitted for exercise sheet “#{shn}”
|
RGSheetSubmittor shn: Submitted for exercise sheet “#{shn}”
|
||||||
CommSubject: Subject
|
CommSubject: Subject
|
||||||
|
CommContent: Content
|
||||||
CommAttachments: Attachments
|
CommAttachments: Attachments
|
||||||
CommAttachmentsTip: In general it is preferable to upload files as course type material instead of sending them as attachments. You can then link to the material from the message. The file is then permanently accessable to the recipients and to persons that, for example, register for the Course type at a later date.
|
CommAttachmentsTip: In general it is preferable to upload files as course type material instead of sending them as attachments. You can then link to the material from the message. The file is then permanently accessable to the recipients and to persons that, for example, register for the Course type at a later date.
|
||||||
CommSuccess n: Message was sent to #{n} #{pluralEN n "recipient" "recipients"}
|
CommSuccess n: Message was sent to #{n} #{pluralEN n "recipient" "recipients"}
|
||||||
@ -79,6 +83,7 @@ MultiUserFieldInvitationExplanationAlways: An invitation will be sent via email
|
|||||||
AmbiguousEmail: Email address is ambiguous
|
AmbiguousEmail: Email address is ambiguous
|
||||||
InvalidEmailAddress: Email address is invalid
|
InvalidEmailAddress: Email address is invalid
|
||||||
InvalidEmailAddressWith e: Email asdress #{show e} is invalid
|
InvalidEmailAddressWith e: Email asdress #{show e} is invalid
|
||||||
|
MailFileAttachment: Attached file
|
||||||
UtilExamResultGrade: Grade
|
UtilExamResultGrade: Grade
|
||||||
UtilExamResultPass: Passed/Failed
|
UtilExamResultPass: Passed/Failed
|
||||||
UtilExamResultNoShow: Not present
|
UtilExamResultNoShow: Not present
|
||||||
@ -93,6 +98,10 @@ RoomReferenceLinkLink: Link
|
|||||||
RoomReferenceLinkLinkPlaceholder: URL
|
RoomReferenceLinkLinkPlaceholder: URL
|
||||||
RoomReferenceLinkInstructions: Instructions
|
RoomReferenceLinkInstructions: Instructions
|
||||||
RoomReferenceLinkInstructionsPlaceholder: Instructions
|
RoomReferenceLinkInstructionsPlaceholder: Instructions
|
||||||
|
UtilNoneSet: None set
|
||||||
|
UtilEmptyChoice: Empty selection
|
||||||
|
UtilEmptyNoChangeTip: Existing values remain unchanged if this field is left empty.
|
||||||
|
MultiNoSelection: No selection
|
||||||
|
|
||||||
#invitation.hs
|
#invitation.hs
|
||||||
InvitationAction: Action
|
InvitationAction: Action
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>
|
-- SPDX-FileCopyrightText: 2022-24 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
|
||||||
--
|
--
|
||||||
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
@ -8,5 +8,31 @@ TransactionLog
|
|||||||
instance InstanceId
|
instance InstanceId
|
||||||
initiator UserId Maybe -- User associated with performing this action
|
initiator UserId Maybe -- User associated with performing this action
|
||||||
remote IP Maybe -- Remote party that triggered this action via HTTP
|
remote IP Maybe -- Remote party that triggered this action via HTTP
|
||||||
info Value -- JSON-encoded `Transaction`
|
info Value -- JSON-encoded `Transaction`. Value allows full backwards compatibility
|
||||||
|
deriving Eq Read Show Generic
|
||||||
|
|
||||||
|
InterfaceLog
|
||||||
|
interface Text
|
||||||
|
subtype Text
|
||||||
|
write Bool -- requestMethod /= GET, i.e. True implies a write to FRADrive
|
||||||
|
time UTCTime
|
||||||
|
rows Int Maybe -- number of datasets transmitted
|
||||||
|
info Text -- addtional status information
|
||||||
|
success Bool default=true -- false logs a failure; but it will be overwritten by next transaction, but logged in TransactionLog
|
||||||
|
UniqueInterfaceSubtypeWrite interface subtype write
|
||||||
|
deriving Eq Read Show Generic
|
||||||
|
|
||||||
|
InterfaceHealth
|
||||||
|
interface Text
|
||||||
|
subtype Text Maybe
|
||||||
|
write Bool Maybe
|
||||||
|
hours Int -- negative number: never expires, i.e. if the last entry is a success, this remains indefinitely
|
||||||
|
UniqueInterfaceHealth interface subtype write !force -- Note that nullable fields must be either empty or unique
|
||||||
|
deriving Eq Read Show Generic
|
||||||
|
|
||||||
|
ProblemLog
|
||||||
|
time UTCTime default=now()
|
||||||
|
info Value -- generic JSON Value allows maximum backwards compatibility
|
||||||
|
solved UTCTime Maybe
|
||||||
|
solver UserId Maybe -- User who marked this problem as done
|
||||||
deriving Eq Read Show Generic
|
deriving Eq Read Show Generic
|
||||||
@ -1,4 +1,4 @@
|
|||||||
-- SPDX-FileCopyrightText: 2022 Steffen Jost <jost@tcs.ifi.lmu.de>
|
-- SPDX-FileCopyrightText: 2022-24 Steffen Jost <jost@tcs.ifi.lmu.de>
|
||||||
--
|
--
|
||||||
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
@ -16,20 +16,19 @@
|
|||||||
UserAvs
|
UserAvs
|
||||||
personId AvsPersonId -- unique identifier for user throughout avs; newtype for Int
|
personId AvsPersonId -- unique identifier for user throughout avs; newtype for Int
|
||||||
user UserId
|
user UserId
|
||||||
noPerson Int default=0 -- only needed for manual communication with personnel from Ausweisverwaltungsstelle
|
noPerson Int default=0 -- only needed for manual communication with personnel from Ausweisverwaltungsstelle, redundant since needed for filtering
|
||||||
lastSynch UTCTime default=now()
|
lastSynch UTCTime default=now()
|
||||||
lastSynchError Text Maybe
|
lastSynchError Text Maybe
|
||||||
|
lastPersonInfo AvsPersonInfo Maybe -- just to discern field changes
|
||||||
|
lastFirmInfo AvsFirmInfo Maybe -- just to discern field changes
|
||||||
|
lastCardNo AvsFullCardNo Maybe -- just to discern changes
|
||||||
UniqueUserAvsUser user
|
UniqueUserAvsUser user
|
||||||
UniqueUserAvsId personId
|
UniqueUserAvsId personId
|
||||||
deriving Generic Show
|
deriving Generic Show
|
||||||
|
|
||||||
-- Multiple UserAvsCards per UserAvs is possible and not too uncommon.
|
AvsSync
|
||||||
-- Purpose of saving cards is to detect external changes in qualifications and postal addresses
|
user UserId -- Note: we need to lookup UserAvs Entity anyway, so no benefit from storing AvsPersonId here
|
||||||
-- TODO: This table will be deleted if AVS CR3 SCF-165 is implemented
|
creationTime UTCTime
|
||||||
UserAvsCard
|
pause Day Maybe -- Don't synch if last synch after this day, otherwise synch
|
||||||
personId AvsPersonId
|
UniqueAvsSyncUser user
|
||||||
cardNo AvsFullCardNo
|
deriving Generic Show
|
||||||
card AvsDataPersonCard
|
|
||||||
lastSynch UTCTime
|
|
||||||
-- UniqueAvsCard cardNo -- Note: cardNo is not unique; invalid cardNo may be reissued to different persons
|
|
||||||
deriving Generic
|
|
||||||
@ -1,24 +1,18 @@
|
|||||||
-- SPDX-FileCopyrightText: 2022 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
|
-- SPDX-FileCopyrightText: 2022-24 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
|
||||||
--
|
--
|
||||||
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
-- Description of companies associated with users
|
-- Description of companies associated with users
|
||||||
|
|
||||||
Company
|
Company
|
||||||
name CompanyName -- == (CI Text)
|
name CompanyName -- == (CI Text) -- NOTE: Fraport department name may carry additional information; use the Shorthand with respect to UserCompanyDepartment
|
||||||
shorthand CompanyShorthand -- == (CI Text) and CompanyKey :: CompanyShorthand -> CompanyId FUTURE TODO: a shorthand will become available through the AVS interface in the future
|
shorthand CompanyShorthand -- == (CI Text) and CompanyKey :: CompanyShorthand -> CompanyId A change to AvsId as primary key is too much work and not strictly necessary due to Uniqueness
|
||||||
avsId Int default=0 -- primary key from avs
|
avsId Int default=0 -- primary key from avs, use negative numbers for non-AVS companies
|
||||||
prefersPostal Bool default=false -- new company users prefers letters by post instead of email
|
prefersPostal Bool default=true -- new company users prefers letters by post instead of email
|
||||||
postAddress StoredMarkup Maybe -- default company postal address
|
postAddress StoredMarkup Maybe -- default company postal address, including company name
|
||||||
UniqueCompanyName name
|
email UserEmail Maybe -- Case-insensitive generic company eMail address
|
||||||
UniqueCompanyShorthand shorthand
|
-- UniqueCompanyName name -- Should be Unique in AVS, but we do not yet need to enforce it
|
||||||
-- UniqueCompanyAvsId avsId -- should be the case, unclear if enforcing works here, since we cannot query avs by company id
|
-- UniqueCompanyShorthand shorthand -- unnecessary, since it is the primary key already
|
||||||
Primary shorthand -- newtype Key Company = CompanyKey { unCompanyKey :: CompanyShorthand }
|
UniqueCompanyAvsId avsId -- Should be the key, is not for historical reasons and for convenience in URLs and columns
|
||||||
|
Primary shorthand -- newtype Key Company = CompanyKey { unCompanyKey :: CompanyShorthand }
|
||||||
deriving Ord Eq Show Generic Binary
|
deriving Ord Eq Show Generic Binary
|
||||||
|
|
||||||
-- TODO: a way to populate this table (manually)
|
|
||||||
CompanySynonym
|
|
||||||
synonym CompanyName
|
|
||||||
canonical CompanyShorthand OnDeleteCascade OnUpdateCascade
|
|
||||||
UniqueCompanySynonym synonym
|
|
||||||
deriving Ord Eq Show Generic
|
|
||||||
|
|||||||
@ -20,11 +20,11 @@ CronLastExec
|
|||||||
time UTCTime -- When was the job executed
|
time UTCTime -- When was the job executed
|
||||||
instance InstanceId -- Which uni2work-instance did the work
|
instance InstanceId -- Which uni2work-instance did the work
|
||||||
UniqueCronLastExec job
|
UniqueCronLastExec job
|
||||||
deriving Generic
|
deriving Generic Show
|
||||||
|
|
||||||
TokenBucket
|
TokenBucket
|
||||||
ident TokenBucketIdent
|
ident TokenBucketIdent
|
||||||
lastValue Int64
|
lastValue Int64
|
||||||
lastAccess UTCTime
|
lastAccess UTCTime
|
||||||
Primary ident
|
Primary ident
|
||||||
deriving Generic
|
deriving Generic Show
|
||||||
122
models/lms.model
122
models/lms.model
@ -1,4 +1,4 @@
|
|||||||
-- SPDX-FileCopyrightText: 2022 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
|
-- SPDX-FileCopyrightText: 2022-23 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
|
||||||
--
|
--
|
||||||
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
@ -13,16 +13,18 @@ Qualification
|
|||||||
refreshWithin CalendarDiffDays Maybe -- notify users about renewal within this number of month/days before expiry; to be used with addGregorianDurationClip
|
refreshWithin CalendarDiffDays Maybe -- notify users about renewal within this number of month/days before expiry; to be used with addGregorianDurationClip
|
||||||
refreshReminder CalendarDiffDays Maybe -- send a second notification about renewal within this number of month/days before expiry
|
refreshReminder CalendarDiffDays Maybe -- send a second notification about renewal within this number of month/days before expiry
|
||||||
elearningStart Bool -- automatically schedule e-refresher
|
elearningStart Bool -- automatically schedule e-refresher
|
||||||
-- elearningOnly Bool -- successful E-learing automatically increases validity. NO!
|
elearningRenews Bool default=true -- successful e-learing automatically increases validity automatically by validDuration
|
||||||
|
elearningLimit Int Maybe -- limit of e-learning attempts, currently only for informative purposes, as it is enforced by LMS only
|
||||||
|
lmsReuses QualificationId Maybe -- if set, lms is also included within the given qualification's lms, but only for direct routes. AuditDuration is used from this Qualification instead.
|
||||||
expiryNotification Bool default=true -- should expiryNotification be generated for this qualification?
|
expiryNotification Bool default=true -- should expiryNotification be generated for this qualification?
|
||||||
avsLicence AvsLicence Maybe -- if set, valid QualificationUsers are synchronized to AVS as a driving licence
|
avsLicence AvsLicence Maybe -- if set, valid QualificationUsers are synchronized to AVS as a driving licence
|
||||||
sapId Text Maybe -- if set, valid QualificationUsers with userCompanyPersonalNumber are transmitted via SAP interface under this id
|
sapId Text Maybe -- if set, valid QualificationUsers with userCompanyPersonalNumber are transmitted via SAP interface under this id
|
||||||
SchoolQualificationShort school shorthand -- must be unique per school and shorthand
|
SchoolQualificationShort school shorthand -- must be unique per school and shorthand
|
||||||
SchoolQualificationName school name -- must be unique per school and name
|
SchoolQualificationName school name -- must be unique per school and name
|
||||||
-- across all schools, only one qualification may be a driving licence:
|
-- across all schools, only one qualification may be a driving licence -- NO LONGER TRUE
|
||||||
UniqueQualificationAvsLicence avsLicence !force
|
-- UniqueQualificationAvsLicence avsLicence !force -- either empty or unique
|
||||||
-- NOTE: two NULL values are not equal for the purpose of Uniqueness constraints!
|
-- NOTE: two NULL values are not equal for the purpose of Uniqueness constraints!
|
||||||
deriving Eq Generic
|
deriving Show Eq Generic
|
||||||
|
|
||||||
-- TODOs:
|
-- TODOs:
|
||||||
-- - Enstehen Kosten, wenn Teilnehmer für KnowHow eingereiht werden, aber nicht am Kurs teilnehmen?
|
-- - Enstehen Kosten, wenn Teilnehmer für KnowHow eingereiht werden, aber nicht am Kurs teilnehmen?
|
||||||
@ -40,11 +42,20 @@ Qualification
|
|||||||
-- - PinReset==1 mit bestehendem Passwort kann problemlos erneut gesendet werden
|
-- - PinReset==1 mit bestehendem Passwort kann problemlos erneut gesendet werden
|
||||||
-- - Flag "interner Mitarbeiter" wird von Know-How ignoriert / nicht ausgewertet (legacy)
|
-- - Flag "interner Mitarbeiter" wird von Know-How ignoriert / nicht ausgewertet (legacy)
|
||||||
|
|
||||||
QualificationPrecondition
|
-- QualificationPrecondition -- NOTE: this can only be enforced through a background job adding or removing qualifications
|
||||||
qualification QualificationId OnDeleteCascade OnUpdateCascade -- AND: not unique, ie. qualification can have multiple required preconditions
|
-- qualification QualificationId OnDeleteCascade OnUpdateCascade -- AND: not unique, ie. qualification can have multiple required preconditions
|
||||||
required [QualificationId] -- OR : alternatives, any one will suffice
|
-- required [QualificationId] -- OR : alternatives, any one will suffice -- we don't want array, since we have recursive CTEs
|
||||||
continuous Bool -- expiring precondition removes qualification
|
-- continuous Bool -- expiring precondition blocks qualification
|
||||||
deriving Generic
|
-- deriving Generic Show
|
||||||
|
|
||||||
|
-- Maybe an alternative for online qualification validity checking, transitivity through recursive CTEs? (already available in our version)
|
||||||
|
QualificationRequirement
|
||||||
|
qualification QualificationId OnDeleteCascade OnUpdateCascade
|
||||||
|
requirement QualificationId OnDeleteCascade OnUpdateCascade
|
||||||
|
group Int -- OR: several requirements within the same group are considered equivalent; no order between groups
|
||||||
|
note Text -- for humans only, no semantical effect
|
||||||
|
UniqueQualificationRequirement qualification requirement
|
||||||
|
deriving Generic Show
|
||||||
|
|
||||||
-- TODO: connect Qualification with Exams!
|
-- TODO: connect Qualification with Exams!
|
||||||
|
|
||||||
@ -52,7 +63,7 @@ QualificationEdit
|
|||||||
user UserId
|
user UserId
|
||||||
time UTCTime
|
time UTCTime
|
||||||
qualification QualificationId OnDeleteCascade OnUpdateCascade
|
qualification QualificationId OnDeleteCascade OnUpdateCascade
|
||||||
deriving Generic
|
deriving Generic Show
|
||||||
|
|
||||||
QualificationUser
|
QualificationUser
|
||||||
user UserId OnDeleteCascade OnUpdateCascade
|
user UserId OnDeleteCascade OnUpdateCascade
|
||||||
@ -60,13 +71,20 @@ QualificationUser
|
|||||||
validUntil Day -- addGregorianMonthsRollOver (toInteger renewalMonths) qualificationUserValidUntil
|
validUntil Day -- addGregorianMonthsRollOver (toInteger renewalMonths) qualificationUserValidUntil
|
||||||
lastRefresh Day -- lastRefresh > validUntil possible, if Qualification^elearningOnly == False
|
lastRefresh Day -- lastRefresh > validUntil possible, if Qualification^elearningOnly == False
|
||||||
firstHeld Day -- first time the qualification was earned, should never change
|
firstHeld Day -- first time the qualification was earned, should never change
|
||||||
blockedDue QualificationBlocked Maybe -- isJust means that the qualification is currently revoked
|
|
||||||
scheduleRenewal Bool default=true -- if false, no automatic renewal is scheduled and the qualification expires
|
scheduleRenewal Bool default=true -- if false, no automatic renewal is scheduled and the qualification expires
|
||||||
lastNotified UTCTime default=now() -- last notficiation about being invalid
|
lastNotified UTCTime default=now() -- last notficiation about actual licence validity changes (does not entail e-learning notifications)
|
||||||
-- temporärer Entzug vorsehen -- SAP Schnittstelle muss dann angepasst werden
|
-- Reasons and temporary revocations are implemented through QualificationUserBlock
|
||||||
-- Begründungsfeld vorsehen
|
-- TODO: adjust SAP interface to transmit end dates
|
||||||
UniqueQualificationUser qualification user
|
UniqueQualificationUser qualification user
|
||||||
deriving Generic
|
deriving Generic Show
|
||||||
|
|
||||||
|
QualificationUserBlock
|
||||||
|
qualificationUser QualificationUserId OnDeleteCascade OnUpdateCascade
|
||||||
|
unblock Bool
|
||||||
|
from UTCTime
|
||||||
|
reason Text
|
||||||
|
blocker UserId Maybe
|
||||||
|
deriving Eq Ord Read Show Generic
|
||||||
|
|
||||||
-- LMS Interface Tables, need regular processing by background jobs, per QualificationId:
|
-- LMS Interface Tables, need regular processing by background jobs, per QualificationId:
|
||||||
--
|
--
|
||||||
@ -80,25 +98,20 @@ QualificationUser
|
|||||||
-- - delete-flag: isJust LmsUserStatus
|
-- - delete-flag: isJust LmsUserStatus
|
||||||
-- Note: REST means that LmsUserResetPin and LmsUserDelete remain unchanged by this GET request!
|
-- Note: REST means that LmsUserResetPin and LmsUserDelete remain unchanged by this GET request!
|
||||||
--
|
--
|
||||||
-- 3. REST POST Userlist.csv: just save as is to LmsUserlist
|
-- 3. REST POST Report.csv: just save as is to LmsReport for later processing
|
||||||
--
|
--
|
||||||
-- 4. REST POST Ergebnisse.csv: just save as is to LmsResult
|
-- 4. When received: Job LmsReport: -- Note: containment needs at-once processing
|
||||||
--
|
|
||||||
-- 5. When received: Job LmsUserlist: -- Note: containment needs at-once processing
|
|
||||||
-- - For all LmsUser:
|
-- - For all LmsUser:
|
||||||
-- + if contained:
|
-- + if contained:
|
||||||
-- set LmsUserReceived to Just now()
|
-- set LmsUserReceived to Just now()
|
||||||
-- if LmsUserlistFailed: set LmsUserStatus to Just LmsBlocked now
|
-- if Failed: set LmsUserStatus to Just LmsBlocked now
|
||||||
|
-- if Success: set LmsUserStatus to Just LmsSuccess now
|
||||||
|
-- and renew QualificationValidTo
|
||||||
-- + not contained, by LmsUserReceived is set: set LmsUserEnded to Just now()
|
-- + not contained, by LmsUserReceived is set: set LmsUserEnded to Just now()
|
||||||
-- - move row to LmsAudit
|
-- - move row to LmsAudit
|
||||||
--
|
--
|
||||||
-- 6. When received: Daily Job LmsResult:
|
-- 5. Daily Job: dequeue LMS Users
|
||||||
-- - set LmsUserReceived to Just now() -- always
|
-- - fail and mark expired LmsUser
|
||||||
-- - set LmsUserStatus to Just LmsSuccess now -- conditional
|
|
||||||
-- - and renew QualificationValidTo
|
|
||||||
-- - move row to LmsAudit
|
|
||||||
--
|
|
||||||
-- 7. Daily Job: dequeue LMS Users
|
|
||||||
-- - remove from LmsUser after audit Period has passed
|
-- - remove from LmsUser after audit Period has passed
|
||||||
|
|
||||||
LmsUser
|
LmsUser
|
||||||
@ -106,33 +119,48 @@ LmsUser
|
|||||||
user UserId OnDeleteCascade OnUpdateCascade
|
user UserId OnDeleteCascade OnUpdateCascade
|
||||||
ident LmsIdent -- must be unique accross all LMS courses!
|
ident LmsIdent -- must be unique accross all LMS courses!
|
||||||
pin Text
|
pin Text
|
||||||
resetPin Bool default=false -- should pin be reset?
|
resetPin Bool default=false -- should pin be reset?
|
||||||
datePin UTCTime default=now() -- time pin was created
|
datePin UTCTime default=now() -- time pin was created
|
||||||
status LmsStatus Maybe -- open, success or failure; status should never change unless isNothing; isJust indicates lms is finished and user shall be deleted from LMS
|
status LmsStatus Maybe -- Nothing=open, LmsSuccess, LmsBlocked or LmsExpired; status should never change unless isNothing; isJust indicates lms is finished and user shall be deleted from LMS
|
||||||
--toDelete encoded by Handler.Utils.LMS.lmsUserToDelete
|
--toDelete encoded by Handler.Utils.LMS.lmsUserToDelete
|
||||||
|
statusDay UTCTime Maybe -- last status change; should be isJust iff isJust status; modelling as a separate table too bothersome, unlike qualification block
|
||||||
started UTCTime default=now()
|
started UTCTime default=now()
|
||||||
received UTCTime Maybe -- last acknowledgement by LMS
|
received UTCTime Maybe -- last acknowledgement by LMS
|
||||||
notified UTCTime Maybe -- last notified by FRADrive
|
notified UTCTime Maybe -- last notified by FRADrive
|
||||||
ended UTCTime Maybe -- ident was deleted from LMS
|
ended UTCTime Maybe -- ident was deleted from LMS
|
||||||
-- Primary ident -- newtype Key LmsUserId = LmsUserKey { unLmsUser :: Text } -- change LmsIdent -> Text. Do we want this?
|
resetTries Bool default=false -- V2 should e-learning exam tries be reset?
|
||||||
|
locked Bool default=false -- V2 last returned lock status
|
||||||
|
-- Primary ident -- newtype Key LmsUserId = LmsUserKey { unLmsUser :: Text } -- change LmsIdent -> Text. Do we want this? No.
|
||||||
UniqueLmsIdent ident -- idents must be unique accross all qualifications, since idents are global within LMS!
|
UniqueLmsIdent ident -- idents must be unique accross all qualifications, since idents are global within LMS!
|
||||||
UniqueLmsQualificationUser qualification user -- each user may be enrolled at most once per course
|
UniqueLmsQualificationUser qualification user -- each user may be enrolled at most once per course
|
||||||
deriving Generic
|
|
||||||
|
|
||||||
-- LmsUserlist stores LMS upload for later processing only
|
|
||||||
LmsUserlist
|
|
||||||
qualification QualificationId OnDeleteCascade OnUpdateCascade
|
|
||||||
ident LmsIdent
|
|
||||||
failed Bool
|
|
||||||
timestamp UTCTime default=now()
|
|
||||||
UniqueLmsUserlist qualification ident
|
|
||||||
deriving Generic Show
|
deriving Generic Show
|
||||||
|
|
||||||
-- LmsResult stores LMS upload for later processing only
|
-- LmsUserStatus
|
||||||
LmsResult
|
-- lmsUser LmsUserId OnDeleteCascade OnUpdateCascade
|
||||||
|
-- result LmsStatus -- data LmsStatus = LmsBlocked | LmsExpired | LmsSuccess
|
||||||
|
-- day Day
|
||||||
|
-- UniqueLmsUserStatus lmsUser -- enforcing uniqueness prohibits history
|
||||||
|
-- deriving Generic
|
||||||
|
|
||||||
|
-- V2 Stores LMS upload for processing in Background Job
|
||||||
|
LmsReport
|
||||||
qualification QualificationId OnDeleteCascade OnUpdateCascade
|
qualification QualificationId OnDeleteCascade OnUpdateCascade
|
||||||
ident LmsIdent
|
ident LmsIdent
|
||||||
success Day -- BEWARE: timezone is local as submitted by LMS
|
date UTCTime Maybe -- BEWARE: timezone is local as submitted by LMS
|
||||||
|
result LmsState -- (0|1|2) 0=LmsFailed[too many tries], 1=LmsOpen, 2=LmsPassed[success]
|
||||||
|
lock Bool -- (0|1)
|
||||||
timestamp UTCTime default=now()
|
timestamp UTCTime default=now()
|
||||||
UniqueLmsResult qualification ident -- required by DBTable
|
UniqueLmsReport qualification ident -- required by DBTable
|
||||||
deriving Generic
|
deriving Generic Show
|
||||||
|
|
||||||
|
-- LmsAudit removed by commit 71cde92a
|
||||||
|
-- due to frequent transmit errors, a separate lms tranmission log is necessary again
|
||||||
|
LmsReportLog
|
||||||
|
qualification QualificationId OnDeleteCascade OnUpdateCascade
|
||||||
|
ident LmsIdent
|
||||||
|
date UTCTime Maybe -- BEWARE: timezone is local as submitted by LMS
|
||||||
|
result LmsState -- (0|1|2) 0=LmsFailed[too many tries], 1=LmsOpen, 2=LmsPassed[success]
|
||||||
|
lock Bool -- (0|1)
|
||||||
|
timestamp UTCTime default=now()
|
||||||
|
missing Bool default=false
|
||||||
|
deriving Generic Show
|
||||||
@ -9,11 +9,24 @@ PrintJob
|
|||||||
file ByteString -- stores plain pdf; otherwise use FileContentReference Maybe
|
file ByteString -- stores plain pdf; otherwise use FileContentReference Maybe
|
||||||
created UTCTime
|
created UTCTime
|
||||||
acknowledged UTCTime Maybe
|
acknowledged UTCTime Maybe
|
||||||
recipient UserId Maybe OnDeleteCascade OnUpdateCascade -- optional as some letters may contain just an address
|
recipient UserId Maybe OnDeleteSetNull OnUpdateCascade -- optional as some letters may contain just an address
|
||||||
|
affected UserId Maybe OnDeleteSetNull OnUpdateCascade -- subject of the letter
|
||||||
sender UserId Maybe OnDeleteSetNull OnUpdateCascade -- senders and associations are optional
|
sender UserId Maybe OnDeleteSetNull OnUpdateCascade -- senders and associations are optional
|
||||||
course CourseId Maybe OnDeleteCascade OnUpdateCascade
|
course CourseId Maybe OnDeleteCascade OnUpdateCascade
|
||||||
qualification QualificationId Maybe OnDeleteCascade OnUpdateCascade
|
qualification QualificationId Maybe OnDeleteCascade OnUpdateCascade
|
||||||
lmsUser LmsIdent Maybe OnDeleteCascade OnUpdateCascade -- allows tracking if recipient has been notified; must be unique
|
lmsUser LmsIdent Maybe OnDeleteSetNull OnUpdateCascade -- allows tracking if recipient has been notified; must be unique
|
||||||
-- UniquePrintJobLmsUser lmsUser -- Note that in fact multiple print jobs per LMS user are possible!
|
-- UniquePrintJobLmsUser lmsUser -- Note that in fact multiple print jobs per LMS user are possible!
|
||||||
-- UniquePrintJobApcIdent apcIdent -- TODO: not yet enforced, since LmsIdent is currently used
|
-- UniquePrintJobApcIdent apcIdent -- TODO: not yet enforced, since LmsIdent is currently used
|
||||||
deriving Generic
|
deriving Generic Show
|
||||||
|
|
||||||
|
PrintAcknowledge -- just to store acknowledging requests to be evaluated by a background job later on
|
||||||
|
apcIdent Text
|
||||||
|
timestamp UTCTime default=now()
|
||||||
|
processed Bool
|
||||||
|
deriving Generic Show
|
||||||
|
|
||||||
|
PrintAckIdAlias
|
||||||
|
needle Text
|
||||||
|
replacement Text
|
||||||
|
priority Int
|
||||||
|
deriving Generic Show
|
||||||
@ -10,8 +10,8 @@ School json
|
|||||||
examMinimumRegisterBeforeStart NominalDiffTime Maybe
|
examMinimumRegisterBeforeStart NominalDiffTime Maybe
|
||||||
examMinimumRegisterDuration NominalDiffTime Maybe
|
examMinimumRegisterDuration NominalDiffTime Maybe
|
||||||
examRequireModeForRegistration Bool default=false
|
examRequireModeForRegistration Bool default=false
|
||||||
examDiscouragedModes ExamModeDNF default='{"dnf-terms":[]}' -- This comment fixes syntax highlighting error only "
|
examDiscouragedModes ExamModeDNF
|
||||||
examCloseMode ExamCloseMode default='separate'
|
examCloseMode ExamCloseMode default='separate'
|
||||||
sheetAuthorshipStatementMode SchoolAuthorshipStatementMode default='optional'
|
sheetAuthorshipStatementMode SchoolAuthorshipStatementMode default='optional'
|
||||||
sheetAuthorshipStatementDefinition AuthorshipStatementDefinitionId Maybe
|
sheetAuthorshipStatementDefinition AuthorshipStatementDefinitionId Maybe
|
||||||
sheetAuthorshipStatementAllowOther Bool default=true
|
sheetAuthorshipStatementAllowOther Bool default=true
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@cip.ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
|
-- SPDX-FileCopyrightText: 2022-24 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
|
||||||
--
|
--
|
||||||
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
-- The files in /models determine the database scheme.
|
-- The files in /models determine t he database scheme.
|
||||||
-- The organisational split into several files has no operational effects.
|
-- The organisational split into several files has no operational effects.
|
||||||
-- White-space and case matters: Each SQL table is named in 1st column of this file
|
-- White-space and case matters: Each SQL table is named in 1st column of this file
|
||||||
-- Indendent lower-case lines describe the SQL-columns of the table with name, type and options
|
-- Indendent lower-case lines describe the SQL-columns of the table with name, type and options
|
||||||
@ -14,14 +14,14 @@
|
|||||||
User json -- Each Uni2work user has a corresponding row in this table; created upon first login.
|
User json -- Each Uni2work user has a corresponding row in this table; created upon first login.
|
||||||
surname UserSurname -- Display user names always through 'nameWidget displayName surname'
|
surname UserSurname -- Display user names always through 'nameWidget displayName surname'
|
||||||
displayName UserDisplayName
|
displayName UserDisplayName
|
||||||
displayEmail UserEmail
|
displayEmail UserEmail -- Case-insensitive eMail address, used for sending; leave empty for using auto-update CompanyEmail via UserCompany
|
||||||
email UserEmail -- Case-insensitive eMail address, used for sending TODO: make this nullable
|
email UserEmail -- Case-insensitive eMail address, used for identification and fallback for sending. Defaults to "AVSNO:dddddddd" if unknown
|
||||||
ident UserIdent -- Case-insensitive user-identifier
|
ident UserIdent -- Case-insensitive user-identifier. Defaults to "AVSID:dddddddd" if unknown
|
||||||
authentication AuthenticationMode -- 'AuthLDAP' or ('AuthPWHash'+password-hash)
|
authentication AuthenticationMode -- 'AuthLDAP' or ('AuthPWHash'+password-hash)
|
||||||
lastAuthentication UTCTime Maybe -- last login date
|
lastAuthentication UTCTime Maybe -- last login date
|
||||||
created UTCTime default=now()
|
created UTCTime default=now()
|
||||||
lastLdapSynchronisation UTCTime Maybe
|
lastLdapSynchronisation UTCTime Maybe
|
||||||
ldapPrimaryKey UserEduPersonPrincipalName Maybe
|
ldapPrimaryKey UserEduPersonPrincipalName Maybe -- Fraport Personnel Number or Email-Prefix for @fraport.de work here
|
||||||
tokensIssuedAfter UTCTime Maybe -- do not accept bearer tokens issued before this time (accept all tokens if null)
|
tokensIssuedAfter UTCTime Maybe -- do not accept bearer tokens issued before this time (accept all tokens if null)
|
||||||
matrikelnummer UserMatriculation Maybe -- usually a number; AVS Personalnummer; nicht Fraport Personalnummer!
|
matrikelnummer UserMatriculation Maybe -- usually a number; AVS Personalnummer; nicht Fraport Personalnummer!
|
||||||
firstName Text -- For export in tables, pre-split firstName from displayName
|
firstName Text -- For export in tables, pre-split firstName from displayName
|
||||||
@ -34,7 +34,7 @@ User json -- Each Uni2work user has a corresponding row in this table; create
|
|||||||
timeFormat DateTimeFormat "default='%R'" -- preferred Time-only display format for user; user-defined
|
timeFormat DateTimeFormat "default='%R'" -- preferred Time-only display format for user; user-defined
|
||||||
downloadFiles Bool default=false -- Should files be opened in browser or downloaded? (users often oblivious that their browser has a setting for this)
|
downloadFiles Bool default=false -- Should files be opened in browser or downloaded? (users often oblivious that their browser has a setting for this)
|
||||||
languages Languages Maybe -- Preferred language; user-defined
|
languages Languages Maybe -- Preferred language; user-defined
|
||||||
notificationSettings NotificationSettings "default='{}'::jsonb" -- Bit-array for which events email notifications are requested by user; user-defined
|
notificationSettings NotificationSettings "default='{}'::jsonb" -- Bit-array for which events email notifications are requested by user; user-defined; missing fields in json object will be parsed to default trigger
|
||||||
warningDays NominalDiffTime default=1209600 -- timedistance to pending deadlines for homepage infos
|
warningDays NominalDiffTime default=1209600 -- timedistance to pending deadlines for homepage infos
|
||||||
csvOptions CsvOptions "default='{}'::jsonb"
|
csvOptions CsvOptions "default='{}'::jsonb"
|
||||||
sex Sex Maybe -- currently ignored
|
sex Sex Maybe -- currently ignored
|
||||||
@ -44,9 +44,9 @@ User json -- Each Uni2work user has a corresponding row in this table; create
|
|||||||
mobile Text Maybe
|
mobile Text Maybe
|
||||||
companyPersonalNumber Text Maybe -- Company will become a new table, but if company=fraport, some information is received via LDAP
|
companyPersonalNumber Text Maybe -- Company will become a new table, but if company=fraport, some information is received via LDAP
|
||||||
companyDepartment Text Maybe -- thus we store such information for ease of reference directly, if available
|
companyDepartment Text Maybe -- thus we store such information for ease of reference directly, if available
|
||||||
pinPassword Text Maybe -- used to encrypt pins within emails
|
pinPassword Text Maybe -- used to encrypt pins within emails, defaults to cardno.version
|
||||||
postAddress StoredMarkup Maybe
|
postAddress StoredMarkup Maybe -- including company name, if any, but excluding username; leave empty for using auto-update CompanyPostAddress via UserCompany
|
||||||
postLastUpdate UTCTime Maybe -- record postal address updates
|
postLastUpdate UTCTime Maybe -- record postal address updates
|
||||||
prefersPostal Bool default=false -- user prefers letters by post instead of email
|
prefersPostal Bool default=false -- user prefers letters by post instead of email
|
||||||
examOfficeGetSynced Bool default=true -- whether synced status should be displayed for exam results by default
|
examOfficeGetSynced Bool default=true -- whether synced status should be displayed for exam results by default
|
||||||
examOfficeGetLabels Bool default=true -- whether labels should be displayed for exam results by default
|
examOfficeGetLabels Bool default=true -- whether labels should be displayed for exam results by default
|
||||||
@ -61,42 +61,47 @@ UserFunction -- Administratively assigned functions (lecturer, admin, evaluation
|
|||||||
function SchoolFunction
|
function SchoolFunction
|
||||||
UniqueUserFunction user school function
|
UniqueUserFunction user school function
|
||||||
deriving Generic
|
deriving Generic
|
||||||
UserSystemFunction
|
UserSystemFunction Show
|
||||||
user UserId
|
user UserId
|
||||||
function SystemFunction -- Defined in Model.Types.User
|
function SystemFunction -- Defined in Model.Types.User
|
||||||
manual Bool -- Inserted manually by Admin or automatic from LDAP
|
manual Bool -- Inserted manually by Admin or automatic from LDAP
|
||||||
isOptOut Bool -- User has currently deactivate the role for themselves
|
isOptOut Bool -- User has currently deactivate the role for themselves
|
||||||
UniqueUserSystemFunction user function
|
UniqueUserSystemFunction user function
|
||||||
deriving Generic
|
deriving Generic Show
|
||||||
UserExamOffice
|
UserExamOffice
|
||||||
user UserId
|
user UserId
|
||||||
field StudyTermsId
|
field StudyTermsId
|
||||||
UniqueUserExamOffice user field
|
UniqueUserExamOffice user field
|
||||||
deriving Generic
|
deriving Generic Show
|
||||||
UserSchool -- Managed by users themselves, encodes "schools of interest"
|
UserSchool -- Managed by users themselves, encodes "schools of interest"
|
||||||
user UserId
|
user UserId
|
||||||
school SchoolId
|
school SchoolId
|
||||||
isOptOut Bool -- true if this a marker, that the user manually deleted this entry; it should not be recreated automatically
|
isOptOut Bool -- true if this a marker, that the user manually deleted this entry; it should not be recreated automatically
|
||||||
UniqueUserSchool user school
|
UniqueUserSchool user school
|
||||||
deriving Generic
|
deriving Generic Show
|
||||||
UserGroupMember
|
UserGroupMember
|
||||||
group UserGroupName
|
group UserGroupName
|
||||||
user UserId
|
user UserId
|
||||||
primary Checkmark nullable
|
primary Checkmark nullable
|
||||||
UniquePrimaryUserGroupMember group primary !force
|
UniquePrimaryUserGroupMember group primary !force
|
||||||
UniqueUserGroupMember group user
|
UniqueUserGroupMember group user
|
||||||
deriving Generic
|
deriving Generic Show
|
||||||
UserCompany
|
UserCompany
|
||||||
user UserId
|
user UserId
|
||||||
company CompanyId OnDeleteCascade OnUpdateCascade
|
company CompanyId OnDeleteCascade OnUpdateCascade
|
||||||
supervisor Bool default=false -- should this user be made supervisor for all _new_ users associated with this company?
|
supervisor Bool default=false -- should this user be made supervisor for all _new_ users associated with this company?
|
||||||
supervisorReroute Bool default=false -- if supervisor is true, should this supervisor receive email for _new_ company users?
|
supervisorReroute Bool default=false -- if supervisor is true, should this supervisor receive email for _new_ company users?
|
||||||
|
priority Int default=0 -- higher number, higher priority; default=1 for Haskell-Code
|
||||||
|
useCompanyAddress Bool default=true -- if true, CompanyPostalAddress and CompanyEmail are used if UserPostalAddress/UserDisplayEmail are Nothing, respects priority
|
||||||
|
reason Text Maybe -- miscellaneous note, e.g. Superior
|
||||||
UniqueUserCompany user company -- a user may belong to multiple companies, but to each one only once
|
UniqueUserCompany user company -- a user may belong to multiple companies, but to each one only once
|
||||||
deriving Generic
|
deriving Generic Show
|
||||||
UserSupervisor
|
UserSupervisor
|
||||||
supervisor UserId -- multiple supervisor per trainee possible
|
supervisor UserId -- multiple supervisor per trainee possible
|
||||||
user UserId
|
user UserId
|
||||||
rerouteNotifications Bool -- User can be his own supervisor to receive notifications as well
|
rerouteNotifications Bool -- User can be his own supervisor to receive notifications as well
|
||||||
UniqueUserSupervisor supervisor user -- each supervisor/user combination is unique (same supervisor can superviser the same user only once)
|
company CompanyId Maybe OnDeleteCascade OnUpdateCascade -- this supervisor was company default supervisor at time of entry
|
||||||
deriving Generic
|
reason Text Maybe -- miscellaneous reason, e.g. Winterservice supervisision
|
||||||
|
UniqueUserSupervisor supervisor user -- each supervisor/user combination is unique (same supervisor can superviser the same user only once)
|
||||||
|
deriving Generic Show
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# SPDX-FileCopyrightText: 2022-2023 Gregor Kleen <gregor@kleen.consulting>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>
|
# SPDX-FileCopyrightText: 2022-2023 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor@kleen.consulting>, Steffen Jost <jost@tcs.ifi.lmu.de>
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
@ -14,11 +14,11 @@ let
|
|||||||
'');
|
'');
|
||||||
in if self ? lastModified then fromDate else "1970-01-01T00:00:01Z";
|
in if self ? lastModified then fromDate else "1970-01-01T00:00:01Z";
|
||||||
|
|
||||||
mkUniworxDocker = { isDemo }: prev.dockerTools.buildImage {
|
mkUniworxDocker = { isTest }: prev.dockerTools.buildImage {
|
||||||
name = "uniworx${optionalString isDemo "-demo"}";
|
name = "uniworx${optionalString isTest "-test"}";
|
||||||
tag =
|
tag =
|
||||||
let
|
let
|
||||||
versionFile = if isDemo then ./demo-version.json else ./version.json;
|
versionFile = if isTest then ./test-version.json else ./version.json;
|
||||||
in (builtins.fromJSON (prev.lib.readFile versionFile)).version;
|
in (builtins.fromJSON (prev.lib.readFile versionFile)).version;
|
||||||
inherit created;
|
inherit created;
|
||||||
|
|
||||||
@ -31,11 +31,12 @@ let
|
|||||||
busybox # should provide a working lpr -- to be tested
|
busybox # should provide a working lpr -- to be tested
|
||||||
htop
|
htop
|
||||||
pdftk # for encrypting pdfs
|
pdftk # for encrypting pdfs
|
||||||
|
roboto roboto-mono
|
||||||
#texlive.combined.scheme-medium # too large for container in LMU build environment.
|
#texlive.combined.scheme-medium # too large for container in LMU build environment.
|
||||||
(texlive.combine {
|
(texlive.combine {
|
||||||
inherit (texlive) scheme-basic
|
inherit (texlive) scheme-basic
|
||||||
babel-german babel-english booktabs textpos
|
babel-german babel-english booktabs textpos
|
||||||
enumitem eurosym koma-script parskip xcolor dejavu
|
enumitem eurosym koma-script parskip xcolor roboto xkeyval
|
||||||
# required fro LuaTeX
|
# required fro LuaTeX
|
||||||
luatexbase lualatex-math unicode-math selnolig
|
luatexbase lualatex-math unicode-math selnolig
|
||||||
;
|
;
|
||||||
@ -44,7 +45,7 @@ let
|
|||||||
curl wget netcat openldap
|
curl wget netcat openldap
|
||||||
unixtools.netstat htop gnugrep
|
unixtools.netstat htop gnugrep
|
||||||
locale
|
locale
|
||||||
] ++ optionals isDemo [ postgresql_12 memcached uniworx.uniworx.components.exes.uniworxdb ];
|
];
|
||||||
|
|
||||||
runAsRoot = ''
|
runAsRoot = ''
|
||||||
#!${final.stdenv.shell}
|
#!${final.stdenv.shell}
|
||||||
@ -62,18 +63,6 @@ let
|
|||||||
|
|
||||||
# just to see how to create directories here
|
# just to see how to create directories here
|
||||||
mkdir -p /testdir
|
mkdir -p /testdir
|
||||||
|
|
||||||
${optionalString isDemo ''
|
|
||||||
install -d -g uniworx -o uniworx -m 0750 /var/lib/postgres
|
|
||||||
|
|
||||||
install -d -g uniworx -o uniworx -m 0750 /var/lib/memcached
|
|
||||||
|
|
||||||
install -d -g uniworx -o uniworx -m 0755 /var/log/postgres
|
|
||||||
install -d -g uniworx -o uniworx -m 0755 /var/log/memcached
|
|
||||||
|
|
||||||
mkdir -p /run
|
|
||||||
install -d -g uniworx -o uniworx -m 0755 /run/postgres
|
|
||||||
''}
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
config =
|
config =
|
||||||
@ -83,27 +72,6 @@ let
|
|||||||
|
|
||||||
cTime=$(date -Is)
|
cTime=$(date -Is)
|
||||||
|
|
||||||
${optionalString isDemo ''
|
|
||||||
pgDir=/var/lib/postgres
|
|
||||||
pgSockDir=/run/postgres
|
|
||||||
pgLogFile=/var/log/postgres/''${cTime}.log
|
|
||||||
export PGHOST=''${pgSockDir}
|
|
||||||
export PGLOG=''${pgLogFile}
|
|
||||||
|
|
||||||
pgNew=
|
|
||||||
if [[ -n "$(find ''${pgDir} -maxdepth 0 -type d -empty 2>/dev/null)" ]]; then
|
|
||||||
pgNew=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
[[ -z "''${pgNew}" ]] || initdb --no-locale --encoding=UTF8 --username postgres --pgdata ''${pgDir}
|
|
||||||
pg_ctl start -D ''${pgDir} -l ''${pgLogFile} -w -o "-k ''${pgSockDir} -c listen_addresses= -c hba_file=${postgresHba} -c unix_socket_permissions=0777 -c max_connections=9990 -c shared_preload_libraries=pg_stat_statements -c auto_explain.log_min_duration=100ms"
|
|
||||||
[[ -z "''${pgNew}" ]] || psql -f ${postgresSchema} postgres postgres
|
|
||||||
|
|
||||||
( cd /var/lib/memcached; memcached -p 11212 ) &>/var/log/memcached/''${cTime}.log &
|
|
||||||
export SESSION_MEMCACHED_HOST=localhost
|
|
||||||
export SESSION_MEMCACHED_PORT=11212
|
|
||||||
''}
|
|
||||||
|
|
||||||
# export LOGDEST=/var/log/uniworx/''${cTime}.log # kubernetes prefers log via stdout
|
# export LOGDEST=/var/log/uniworx/''${cTime}.log # kubernetes prefers log via stdout
|
||||||
typeset -a configs
|
typeset -a configs
|
||||||
configs=()
|
configs=()
|
||||||
@ -115,9 +83,6 @@ let
|
|||||||
fi
|
fi
|
||||||
configs+=('${uniworxConfig}')
|
configs+=('${uniworxConfig}')
|
||||||
cd /var/lib/uniworx
|
cd /var/lib/uniworx
|
||||||
${optionalString isDemo ''
|
|
||||||
[[ -z "''${pgNew}" ]] || uniworxdb -f ''${configs}
|
|
||||||
''}
|
|
||||||
exec -- uniworx ''${configs}
|
exec -- uniworx ''${configs}
|
||||||
'';
|
'';
|
||||||
postgresSchema = prev.writeText "schema.sql" ''
|
postgresSchema = prev.writeText "schema.sql" ''
|
||||||
@ -142,13 +107,11 @@ let
|
|||||||
Volumes = {
|
Volumes = {
|
||||||
"/var/lib/uniworx" = {};
|
"/var/lib/uniworx" = {};
|
||||||
"/var/log" = {};
|
"/var/log" = {};
|
||||||
} // optionalAttrs isDemo {
|
|
||||||
"/var/lib/postgres" = {};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
mapAttrs (_name: mkUniworxDocker) {
|
mapAttrs (_name: mkUniworxDocker) {
|
||||||
uniworxDemoDocker = { isDemo = true; };
|
uniworxTestDocker = { isTest = true; };
|
||||||
uniworxDocker = { isDemo = false; };
|
uniworxDocker = { isTest = false; };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "27.4.11"
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
3
nix/docker/test-version.json
Normal file
3
nix/docker/test-version.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"version": "27.4.18"
|
||||||
|
}
|
||||||
3
nix/docker/test-version.json.license
Normal file
3
nix/docker/test-version.json.license
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
SPDX-FileCopyrightText: 2022-2023 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"version": "27.4.11"
|
"version": "27.4.79"
|
||||||
}
|
}
|
||||||
|
|||||||
3
nix/frontend/node-env.nix.license
Normal file
3
nix/frontend/node-env.nix.license
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
SPDX-FileCopyrightText: 2022-2023 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor@kleen.consulting>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
3
nix/frontend/node-packages.nix.license
Normal file
3
nix/frontend/node-packages.nix.license
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
SPDX-FileCopyrightText: 2022-2023 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor@kleen.consulting>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "uni2work",
|
"name": "uni2work",
|
||||||
"version": "27.4.11",
|
"version": "27.4.79",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "uni2work",
|
"name": "uni2work",
|
||||||
"version": "27.4.11",
|
"version": "27.4.79",
|
||||||
"description": "",
|
"description": "",
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
@ -23,9 +23,9 @@
|
|||||||
"frontend:build": "webpack --progress",
|
"frontend:build": "webpack --progress",
|
||||||
"frontend:build:watch": "webpack --watch --progress",
|
"frontend:build:watch": "webpack --watch --progress",
|
||||||
"i18n:test": "./missing-translations.sh",
|
"i18n:test": "./missing-translations.sh",
|
||||||
"prerelease": "npm run test",
|
"prerelease": "./is-clean.sh && npm run test",
|
||||||
"release": "standard-version -a",
|
"release": "./release.sh",
|
||||||
"postrelease": "git push --follow-tags origin master",
|
"postrelease": "git push --follow-tags",
|
||||||
"parse-changelog": "changelog-parser ./CHANGELOG.md > changelog.json"
|
"parse-changelog": "changelog-parser ./CHANGELOG.md > changelog.json"
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
SPDX-FileCopyrightText: 2022 Felix Hamann <felix.hamann@campus.lmu.de>,Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
|
SPDX-FileCopyrightText: 2022-2023 Sarah Vaupel <sarah.vaupel@uniworx.de>, Felix Hamann <felix.hamann@campus.lmu.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
name: uniworx
|
name: uniworx
|
||||||
version: 27.4.11
|
version: 27.4.79
|
||||||
dependencies:
|
dependencies:
|
||||||
- base
|
- base
|
||||||
- yesod
|
- yesod
|
||||||
@ -259,6 +259,7 @@ ghc-options:
|
|||||||
- -j
|
- -j
|
||||||
- -freduction-depth=0
|
- -freduction-depth=0
|
||||||
- -fprof-auto-calls
|
- -fprof-auto-calls
|
||||||
|
- -g
|
||||||
when:
|
when:
|
||||||
- condition: flag(pedantic)
|
- condition: flag(pedantic)
|
||||||
ghc-options:
|
ghc-options:
|
||||||
|
|||||||
20
release.sh
Executable file
20
release.sh
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# SPDX-FileCopyrightText: 2023 Sarah Vaupel <sarah.vaupel@uniworx.de>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
case "$(git rev-parse --abbrev-ref HEAD)" in
|
||||||
|
"master" | "main")
|
||||||
|
standard-version -a
|
||||||
|
;;
|
||||||
|
"test")
|
||||||
|
standard-version -a -t t
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Current branch not supported for release!"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
SPDX-FileCopyrightText: 2023 Steffen Jost <S.Jost@Fraport.de>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: LicenseRef-Fraport-Corporate-Design
|
||||||
3
resources/FraportIcons.zip.license
Normal file
3
resources/FraportIcons.zip.license
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
SPDX-FileCopyrightText: 2023 Steffen Jost <S.Jost@Fraport.de>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: LicenseRef-Fraport-Corporate-Design
|
||||||
3
resources/fraport_icons_übersicht_2018-11-15.pdf.license
Normal file
3
resources/fraport_icons_übersicht_2018-11-15.pdf.license
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
SPDX-FileCopyrightText: 2023 Steffen Jost <S.Jost@Fraport.de>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: LicenseRef-Fraport-Corporate-Design
|
||||||
102
routes
102
routes
@ -1,4 +1,4 @@
|
|||||||
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Wolfgang Witt <Wolfgang.Witt@campus.lmu.de>
|
-- SPDX-FileCopyrightText: 2022-24 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Wolfgang Witt <Wolfgang.Witt@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>
|
||||||
--
|
--
|
||||||
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
@ -52,9 +52,9 @@
|
|||||||
|
|
||||||
/ NewsR GET !free
|
/ NewsR GET !free
|
||||||
/users UsersR GET POST -- no tags, i.e. admins only
|
/users UsersR GET POST -- no tags, i.e. admins only
|
||||||
/users/#CryptoUUIDUser AdminUserR GET POST
|
/users/#CryptoUUIDUser AdminUserR GET POST
|
||||||
/users/#CryptoUUIDUser/delete AdminUserDeleteR POST
|
/users/#CryptoUUIDUser/delete AdminUserDeleteR POST
|
||||||
/users/#CryptoUUIDUser/hijack AdminHijackUserR POST !adminANDno-escalation
|
/users/#CryptoUUIDUser/hijack AdminHijackUserR GET POST !adminANDno-escalation
|
||||||
/users/#CryptoUUIDUser/notifications UserNotificationR GET POST !self
|
/users/#CryptoUUIDUser/notifications UserNotificationR GET POST !self
|
||||||
/users/#CryptoUUIDUser/password UserPasswordR GET POST !selfANDis-pw-hash
|
/users/#CryptoUUIDUser/password UserPasswordR GET POST !selfANDis-pw-hash
|
||||||
!/users/functionary-invite/new AdminNewFunctionaryInviteR GET POST
|
!/users/functionary-invite/new AdminNewFunctionaryInviteR GET POST
|
||||||
@ -66,35 +66,46 @@
|
|||||||
/admin/errMsg AdminErrMsgR GET POST
|
/admin/errMsg AdminErrMsgR GET POST
|
||||||
/admin/tokens AdminTokensR GET POST
|
/admin/tokens AdminTokensR GET POST
|
||||||
/admin/crontab AdminCrontabR GET
|
/admin/crontab AdminCrontabR GET
|
||||||
|
/admin/crontab/jobs AdminJobsR GET POST
|
||||||
/admin/avs AdminAvsR GET POST
|
/admin/avs AdminAvsR GET POST
|
||||||
/admin/avs/#CryptoUUIDUser AdminAvsUserR GET
|
/admin/avs/#CryptoUUIDUser AdminAvsUserR GET POST
|
||||||
/admin/ldap AdminLdapR GET POST
|
/admin/ldap AdminLdapR GET POST
|
||||||
/admin/problems AdminProblemsR GET
|
/admin/problems AdminProblemsR GET POST
|
||||||
/admin/problems/no-contact ProblemUnreachableR GET
|
/admin/problems/no-contact ProblemUnreachableR GET POST
|
||||||
/admin/problems/no-avs-id ProblemWithoutAvsId GET
|
/admin/problems/no-avs-id ProblemWithoutAvsId GET
|
||||||
/admin/problems/r-without-f ProblemFbutNoR GET
|
/admin/problems/r-without-f ProblemFbutNoR GET
|
||||||
/admin/problems/avs ProblemAvsSynchR GET POST
|
/admin/problems/avs ProblemAvsSynchR GET POST
|
||||||
|
/admin/problems/avs/errors ProblemAvsErrorR GET
|
||||||
|
/admin/config/interfaces ConfigInterfacesR GET POST
|
||||||
|
|
||||||
|
/comm CommCenterR GET
|
||||||
|
/comm/email MailCenterR GET POST
|
||||||
|
/comm/email/html/#CryptoUUIDSentMail MailHtmlR GET
|
||||||
|
/comm/email/plain/#CryptoUUIDSentMail MailPlainR GET
|
||||||
|
/comm/email/attachment/#CryptoUUIDSentMail/#Text MailAttachmentR GET
|
||||||
|
|
||||||
/print PrintCenterR GET POST !system-printer
|
/print PrintCenterR GET POST !system-printer
|
||||||
/print/acknowledge/#Day/#Int/#Int PrintAckR GET POST !system-printer
|
/print/acknowledge/#Day/#Int/#Int PrintAckR GET POST !system-printer
|
||||||
/print/acknowledge/direct PrintAckDirectR POST !system-printer
|
/print/acknowledge/direct PrintAckDirectR GET POST !system-printer
|
||||||
/print/send PrintSendR GET POST
|
/print/send PrintSendR GET POST
|
||||||
/print/download/#CryptoUUIDPrintJob PrintDownloadR GET !system-printer
|
/print/download/#CryptoUUIDPrintJob PrintDownloadR GET !system-printer
|
||||||
|
/print/log PrintLogR GET !system-printer
|
||||||
|
|
||||||
/health HealthR GET !free
|
/health HealthR GET !free
|
||||||
/instance InstanceR GET !free
|
/health/interface/+Texts HealthInterfaceR GET !free
|
||||||
/info InfoR GET !free
|
/instance InstanceR GET !free
|
||||||
/info/lecturer InfoLecturerR GET !free
|
/info InfoR GET !free
|
||||||
/info/supervisor InfoSupervisorR GET !free
|
/info/lecturer InfoLecturerR GET !free
|
||||||
/info/legal LegalR GET !free
|
/info/supervisor InfoSupervisorR GET !free
|
||||||
/info/glossary GlossaryR GET !free
|
/info/legal LegalR GET !free
|
||||||
/info/faq FaqR GET !free
|
/info/glossary GlossaryR GET !free
|
||||||
/info/terms-of-use TermsOfUseR GET !free
|
/info/faq FaqR GET !free
|
||||||
/info/payments PaymentsR GET !free
|
/info/terms-of-use TermsOfUseR GET !free
|
||||||
/imprint ImprintR GET !free
|
/info/payments PaymentsR GET !free
|
||||||
/data-protection DataProtectionR GET !free
|
/imprint ImprintR GET !free
|
||||||
/version VersionR GET !free
|
/data-protection DataProtectionR GET !free
|
||||||
/status StatusR GET !free
|
/version VersionR GET !free
|
||||||
|
/status StatusR GET !free
|
||||||
|
|
||||||
/help HelpR GET POST !free
|
/help HelpR GET POST !free
|
||||||
|
|
||||||
@ -111,6 +122,11 @@
|
|||||||
/for/#CryptoUUIDUser/user ForProfileR GET POST !supervisor !self
|
/for/#CryptoUUIDUser/user ForProfileR GET POST !supervisor !self
|
||||||
/for/#CryptoUUIDUser/user/profile ForProfileDataR GET !supervisor !self
|
/for/#CryptoUUIDUser/user/profile ForProfileDataR GET !supervisor !self
|
||||||
|
|
||||||
|
/firms FirmAllR GET POST -- not yet !supervisor
|
||||||
|
/firms/comm/+Companies FirmsCommR GET POST
|
||||||
|
/firm/#CompanyShorthand/comm FirmCommR GET POST
|
||||||
|
/firm/#CompanyShorthand FirmUsersR GET POST -- not yet !supervisor
|
||||||
|
/firm/#CompanyShorthand/supers FirmSupersR GET POST -- not yet !supervisor
|
||||||
|
|
||||||
/exam-office ExamOfficeR !exam-office:
|
/exam-office ExamOfficeR !exam-office:
|
||||||
/ EOExamsR GET POST !system-exam-office
|
/ EOExamsR GET POST !system-exam-office
|
||||||
@ -260,29 +276,29 @@
|
|||||||
!/#UUID CryptoUUIDDispatchR GET !free -- just redirect
|
!/#UUID CryptoUUIDDispatchR GET !free -- just redirect
|
||||||
-- !/*{CI FilePath} CryptoFileNameDispatchR GET !free -- Disabled until preliminary check for valid cID exists
|
-- !/*{CI FilePath} CryptoFileNameDispatchR GET !free -- Disabled until preliminary check for valid cID exists
|
||||||
|
|
||||||
/qualification QualificationAllR GET !free
|
/qualification QualificationAllR GET !free
|
||||||
/qualification/#SchoolId QualificationSchoolR GET !free
|
/qualification/#SchoolId QualificationSchoolR GET !free
|
||||||
/qualification/#SchoolId/#QualificationShorthand QualificationR GET POST !free
|
/qualification/#SchoolId/#QualificationShorthand QualificationR GET POST !free
|
||||||
/qualifications/sap/direct QualificationSAPDirectR GET -- !token -- SAP EXPORT -- TODO reinstate token requirement
|
-- /qualification/#SchoolId/#QualificationShorthand/#CryptoUUIDUser QualificationUserR GET -- see LmsUserR
|
||||||
-- /qualification/CryptoUUIDUser/ -- maybe distingquish via URL
|
/qualifications/sap/direct QualificationSAPDirectR GET -- !token -- SAP EXPORT -- TODO reinstate token requirement
|
||||||
|
|
||||||
|
|
||||||
-- LMS
|
-- LMS
|
||||||
/lms LmsAllR GET POST
|
/lms LmsAllR GET POST
|
||||||
/lms/#SchoolId LmsSchoolR GET
|
/lms/#SchoolId LmsSchoolR GET
|
||||||
/lms/#SchoolId/#QualificationShorthand LmsR GET POST
|
/lms/#SchoolId/#QualificationShorthand LmsR GET POST
|
||||||
/lms/#SchoolId/#QualificationShorthand/edit LmsEditR GET POST
|
/lms/#SchoolId/#QualificationShorthand/edit LmsEditR GET POST
|
||||||
/lms/#SchoolId/#QualificationShorthand/users LmsUsersR GET
|
-- new V2 LMS Interface
|
||||||
/lms/#SchoolId/#QualificationShorthand/users/direct LmsUsersDirectR GET !token -- LMS
|
/lms/#SchoolId/#QualificationShorthand/learners LmsLearnersR GET
|
||||||
/lms/#SchoolId/#QualificationShorthand/userlist LmsUserlistR GET POST
|
/lms/#SchoolId/#QualificationShorthand/learners/direct LmsLearnersDirectR GET !token -- LMS
|
||||||
/lms/#SchoolId/#QualificationShorthand/userlist/upload LmsUserlistUploadR GET POST !development
|
/lms/#SchoolId/#QualificationShorthand/report LmsReportR GET POST
|
||||||
/lms/#SchoolId/#QualificationShorthand/userlist/direct LmsUserlistDirectR POST !token -- LMS
|
/lms/#SchoolId/#QualificationShorthand/report/upload LmsReportUploadR GET POST
|
||||||
/lms/#SchoolId/#QualificationShorthand/result LmsResultR GET POST
|
/lms/#SchoolId/#QualificationShorthand/report/direct LmsReportDirectR POST !token -- LMS
|
||||||
/lms/#SchoolId/#QualificationShorthand/result/upload LmsResultUploadR GET POST !development
|
-- other lms routes
|
||||||
/lms/#SchoolId/#QualificationShorthand/result/direct LmsResultDirectR POST !token -- LMS
|
/lms/#SchoolId/#QualificationShorthand/ident/#LmsIdent LmsIdentR GET -- redirect to LmsR with filter-parameter
|
||||||
/lms/#SchoolId/#QualificationShorthand/ident/#LmsIdent LmsIdentR GET -- redirect to LmsR with filter-parameter
|
/lms/#SchoolId/#QualificationShorthand/user/#CryptoUUIDUser LmsUserR GET
|
||||||
/lmsuser/#CryptoUUIDUser LmsUserR GET
|
/lmsuser/#CryptoUUIDUser LmsUserAllR GET
|
||||||
|
/lmsuser/#CryptoUUIDUser/#SchoolId LmsUserSchoolR GET
|
||||||
|
|
||||||
|
|
||||||
/api ApiDocsR GET !free
|
/api ApiDocsR GET !free
|
||||||
/swagger SwaggerR GET !free
|
/swagger SwaggerR GET !free
|
||||||
|
|||||||
15
shell.nix
15
shell.nix
@ -197,9 +197,9 @@ let
|
|||||||
UPLOAD_S3_KEY_ID=''${MINIO_ACCESS_KEY}
|
UPLOAD_S3_KEY_ID=''${MINIO_ACCESS_KEY}
|
||||||
UPLOAD_S3_KEY=''${MINIO_SECRET_KEY}
|
UPLOAD_S3_KEY=''${MINIO_SECRET_KEY}
|
||||||
|
|
||||||
SMTPHOST=''${SMTPHOST}
|
# SMTPHOST=''${SMTPHOST}
|
||||||
SMTPPORT=''${SMTPPORT}
|
# SMTPPORT=''${SMTPPORT}
|
||||||
SMTPSSL=''${SMTPSSL}
|
# SMTPSSL=''${SMTPSSL}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
set +xe
|
set +xe
|
||||||
@ -223,7 +223,7 @@ let
|
|||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
killallUni2work = pkgs.writeScriptBin "killall-uni2work" ''
|
killallUni2work = pkgs.writeScriptBin "killuni2work" ''
|
||||||
#!${pkgs.zsh}/bin/zsh
|
#!${pkgs.zsh}/bin/zsh
|
||||||
|
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
@ -275,20 +275,21 @@ in pkgs.mkShell {
|
|||||||
++ (with pkgs;
|
++ (with pkgs;
|
||||||
[ stack nodejs-14_x postgresql_12 openldap exiftool memcached minio minio-client
|
[ stack nodejs-14_x postgresql_12 openldap exiftool memcached minio minio-client
|
||||||
gup reuse pre-commit
|
gup reuse pre-commit
|
||||||
node2nix
|
# node2nix
|
||||||
# busybox # for print services, but interferes with build commands in develop-shell
|
# busybox # for print services, but interferes with build commands in develop-shell
|
||||||
htop
|
htop
|
||||||
pdftk # pdftk just for testing pdf-passwords
|
pdftk # pdftk just for testing pdf-passwords
|
||||||
|
roboto roboto-mono
|
||||||
# texlive.combined.scheme-full # works
|
# texlive.combined.scheme-full # works
|
||||||
# texlive.combined.scheme-medium
|
# texlive.combined.scheme-medium
|
||||||
# texlive.combined.scheme-small
|
# texlive.combined.scheme-small
|
||||||
(texlive.combine {
|
(texlive.combine {
|
||||||
inherit (texlive) scheme-basic
|
inherit (texlive) scheme-basic
|
||||||
babel-german babel-english booktabs textpos
|
babel-german babel-english booktabs textpos
|
||||||
enumitem eurosym koma-script parskip xcolor dejavu
|
enumitem eurosym koma-script parskip xcolor roboto xkeyval
|
||||||
luatexbase lualatex-math unicode-math selnolig # required for LuaTeX
|
luatexbase lualatex-math unicode-math selnolig # required for LuaTeX
|
||||||
;
|
;
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
) ++ (with pkgs.haskellPackages; [ yesod-bin hlint cabal-install weeder profiteur ]);
|
) ++ (with pkgs.haskellPackages; [ yesod-bin hlint cabal-install weeder ]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -124,7 +124,7 @@ import Handler.Utils.Memcached (manageMemcachedLocalInvalidations)
|
|||||||
|
|
||||||
import qualified System.Clock as Clock
|
import qualified System.Clock as Clock
|
||||||
|
|
||||||
import Utils.Avs
|
import Utils.Avs (mkAvsQuery)
|
||||||
|
|
||||||
-- Import all relevant handler modules here.
|
-- Import all relevant handler modules here.
|
||||||
-- (HPack takes care to add new modules to our cabal file nowadays.)
|
-- (HPack takes care to add new modules to our cabal file nowadays.)
|
||||||
@ -145,6 +145,7 @@ import Handler.Material
|
|||||||
import Handler.CryptoIDDispatch
|
import Handler.CryptoIDDispatch
|
||||||
import Handler.SystemMessage
|
import Handler.SystemMessage
|
||||||
import Handler.Health
|
import Handler.Health
|
||||||
|
import Handler.Health.Interface
|
||||||
import Handler.Exam
|
import Handler.Exam
|
||||||
import Handler.ExamOffice
|
import Handler.ExamOffice
|
||||||
import Handler.Metrics
|
import Handler.Metrics
|
||||||
@ -156,9 +157,12 @@ import Handler.Upload
|
|||||||
import Handler.Qualification
|
import Handler.Qualification
|
||||||
import Handler.LMS
|
import Handler.LMS
|
||||||
import Handler.SAP
|
import Handler.SAP
|
||||||
|
import Handler.CommCenter
|
||||||
|
import Handler.MailCenter
|
||||||
import Handler.PrintCenter
|
import Handler.PrintCenter
|
||||||
import Handler.ApiDocs
|
import Handler.ApiDocs
|
||||||
import Handler.Swagger
|
import Handler.Swagger
|
||||||
|
import Handler.Firm
|
||||||
|
|
||||||
import ServantApi () -- YesodSubDispatch instances
|
import ServantApi () -- YesodSubDispatch instances
|
||||||
import Servant.API
|
import Servant.API
|
||||||
@ -350,15 +354,15 @@ makeFoundation appSettings''@AppSettings{..} = do
|
|||||||
handleIf isBucketExists (const $ return ()) $ Minio.makeBucket appUploadTmpBucket Nothing
|
handleIf isBucketExists (const $ return ()) $ Minio.makeBucket appUploadTmpBucket Nothing
|
||||||
return conn
|
return conn
|
||||||
|
|
||||||
appAvsQuery <- case appAvsConf of
|
appAvsQuery <- case appAvsConf of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
$logErrorS "avsPrepare" "appAvsConfig is empty, i.e. invalid AVS configuration settings."
|
$logErrorS "avsPrepare" "appAvsConfig is empty, i.e. invalid AVS configuration settings."
|
||||||
return Nothing
|
return Nothing
|
||||||
-- error "AvsConfig is empty, i.e. invalid AVS configuration settings."
|
-- error "AvsConfig is empty, i.e. invalid AVS configuration settings."
|
||||||
|
|
||||||
Just avsConf -> do
|
Just avsConf -> do
|
||||||
manager <- newManagerSettings $ mkManagerSettings (def { settingDisableCertificateValidation = True }) Nothing
|
manager <- newManagerSettings $ mkManagerSettings (def { settingDisableCertificateValidation = True }) Nothing
|
||||||
let avsServer = BaseUrl
|
let avsServer = BaseUrl
|
||||||
{ baseUrlScheme = Https
|
{ baseUrlScheme = Https
|
||||||
, baseUrlHost = avsHost avsConf
|
, baseUrlHost = avsHost avsConf
|
||||||
, baseUrlPort = avsPort avsConf
|
, baseUrlPort = avsPort avsConf
|
||||||
@ -550,8 +554,8 @@ warpSettings foundation = defaultSettings
|
|||||||
& Set.filter (is _Just . (foundation ^. _appHealthCheckInterval))
|
& Set.filter (is _Just . (foundation ^. _appHealthCheckInterval))
|
||||||
atomically $ do
|
atomically $ do
|
||||||
results <- readTVar $ foundation ^. _appHealthReport
|
results <- readTVar $ foundation ^. _appHealthReport
|
||||||
guard $ activeChecks == Set.map (classifyHealthReport . snd) results
|
guard $ activeChecks `Set.isSubsetOf` Set.map (classifyHealthReport . snd) results
|
||||||
guard . (== Min HealthSuccess) $ foldMap (Min . healthReportStatus . snd) results
|
guard . (/= Min HealthFailure) $ foldMap (Min . healthReportStatus . snd) results
|
||||||
notifyReady
|
notifyReady
|
||||||
| otherwise
|
| otherwise
|
||||||
-> notifyReady
|
-> notifyReady
|
||||||
@ -655,7 +659,7 @@ appMain = runResourceT $ do
|
|||||||
notifyWatchdog = forever' Nothing $ \pResults -> do
|
notifyWatchdog = forever' Nothing $ \pResults -> do
|
||||||
let delay = floor $ wInterval % 4
|
let delay = floor $ wInterval % 4
|
||||||
d <- liftIO $ newDelay delay
|
d <- liftIO $ newDelay delay
|
||||||
|
|
||||||
$logDebugS "Notify" $ "Waiting up to " <> tshow delay <> "µs..."
|
$logDebugS "Notify" $ "Waiting up to " <> tshow delay <> "µs..."
|
||||||
mResults <- atomically $ asum
|
mResults <- atomically $ asum
|
||||||
[ pResults <$ waitDelay d
|
[ pResults <$ waitDelay d
|
||||||
@ -679,7 +683,7 @@ appMain = runResourceT $ do
|
|||||||
interval <- mInterval
|
interval <- mInterval
|
||||||
let lastSuccess = maybeMonoid mResults
|
let lastSuccess = maybeMonoid mResults
|
||||||
& Set.filter (\(_, rep) -> classifyHealthReport rep == hc)
|
& Set.filter (\(_, rep) -> classifyHealthReport rep == hc)
|
||||||
& Set.filter (\(_, rep) -> healthReportStatus rep >= HealthSuccess)
|
& Set.filter (\(_, rep) -> healthReportStatus rep > HealthFailure)
|
||||||
& Set.mapMonotonic (view _1)
|
& Set.mapMonotonic (view _1)
|
||||||
& Set.lookupMax
|
& Set.lookupMax
|
||||||
|
|
||||||
@ -744,8 +748,8 @@ shutdownApp app = do
|
|||||||
|
|
||||||
-- | Run a handler
|
-- | Run a handler
|
||||||
handler, handler' :: Handler a -> IO a
|
handler, handler' :: Handler a -> IO a
|
||||||
handler h = runResourceT $ getAppDevSettings >>= makeFoundation >>= liftIO . flip unsafeHandler h
|
handler h = runResourceT $ getAppDevSettings >>= makeFoundation >>= liftIO . flip unsafeHandler h
|
||||||
handler' h = runResourceT $ getAppSettings >>= makeFoundation >>= liftIO . flip unsafeHandler h
|
handler' h = runResourceT $ getAppSettings >>= makeFoundation >>= liftIO . flip unsafeHandler h
|
||||||
|
|
||||||
-- | Run DB queries
|
-- | Run DB queries
|
||||||
db, db' :: DB a -> IO a
|
db, db' :: DB a -> IO a
|
||||||
|
|||||||
96
src/Audit.hs
96
src/Audit.hs
@ -1,13 +1,17 @@
|
|||||||
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>
|
-- SPDX-FileCopyrightText: 2023-24 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
|
||||||
--
|
--
|
||||||
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
module Audit
|
module Audit
|
||||||
( module Audit.Types
|
( module Audit.Types
|
||||||
, AuditException(..)
|
, AuditException(..)
|
||||||
, audit
|
, audit
|
||||||
, AuditRemoteException(..)
|
, AuditRemoteException(..)
|
||||||
, getRemote
|
, getRemote
|
||||||
|
, logInterface, logInterface'
|
||||||
|
, reportAdminProblem
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
|
||||||
@ -15,8 +19,11 @@ import Import.NoModel
|
|||||||
import Settings
|
import Settings
|
||||||
import Model
|
import Model
|
||||||
import Database.Persist.Sql
|
import Database.Persist.Sql
|
||||||
|
import qualified Database.Esqueleto.Experimental as E -- needs TypeApplications Lang-Pragma
|
||||||
|
import qualified Database.Esqueleto.Utils as E
|
||||||
import Audit.Types
|
import Audit.Types
|
||||||
|
|
||||||
|
import qualified Data.Text as Text
|
||||||
import qualified Data.Text.Encoding as Text
|
import qualified Data.Text.Encoding as Text
|
||||||
|
|
||||||
import Utils.Lens
|
import Utils.Lens
|
||||||
@ -102,12 +109,93 @@ audit :: ( AuthId (HandlerSite m) ~ Key User
|
|||||||
-- - `transactionLogInitiator` is currently logged in user (or none)
|
-- - `transactionLogInitiator` is currently logged in user (or none)
|
||||||
-- - `transactionLogRemote` is determined from current HTTP-Request
|
-- - `transactionLogRemote` is determined from current HTTP-Request
|
||||||
audit transaction@(toJSON -> transactionLogInfo) = do
|
audit transaction@(toJSON -> transactionLogInfo) = do
|
||||||
|
|
||||||
transactionLogTime <- liftIO getCurrentTime
|
transactionLogTime <- liftIO getCurrentTime
|
||||||
transactionLogInstance <- getsYesod $ view instanceID
|
transactionLogInstance <- getsYesod $ view instanceID
|
||||||
transactionLogInitiator <- liftHandler maybeAuthId
|
transactionLogInitiator <- liftHandler maybeAuthId
|
||||||
transactionLogRemote <- handle (throwM . AuditRemoteException) $ Just <$> getRemote
|
transactionLogRemote <- handle (throwM . AuditRemoteException) $ Just <$> getRemote
|
||||||
|
|
||||||
insert_ TransactionLog{..}
|
insert_ TransactionLog{..}
|
||||||
|
$logInfoS "Audit" $ Text.filter (/= '\n') $ tshow (transaction, transactionLogInitiator, transactionLogRemote) <> " - " <> pack (prettyCallStack callStack)
|
||||||
|
|
||||||
|
logInterface :: ( AuthId (HandlerSite m) ~ Key User
|
||||||
|
, IsSqlBackend (YesodPersistBackend (HandlerSite m))
|
||||||
|
, SqlBackendCanWrite (YesodPersistBackend (HandlerSite m))
|
||||||
|
, HasInstanceID (HandlerSite m) InstanceId
|
||||||
|
, YesodAuthPersist (HandlerSite m)
|
||||||
|
, MonadHandler m
|
||||||
|
, MonadCatch m
|
||||||
|
, HasAppSettings (HandlerSite m)
|
||||||
|
, HasCallStack
|
||||||
|
)
|
||||||
|
=> Text -- ^ Interface that is used
|
||||||
|
-> Text -- ^ Subtype of the interface, if any
|
||||||
|
-> Bool -- ^ Success=True, Failure=False
|
||||||
|
-> Maybe Int -- ^ Number of transmitted datasets
|
||||||
|
-> Text -- ^ Any additional information
|
||||||
|
-> ReaderT (YesodPersistBackend (HandlerSite m)) m ()
|
||||||
|
-- ^ Log a transaction using information available from `HandlerT`, also calls `audit`
|
||||||
|
logInterface interfaceLogInterface interfaceLogSubtype interfaceLogSuccess interfaceLogRows interfaceLogInfo = do
|
||||||
|
interfaceLogWrite <- (methodGet /=) . Wai.requestMethod . reqWaiRequest <$> getRequest
|
||||||
|
logInterface' interfaceLogInterface interfaceLogSubtype interfaceLogWrite interfaceLogSuccess interfaceLogRows interfaceLogInfo
|
||||||
|
|
||||||
|
logInterface' :: ( AuthId (HandlerSite m) ~ Key User
|
||||||
|
, IsSqlBackend (YesodPersistBackend (HandlerSite m))
|
||||||
|
, SqlBackendCanWrite (YesodPersistBackend (HandlerSite m))
|
||||||
|
, HasInstanceID (HandlerSite m) InstanceId
|
||||||
|
, YesodAuthPersist (HandlerSite m)
|
||||||
|
, MonadHandler m
|
||||||
|
, MonadCatch m
|
||||||
|
, HasAppSettings (HandlerSite m)
|
||||||
|
, HasCallStack
|
||||||
|
)
|
||||||
|
=> Text -- ^ Interface that is used
|
||||||
|
-> Text -- ^ Subtype of the interface, if any
|
||||||
|
-> Bool -- ^ True indicates Write Access to FRADrive
|
||||||
|
-> Bool -- ^ Success=True, Failure=False
|
||||||
|
-> Maybe Int -- ^ Number of transmitted datasets
|
||||||
|
-> Text -- ^ Any additional information
|
||||||
|
-> ReaderT (YesodPersistBackend (HandlerSite m)) m ()
|
||||||
|
-- ^ Log a transaction using information available from `HandlerT`, also calls `audit`
|
||||||
|
logInterface' (Text.strip -> interfaceLogInterface) (Text.strip -> interfaceLogSubtype) interfaceLogWrite interfaceLogSuccess interfaceLogRows (Text.strip -> interfaceLogInfo) = do
|
||||||
|
interfaceLogTime <- liftIO getCurrentTime
|
||||||
|
-- deleteBy $ UniqueInterfaceSubtypeWrite interfaceLogInterface interfaceLogSubtype interfaceLogWrite -- deleteBy & insert would be justified here, leading to a new Row-ID, since the two rows are not truly connected.
|
||||||
|
-- insert_ InterfaceLog{..}
|
||||||
|
void $ upsertBy (UniqueInterfaceSubtypeWrite interfaceLogInterface interfaceLogSubtype interfaceLogWrite)
|
||||||
|
( InterfaceLog{..} )
|
||||||
|
[ InterfaceLogTime =. interfaceLogTime
|
||||||
|
, InterfaceLogRows =. interfaceLogRows
|
||||||
|
, InterfaceLogInfo =. interfaceLogInfo
|
||||||
|
, InterfaceLogSuccess =. interfaceLogSuccess
|
||||||
|
]
|
||||||
|
audit TransactionInterface
|
||||||
|
{ transactionInterfaceName = interfaceLogInterface
|
||||||
|
, transactionInterfaceSubtype = interfaceLogSubtype
|
||||||
|
, transactionInterfaceWrite = interfaceLogWrite
|
||||||
|
, transactionInterfaceRows = interfaceLogRows
|
||||||
|
, transactionInterfaceInfo = interfaceLogInfo
|
||||||
|
, transactionInterfaceSuccess = Just interfaceLogSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
reportAdminProblem :: ( IsSqlBackend (YesodPersistBackend (HandlerSite m))
|
||||||
|
, SqlBackendCanWrite (YesodPersistBackend (HandlerSite m))
|
||||||
|
, MonadHandler m
|
||||||
|
-- , HasCallStack
|
||||||
|
)
|
||||||
|
=> AdminProblem -- ^ Problem to record
|
||||||
|
-> ReaderT (YesodPersistBackend (HandlerSite m)) m ()
|
||||||
|
-- ^ Log a problem that needs interventions by admins, provided this problem has not already been reported and is still unsolved
|
||||||
|
--
|
||||||
|
-- - `problemLogTime` is now
|
||||||
|
-- - `problemSolver` is Nothing, we do not record the person who caused it
|
||||||
|
reportAdminProblem problem = do
|
||||||
|
let problemLogSolved = Nothing
|
||||||
|
problemLogSolver = Nothing
|
||||||
|
problemLogInfo = toJSON problem
|
||||||
|
problemLogTime <- liftIO getCurrentTime
|
||||||
|
isKnown <- E.selectExists $ do
|
||||||
|
pl <- E.from $ E.table @ProblemLog
|
||||||
|
E.where_ $ E.isNothing (pl E.^. ProblemLogSolved)
|
||||||
|
E.&&. E.val problemLogInfo E.==. pl E.^. ProblemLogInfo
|
||||||
|
unless isKnown $ insert_ ProblemLog{..}
|
||||||
|
$logWarnS "Problem" $ Text.filter (/= '\n') $ tshow problem -- <> " - " <> pack (prettyCallStack callStack)
|
||||||
|
|
||||||
|
|
||||||
$logInfoS "Audit" $ tshow (transaction, transactionLogInitiator, transactionLogRemote) <> "\n" <> pack (prettyCallStack callStack)
|
|
||||||
|
|||||||
@ -1,15 +1,18 @@
|
|||||||
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <vaupel.sarah@campus.lmu.de>
|
-- SPDX-FileCopyrightText: 2022-24 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <vaupel.sarah@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>
|
||||||
--
|
--
|
||||||
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
module Audit.Types
|
module Audit.Types
|
||||||
( Transaction(..)
|
( Transaction(..)
|
||||||
|
, AdminProblem(..)
|
||||||
|
, decodeAdminProblem
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import ClassyPrelude.Yesod hiding (derivePersistFieldJSON)
|
import ClassyPrelude.Yesod hiding (derivePersistFieldJSON)
|
||||||
import Model.Types.TH.JSON
|
import Model.Types.TH.JSON
|
||||||
import Model
|
import Model
|
||||||
|
|
||||||
|
import Data.Aeson
|
||||||
import Data.Aeson.TH
|
import Data.Aeson.TH
|
||||||
import Utils.PathPiece
|
import Utils.PathPiece
|
||||||
|
|
||||||
@ -180,29 +183,43 @@ data Transaction
|
|||||||
{ transactionOldUserIdent
|
{ transactionOldUserIdent
|
||||||
, transactionNewUserIdent :: UserIdent
|
, transactionNewUserIdent :: UserIdent
|
||||||
}
|
}
|
||||||
|
| TransactionLmsStart
|
||||||
|
{ transactionQualification :: QualificationId
|
||||||
|
, transactionLmsIdent :: LmsIdent
|
||||||
|
, transactionLmsUser :: UserId
|
||||||
|
, transactionLmsUserKey :: LmsUserId
|
||||||
|
}
|
||||||
|
| TransactionLmsReset
|
||||||
|
{ transactionQualification :: QualificationId
|
||||||
|
, transactionLmsUser :: UserId
|
||||||
|
, transactionLmsReset :: Bool
|
||||||
|
, transactionLmsResetExtend :: Maybe Integer
|
||||||
|
, transactionLmsResetUnblock :: Maybe Bool
|
||||||
|
, transactionLmsResetNotify :: Maybe Bool
|
||||||
|
}
|
||||||
| TransactionLmsBlocked
|
| TransactionLmsBlocked
|
||||||
{ transactionQualification :: QualificationId
|
{ transactionQualification :: QualificationId
|
||||||
, transactionLmsIdent :: LmsIdent
|
, transactionLmsIdent :: LmsIdent
|
||||||
, transactionLmsDay :: Day
|
, transactionLmsDay :: UTCTime
|
||||||
, transactionLmsUser :: Maybe UserId
|
, transactionLmsUser :: UserId
|
||||||
, transactionNote :: Maybe Text
|
, transactionNote :: Maybe Text
|
||||||
, transactionReceived :: UTCTime -- when was the csv file received?
|
, transactionReceived :: UTCTime -- when was the csv file received?
|
||||||
}
|
}
|
||||||
| TransactionLmsSuccess
|
| TransactionLmsSuccess
|
||||||
{ transactionQualification :: QualificationId
|
{ transactionQualification :: QualificationId
|
||||||
, transactionLmsIdent :: LmsIdent
|
, transactionLmsIdent :: LmsIdent
|
||||||
, transactionLmsDay :: Day
|
, transactionLmsDay :: UTCTime
|
||||||
, transactionLmsUser :: Maybe UserId
|
, transactionLmsUser :: UserId
|
||||||
, transactionNote :: Maybe Text
|
, transactionNote :: Maybe Text
|
||||||
, transactionReceived :: UTCTime -- when was the csv file received?
|
, transactionReceived :: UTCTime -- when was the csv file received?
|
||||||
}
|
}
|
||||||
| TransactionQualificationUserEdit -- Note that a renewal always entails unblocking as well!
|
| TransactionQualificationUserEdit -- Note that a renewal always entails unblocking as well!
|
||||||
{ transactionUser :: UserId -- qualification holder that is updated
|
{ transactionUser :: UserId -- qualification holder that is updated
|
||||||
, transactionQualificationUser :: QualificationUserId -- könnte entfernt werden
|
, transactionQualificationUser :: QualificationUserId -- not really necessary, maybe remove?
|
||||||
, transactionQualification :: QualificationId
|
, transactionQualification :: QualificationId
|
||||||
, transactionQualificationValidUntil :: Day
|
, transactionQualificationValidUntil :: Day
|
||||||
, transactionQualificationScheduleRenewal :: Maybe Bool -- Maybe, because some update may leave it unchanged (also avoids DB Migration)
|
, transactionQualificationScheduleRenewal :: Maybe Bool -- Maybe, because some update may leave it unchanged (also avoids DB Migration)
|
||||||
|
, transactionNote :: Maybe Text
|
||||||
}
|
}
|
||||||
| TransactionQualificationUserDelete
|
| TransactionQualificationUserDelete
|
||||||
{ transactionUser :: UserId
|
{ transactionUser :: UserId
|
||||||
@ -211,9 +228,22 @@ data Transaction
|
|||||||
}
|
}
|
||||||
| TransactionQualificationUserBlocking
|
| TransactionQualificationUserBlocking
|
||||||
{ transactionUser :: UserId -- qualification holder that is updated
|
{ transactionUser :: UserId -- qualification holder that is updated
|
||||||
-- , transactionQualificationUser :: QualificationUserId -- not neccessary due to UniqueQualificationUser
|
-- , transactionQualificationUser :: QualificationUserId -- not neccessary due to UniqueQualificationUser
|
||||||
, transactionQualification :: QualificationId
|
, transactionQualification :: QualificationId
|
||||||
, transactionQualificationBlock :: Maybe QualificationBlocked -- Nothing indicates unblocking
|
, transactionQualificationBlock :: QualificationUserBlock -- full information about block
|
||||||
|
}
|
||||||
|
| TransactionQualificationUserScheduleRenewal
|
||||||
|
{ transactionUser :: UserId -- qualification holder that is updated
|
||||||
|
, transactionQualification :: QualificationId
|
||||||
|
, transactionQualificationScheduleRenewal :: Maybe Bool -- TRUE=will be notified upon expiry, FALSE=won't be notified; always JUST, for compatibility with TransactionQualificationUserEdit
|
||||||
|
}
|
||||||
|
| TransactionInterface
|
||||||
|
{ transactionInterfaceName :: Text
|
||||||
|
, transactionInterfaceSubtype :: Text
|
||||||
|
, transactionInterfaceWrite :: Bool -- True implies a write to FRADrive
|
||||||
|
, transactionInterfaceRows :: Maybe Int
|
||||||
|
, transactionInterfaceInfo :: Text
|
||||||
|
, transactionInterfaceSuccess :: Maybe Bool -- Just False implies a failure; Maybe used to achieve backwards compatibility
|
||||||
}
|
}
|
||||||
deriving (Eq, Ord, Read, Show, Generic)
|
deriving (Eq, Ord, Read, Show, Generic)
|
||||||
|
|
||||||
@ -225,3 +255,62 @@ deriveJSON defaultOptions
|
|||||||
} ''Transaction
|
} ''Transaction
|
||||||
|
|
||||||
derivePersistFieldJSON ''Transaction
|
derivePersistFieldJSON ''Transaction
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- Datatype for raising admin awareness to certain problems
|
||||||
|
-- Database stores generic Value in table ProblemLog, such that changes do not disturb old entries
|
||||||
|
-- Note that there is no RenderMessage instance, instead see @Handler.Admin.adminProblemCell dealing with special cases instead
|
||||||
|
-- Note: Adjust MsgAdminProblemInfoTooltip as well
|
||||||
|
data AdminProblem
|
||||||
|
= AdminProblemNewCompany -- new company was noticed, presumably without supervisors
|
||||||
|
{ adminProblemCompany :: CompanyId
|
||||||
|
}
|
||||||
|
| AdminProblemSupervisorNewCompany
|
||||||
|
{ adminProblemUser :: UserId -- a default supervisor has changed company
|
||||||
|
, adminProblemCompany :: CompanyId -- old company where the user had default supervisor rights
|
||||||
|
, adminProblemCompanyNew :: CompanyId -- new company of the user
|
||||||
|
, adminProblemSupervisorReroute :: Bool -- reroute included?
|
||||||
|
}
|
||||||
|
| AdminProblemSupervisorLeftCompany
|
||||||
|
{ adminProblemUser :: UserId -- user who had a supervisor but no longer has, due to supervisor change
|
||||||
|
, adminProblemCompany :: CompanyId -- old company
|
||||||
|
, adminProblemSupervisorReroute :: Bool -- reroute included?
|
||||||
|
}
|
||||||
|
| AdminProblemCompanySuperiorChange -- a company received a new superior user through AVS
|
||||||
|
{ adminProblemUser :: UserId -- new superior user
|
||||||
|
, adminProblemCompany :: CompanyId -- affected company
|
||||||
|
, adminProblemUserOld :: Maybe UserId -- previous superior
|
||||||
|
}
|
||||||
|
| AdminProblemCompanySuperiorNotFound -- a company received a new superior user through AVS, but user could not be created from email
|
||||||
|
{ adminProblemEmail :: Maybe Text -- new superior user's email, not found in LDAP
|
||||||
|
, adminProblemCompany :: CompanyId -- affected company
|
||||||
|
, adminProblemUserOld :: Maybe UserId -- previous superior
|
||||||
|
}
|
||||||
|
| AdminProblemNewlyUnsupervised
|
||||||
|
{ adminProblemUser :: UserId -- user who had a supervisor but no longer has, due to user company change
|
||||||
|
, adminProblemCompanyOld :: Maybe CompanyId -- old company
|
||||||
|
, adminProblemCompanyNew :: CompanyId -- new company of the user
|
||||||
|
}
|
||||||
|
| AdminProblemUnknown -- miscellanous problem, just displaying text
|
||||||
|
{ adminProblemText :: Text
|
||||||
|
}
|
||||||
|
deriving (Eq, Ord, Read, Show, Generic)
|
||||||
|
|
||||||
|
-- Columns shown in problem table: adminProblemCompany, adminProblemUser
|
||||||
|
-- For display: add clause to Handler.Admin.adminProblemCell
|
||||||
|
|
||||||
|
deriveJSON defaultOptions
|
||||||
|
{ constructorTagModifier = camelToPathPiece' 2
|
||||||
|
, fieldLabelModifier = camelToPathPiece' 2
|
||||||
|
, tagSingleConstructors = True
|
||||||
|
, sumEncoding = TaggedObject "problem" "data"
|
||||||
|
, rejectUnknownFields = False
|
||||||
|
} ''AdminProblem
|
||||||
|
|
||||||
|
derivePersistFieldJSON ''AdminProblem
|
||||||
|
|
||||||
|
decodeAdminProblem :: Value -> AdminProblem
|
||||||
|
decodeAdminProblem v = case fromJSON v of
|
||||||
|
Error msg -> AdminProblemUnknown $ pack msg
|
||||||
|
Success p -> p
|
||||||
|
|||||||
@ -34,7 +34,7 @@ dummyForm = do
|
|||||||
mr <- getMessageRender
|
mr <- getMessageRender
|
||||||
wreq (ciField & addDatalist userList) (fslpI MsgDummyIdent (mr MsgDummyIdentPlaceholder) & addAttr "autocomplete" "username" & addName PostLoginDummy) Nothing
|
wreq (ciField & addDatalist userList) (fslpI MsgDummyIdent (mr MsgDummyIdentPlaceholder) & addAttr "autocomplete" "username" & addName PostLoginDummy) Nothing
|
||||||
where
|
where
|
||||||
userList = fmap mkOptionList . runDB $ withReaderT projectBackend (map toOption <$> selectList [] [Asc UserIdent] :: ReaderT SqlBackend _ [Option UserIdent])
|
userList = fmap mkOptionList . runDB $ withReaderT projectBackend (map toOption <$> selectList [UserId <=. UserKey 12] [Asc UserIdent] :: ReaderT SqlBackend _ [Option UserIdent])
|
||||||
toOption (Entity _ User{..}) = Option userDisplayName userIdent (CI.original userIdent)
|
toOption (Entity _ User{..}) = Option userDisplayName userIdent (CI.original userIdent)
|
||||||
|
|
||||||
apDummy :: Text
|
apDummy :: Text
|
||||||
|
|||||||
@ -59,6 +59,7 @@ decCryptoIDs [ ''SubmissionId
|
|||||||
, ''MaterialFileId
|
, ''MaterialFileId
|
||||||
, ''PrintJobId
|
, ''PrintJobId
|
||||||
, ''QualificationId
|
, ''QualificationId
|
||||||
|
, ''SentMailId
|
||||||
]
|
]
|
||||||
|
|
||||||
decCryptoIDKeySize
|
decCryptoIDKeySize
|
||||||
|
|||||||
@ -128,4 +128,4 @@ instance Swagger.ToSchema s => Swagger.ToSchema (CI s) where
|
|||||||
|
|
||||||
instance (CI.FoldCase s, Binary s) => Binary (CI s) where
|
instance (CI.FoldCase s, Binary s) => Binary (CI s) where
|
||||||
get = CI.mk <$> Binary.get
|
get = CI.mk <$> Binary.get
|
||||||
put = Binary.put . CI.original
|
put = Binary.put . CI.original
|
||||||
@ -1,4 +1,4 @@
|
|||||||
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
|
-- SPDX-FileCopyrightText: 2022-24 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
|
||||||
--
|
--
|
||||||
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
@ -14,36 +14,49 @@ module Database.Esqueleto.Utils
|
|||||||
, strConcat, substring
|
, strConcat, substring
|
||||||
, (=?.), (?=.)
|
, (=?.), (?=.)
|
||||||
, (=~.), (~=.)
|
, (=~.), (~=.)
|
||||||
|
, (>~.), (<~.)
|
||||||
|
, (~.), (~*.), (!~.), (!~*.)
|
||||||
, or, and
|
, or, and
|
||||||
, any, all
|
, any, all
|
||||||
|
, not__, parens
|
||||||
, subSelectAnd, subSelectOr
|
, subSelectAnd, subSelectOr
|
||||||
, mkExactFilter, mkExactFilterWith
|
, mkExactFilter, mkExactFilterWith, mkExactFilterWithComma
|
||||||
, mkExactFilterLast, mkExactFilterLastWith
|
, mkExactFilterLast, mkExactFilterLastWith
|
||||||
|
, mkExactFilterMaybeLast, mkExactFilterMaybeLast'
|
||||||
, mkContainsFilter, mkContainsFilterWith
|
, mkContainsFilter, mkContainsFilterWith
|
||||||
|
, mkContainsFilterWithSet, mkContainsFilterWithComma, mkContainsFilterWithCommaPlus
|
||||||
, mkDayFilter, mkDayFilterFrom, mkDayFilterTo
|
, mkDayFilter, mkDayFilterFrom, mkDayFilterTo
|
||||||
, mkExistsFilter
|
, mkExistsFilter, mkExistsFilterWithComma
|
||||||
|
-- , mkRegExFilterWith
|
||||||
, anyFilter, allFilter
|
, anyFilter, allFilter
|
||||||
, ascNullsFirst, descNullsLast
|
, ascNullsFirst, descNullsLast
|
||||||
, orderByList
|
, orderByList
|
||||||
, orderByOrd, orderByEnum
|
, orderByOrd, orderByEnum
|
||||||
, strip, lower, ciEq
|
, strip, lower, ciEq
|
||||||
, selectExists, selectNotExists
|
, selectExists, selectNotExists
|
||||||
|
, filterExists
|
||||||
, SqlHashable
|
, SqlHashable
|
||||||
, sha256
|
, sha256
|
||||||
|
, isTrue, isFalse
|
||||||
, maybe, maybe2, maybeEq, guardMaybe, unsafeCoalesce
|
, maybe, maybe2, maybeEq, guardMaybe, unsafeCoalesce
|
||||||
, bool
|
, bool
|
||||||
, max, min
|
, max, min
|
||||||
, greatest, least
|
, greatest, least
|
||||||
, abs
|
, abs
|
||||||
, SqlProject(..)
|
, SqlProject(..)
|
||||||
, (->.), (->>.), (#>>.)
|
, (->.), (->>.), (->>>.), (#>>.)
|
||||||
, fromSqlKey
|
, fromSqlKey
|
||||||
, unKey
|
, unKey
|
||||||
|
, subSelectCountDistinct
|
||||||
, selectCountRows, selectCountDistinct
|
, selectCountRows, selectCountDistinct
|
||||||
, selectMaybe
|
, selectMaybe
|
||||||
, day, day', interval, diffDays, diffTimes
|
, str2text, str2text'
|
||||||
|
, num2text --, text2num
|
||||||
|
, day, day', dayMaybe, interval, diffDays, diffTimes
|
||||||
, exprLift
|
, exprLift
|
||||||
, explicitUnsafeCoerceSqlExprValue
|
, explicitUnsafeCoerceSqlExprValue
|
||||||
|
, psqlVersion_
|
||||||
|
, truncateTable
|
||||||
, module Database.Esqueleto.Utils.TH
|
, module Database.Esqueleto.Utils.TH
|
||||||
) where
|
) where
|
||||||
|
|
||||||
@ -54,12 +67,17 @@ import qualified Data.Set as Set
|
|||||||
import qualified Data.List as List
|
import qualified Data.List as List
|
||||||
import qualified Data.Foldable as F
|
import qualified Data.Foldable as F
|
||||||
import Data.List.NonEmpty (NonEmpty(..))
|
import Data.List.NonEmpty (NonEmpty(..))
|
||||||
|
import qualified Database.Persist as P
|
||||||
|
import qualified Database.Persist.EntityDef.Internal as P (entityDB)
|
||||||
import qualified Database.Esqueleto.Legacy as E
|
import qualified Database.Esqueleto.Legacy as E
|
||||||
import qualified Database.Esqueleto.Experimental as Ex
|
import qualified Database.Esqueleto.Experimental as Ex
|
||||||
import qualified Database.Esqueleto.PostgreSQL as E
|
import qualified Database.Esqueleto.PostgreSQL as E
|
||||||
import qualified Database.Esqueleto.Internal.Internal as E
|
import qualified Database.Esqueleto.Internal.Internal as E
|
||||||
import Database.Esqueleto.Utils.TH
|
import Database.Esqueleto.Utils.TH
|
||||||
|
|
||||||
|
-- import qualified Database.Persist.Postgresql as P
|
||||||
|
|
||||||
|
import qualified Data.Text as Text
|
||||||
import qualified Data.Text.Lazy as Lazy (Text)
|
import qualified Data.Text.Lazy as Lazy (Text)
|
||||||
import qualified Data.ByteString.Lazy as Lazy (ByteString)
|
import qualified Data.ByteString.Lazy as Lazy (ByteString)
|
||||||
|
|
||||||
@ -75,6 +93,10 @@ import qualified Data.Text.Lazy.Builder as Text.Builder
|
|||||||
|
|
||||||
import Data.Monoid (Last(..))
|
import Data.Monoid (Last(..))
|
||||||
|
|
||||||
|
import Utils (commaSeparatedText)
|
||||||
|
-- import Utils.Set (concatMapSet)
|
||||||
|
|
||||||
|
|
||||||
{-# ANN any ("HLint: ignore Use any" :: String) #-}
|
{-# ANN any ("HLint: ignore Use any" :: String) #-}
|
||||||
{-# ANN all ("HLint: ignore Use all" :: String) #-}
|
{-# ANN all ("HLint: ignore Use all" :: String) #-}
|
||||||
|
|
||||||
@ -134,6 +156,35 @@ infixl 4 ~=.
|
|||||||
(~=.) :: PersistField typ => E.SqlExpr (E.Value (Maybe typ)) -> E.SqlExpr (E.Value typ) -> E.SqlExpr (E.Value Bool)
|
(~=.) :: PersistField typ => E.SqlExpr (E.Value (Maybe typ)) -> E.SqlExpr (E.Value typ) -> E.SqlExpr (E.Value Bool)
|
||||||
(~=.) a b = E.isNothing a E.||. (a E.==. E.just b)
|
(~=.) a b = E.isNothing a E.||. (a E.==. E.just b)
|
||||||
|
|
||||||
|
-- | like (>.), but also succeeds if the right-hand side is NULL
|
||||||
|
infixl 4 >~.
|
||||||
|
(>~.) :: PersistField typ => E.SqlExpr (E.Value typ) -> E.SqlExpr (E.Value (Maybe typ)) -> E.SqlExpr (E.Value Bool)
|
||||||
|
(>~.) a b = E.isNothing b E.||. (E.just a E.>. b)
|
||||||
|
|
||||||
|
-- | like (<.), but also succeeds if the right-hand side is NULL
|
||||||
|
infixl 4 <~.
|
||||||
|
(<~.) :: PersistField typ => E.SqlExpr (E.Value typ) -> E.SqlExpr (E.Value (Maybe typ)) -> E.SqlExpr (E.Value Bool)
|
||||||
|
(<~.) a b = E.isNothing b E.||. (E.just a E.<. b)
|
||||||
|
|
||||||
|
infixr 2 ~., ~*., !~., !~*.
|
||||||
|
|
||||||
|
-- | PostgreSQL regular expression match, case sensitive. Works, but may throw SQL error for unblanced parenthesis, etc. Not suitable for dbTable filters
|
||||||
|
(~.) :: E.SqlString s => E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value Bool)
|
||||||
|
(~.) = E.unsafeSqlBinOp " ~ "
|
||||||
|
|
||||||
|
-- | PostgreSQL regular expression match, case insensitive. Works, but may throw SQL errors
|
||||||
|
(~*.) :: E.SqlString s => E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value Bool)
|
||||||
|
(~*.) = E.unsafeSqlBinOp " ~* "
|
||||||
|
|
||||||
|
-- | PostgreSQL regular expression does not match, case sensitive. Works, but may throw SQL error for unblanced parenthesis, etc. Not suitable for dbTable filters
|
||||||
|
(!~.) :: E.SqlString s => E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value Bool)
|
||||||
|
(!~.) = E.unsafeSqlBinOp " !~ "
|
||||||
|
|
||||||
|
-- | PostgreSQL regular expression does not match, case insensitive. Works, but may throw SQL errors
|
||||||
|
(!~*.) :: E.SqlString s => E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value Bool)
|
||||||
|
(!~*.) = E.unsafeSqlBinOp " !~* "
|
||||||
|
|
||||||
|
|
||||||
-- | Negation of `isNothing` which is missing
|
-- | Negation of `isNothing` which is missing
|
||||||
isJust :: PersistField typ => E.SqlExpr (E.Value (Maybe typ)) -> E.SqlExpr (E.Value Bool)
|
isJust :: PersistField typ => E.SqlExpr (E.Value (Maybe typ)) -> E.SqlExpr (E.Value Bool)
|
||||||
isJust = E.not_ . E.isNothing
|
isJust = E.not_ . E.isNothing
|
||||||
@ -205,8 +256,13 @@ explicitUnsafeCoerceSqlExprValue typ (E.ERaw _m1 f1) = E.ERaw E.noMeta $ \_nPare
|
|||||||
)
|
)
|
||||||
|
|
||||||
and, or :: Foldable f => f (E.SqlExpr (E.Value Bool)) -> E.SqlExpr (E.Value Bool)
|
and, or :: Foldable f => f (E.SqlExpr (E.Value Bool)) -> E.SqlExpr (E.Value Bool)
|
||||||
and = F.foldr (E.&&.) true
|
-- and = F.foldl' (E.&&.) true -- we can use foldl' since PostgreSQL reorders conditions anyway
|
||||||
or = F.foldr (E.||.) false
|
-- or = F.foldl' (E.||.) false
|
||||||
|
-- Maybe this help the PostgreSQL query optimizer, though I doubt it?
|
||||||
|
and f | F.null f = true
|
||||||
|
| otherwise = F.foldl1 (E.&&.) f
|
||||||
|
or f | F.null f = false
|
||||||
|
| otherwise = F.foldl1 (E.||.) f
|
||||||
|
|
||||||
-- | Given a test and a set of values, check whether anyone succeeds the test
|
-- | Given a test and a set of values, check whether anyone succeeds the test
|
||||||
-- WARNING: SQL leaves it explicitely unspecified whether `||` is short curcuited (i.e. lazily evaluated)
|
-- WARNING: SQL leaves it explicitely unspecified whether `||` is short curcuited (i.e. lazily evaluated)
|
||||||
@ -225,6 +281,9 @@ subSelectOr q = parens . E.subSelectUnsafe $ flip (E.unsafeSqlAggregateFunction
|
|||||||
parens :: E.SqlExpr (E.Value a) -> E.SqlExpr (E.Value a)
|
parens :: E.SqlExpr (E.Value a) -> E.SqlExpr (E.Value a)
|
||||||
parens = E.unsafeSqlFunction ""
|
parens = E.unsafeSqlFunction ""
|
||||||
|
|
||||||
|
-- | Workaround for Esqueleto-Bug not placing parenthesis after NOT, see #155
|
||||||
|
not__ :: E.SqlExpr (E.Value Bool) -> E.SqlExpr (E.Value Bool)
|
||||||
|
not__ = E.not_ . parens
|
||||||
|
|
||||||
-- Allow usage of Tuples as DbtRowKey, i.e. SqlIn instances for tuples
|
-- Allow usage of Tuples as DbtRowKey, i.e. SqlIn instances for tuples
|
||||||
$(sqlInTuples [2..16])
|
$(sqlInTuples [2..16])
|
||||||
@ -263,6 +322,17 @@ mkExactFilterWith cast lenslike row criterias
|
|||||||
| Set.null criterias = true
|
| Set.null criterias = true
|
||||||
| otherwise = lenslike row `E.in_` E.valList (cast <$> Set.toList criterias)
|
| otherwise = lenslike row `E.in_` E.valList (cast <$> Set.toList criterias)
|
||||||
|
|
||||||
|
-- | like `mkExactFilterWith` but splits comma separared Texts into multiple criteria
|
||||||
|
mkExactFilterWithComma :: (PersistField b)
|
||||||
|
=> (Text -> Maybe b) -- ^ type conversion
|
||||||
|
-> (t -> E.SqlExpr (E.Value b)) -- ^ getter from query to searched element
|
||||||
|
-> t -- ^ query row
|
||||||
|
-> Set.Set Text -- ^ needle collection
|
||||||
|
-> E.SqlExpr (E.Value Bool)
|
||||||
|
mkExactFilterWithComma cast lenslike row (foldMap commaSeparatedText -> criterias)
|
||||||
|
| Set.null criterias = true
|
||||||
|
| otherwise = lenslike row `E.in_` E.valList (mapMaybe cast $ Set.toList criterias)
|
||||||
|
|
||||||
-- | generic filter creation for dbTable
|
-- | generic filter creation for dbTable
|
||||||
-- Given a lens-like function, make filter for exact matches against last element of a collection
|
-- Given a lens-like function, make filter for exact matches against last element of a collection
|
||||||
mkExactFilterLast :: (PersistField a)
|
mkExactFilterLast :: (PersistField a)
|
||||||
@ -283,10 +353,33 @@ mkExactFilterLastWith cast lenslike row criterias
|
|||||||
| Last (Just crit) <- criterias = lenslike row E.==. E.val (cast crit)
|
| Last (Just crit) <- criterias = lenslike row E.==. E.val (cast crit)
|
||||||
| otherwise = true
|
| otherwise = true
|
||||||
|
|
||||||
|
-- | like `mkExactFilterLast` but deals with Nothing being a filter criterion as well
|
||||||
|
mkExactFilterMaybeLast :: PersistField a
|
||||||
|
=> (t -> E.SqlExpr (E.Value (Maybe a))) -- ^ getter from query to searched element
|
||||||
|
-> t -- ^ query row
|
||||||
|
-> Last (Maybe a) -- ^ needle
|
||||||
|
-> E.SqlExpr (E.Value Bool)
|
||||||
|
mkExactFilterMaybeLast lenslike row criterias
|
||||||
|
| Last (Just Nothing) <- criterias = E.isNothing $ lenslike row
|
||||||
|
| Last (Just crit) <- criterias = lenslike row E.==. E.val crit
|
||||||
|
| otherwise = true
|
||||||
|
|
||||||
|
-- | like `mkExactFilterMaybeLast` but for doubly wrapped Maybes
|
||||||
|
mkExactFilterMaybeLast' :: (PersistField a, PersistField b)
|
||||||
|
=> (t -> E.SqlExpr (E.Value (Maybe b))) -- ^ getter from query ensure entity exists at all
|
||||||
|
-> (t -> E.SqlExpr (E.Value (Maybe (Maybe a)))) -- ^ getter from query to searched element
|
||||||
|
-> t -- ^ query row
|
||||||
|
-> Last (Maybe a) -- ^ needle
|
||||||
|
-> E.SqlExpr (E.Value Bool)
|
||||||
|
mkExactFilterMaybeLast' lensexists lenslike row criterias
|
||||||
|
| Last (Just Nothing) <- criterias = isJust (lensexists row) E.&&. E.isNothing (E.joinV $ lenslike row)
|
||||||
|
| Last (Just crit) <- criterias = lenslike row E.==. E.val (Just crit)
|
||||||
|
| otherwise = true
|
||||||
|
|
||||||
-- | generic filter creation for dbTable
|
-- | generic filter creation for dbTable
|
||||||
-- Given a lens-like function, make filter searching for needles in String-like elements
|
-- Given a lens-like function, make filter searching for needles in String-like elements
|
||||||
-- (Keep Set here to ensure that there are no duplicates)
|
-- (Keep Set here to ensure that there are no duplicates)
|
||||||
mkContainsFilter :: E.SqlString a
|
mkContainsFilter :: (E.SqlString a, Ord a)
|
||||||
=> (t -> E.SqlExpr (E.Value a)) -- ^ getter from query to searched element
|
=> (t -> E.SqlExpr (E.Value a)) -- ^ getter from query to searched element
|
||||||
-> t -- ^ query row
|
-> t -- ^ query row
|
||||||
-> Set.Set a -- ^ needle collection
|
-> Set.Set a -- ^ needle collection
|
||||||
@ -294,7 +387,7 @@ mkContainsFilter :: E.SqlString a
|
|||||||
mkContainsFilter = mkContainsFilterWith id
|
mkContainsFilter = mkContainsFilterWith id
|
||||||
|
|
||||||
-- | like `mkContainsFilter` but allows for conversion; convenient in conjunction with `anyFilter` and `allFilter`
|
-- | like `mkContainsFilter` but allows for conversion; convenient in conjunction with `anyFilter` and `allFilter`
|
||||||
mkContainsFilterWith :: E.SqlString b
|
mkContainsFilterWith :: (E.SqlString b, Ord a)
|
||||||
=> (a -> b)
|
=> (a -> b)
|
||||||
-> (t -> E.SqlExpr (E.Value b)) -- ^ getter from query to searched element
|
-> (t -> E.SqlExpr (E.Value b)) -- ^ getter from query to searched element
|
||||||
-> t -- ^ query row
|
-> t -- ^ query row
|
||||||
@ -302,8 +395,58 @@ mkContainsFilterWith :: E.SqlString b
|
|||||||
-> E.SqlExpr (E.Value Bool)
|
-> E.SqlExpr (E.Value Bool)
|
||||||
mkContainsFilterWith cast lenslike row criterias
|
mkContainsFilterWith cast lenslike row criterias
|
||||||
| Set.null criterias = true
|
| Set.null criterias = true
|
||||||
| otherwise = any (hasInfix $ lenslike row) (E.val . cast <$> Set.toList criterias)
|
| otherwise = any (hasInfix (lenslike row) . E.val . cast) criterias
|
||||||
|
|
||||||
|
-- | like `mkContainsFilterWith` but allows conversion to produce multiple needles
|
||||||
|
mkContainsFilterWithSet :: (E.SqlString b, Ord b, Ord a)
|
||||||
|
=> (a -> Set.Set b)
|
||||||
|
-> (t -> E.SqlExpr (E.Value b)) -- ^ getter from query to searched element
|
||||||
|
-> t -- ^ query row
|
||||||
|
-> Set.Set a -- ^ needle collection
|
||||||
|
-> E.SqlExpr (E.Value Bool)
|
||||||
|
mkContainsFilterWithSet cast lenslike row criterias
|
||||||
|
| Set.null criterias = true
|
||||||
|
| otherwise = any (hasInfix (lenslike row) . E.val) (foldMap cast criterias)
|
||||||
|
|
||||||
|
-- | like `mkContainsFilterWithSet` but fixed to comma separated Texts
|
||||||
|
mkContainsFilterWithComma :: (E.SqlString b, Ord b)
|
||||||
|
=> (Text -> b)
|
||||||
|
-> (t -> E.SqlExpr (E.Value b)) -- ^ getter from query to searched element
|
||||||
|
-> t -- ^ query row
|
||||||
|
-> Set.Set Text -- ^ needle collection
|
||||||
|
-> E.SqlExpr (E.Value Bool)
|
||||||
|
mkContainsFilterWithComma cast lenslike row (foldMap commaSeparatedText -> criterias)
|
||||||
|
| Set.null criterias = true
|
||||||
|
| otherwise = any (hasInfix (lenslike row) . E.val . cast) criterias
|
||||||
|
|
||||||
|
-- | like `mkContainsFilterWithComma` but enforced the existence of all Texts prefixed with +
|
||||||
|
mkContainsFilterWithCommaPlus :: (E.SqlString b, Ord b)
|
||||||
|
=> (Text -> b)
|
||||||
|
-> (t -> E.SqlExpr (E.Value b)) -- ^ getter from query to searched element
|
||||||
|
-> t -- ^ query row
|
||||||
|
-> Set.Set Text -- ^ needle collection
|
||||||
|
-> E.SqlExpr (E.Value Bool)
|
||||||
|
mkContainsFilterWithCommaPlus cast lenslike row (foldMap commaSeparatedText -> criterias)
|
||||||
|
| Set.null criterias = true
|
||||||
|
| Set.null compulsories = cond_optional
|
||||||
|
| Set.null alternatives = cond_compulsory
|
||||||
|
| otherwise = cond_compulsory E.&&. cond_optional
|
||||||
|
where
|
||||||
|
(Set.mapMonotonic (Text.stripStart . Text.drop 1) -> compulsories, alternatives) = Set.partition (Text.isPrefixOf "+") criterias
|
||||||
|
cond_compulsory = all (hasInfix (lenslike row) . E.val . cast) compulsories
|
||||||
|
cond_optional = any (hasInfix (lenslike row) . E.val . cast) alternatives
|
||||||
|
|
||||||
|
-- like `mkContainsFilterWith` but allows regular expression criterias
|
||||||
|
-- This works, but throws SQL errors for unbalanced parenthesis and similar invalid regex expressions
|
||||||
|
-- mkRegExFilterWith :: (E.SqlString b, Ord a)
|
||||||
|
-- => (a -> b)
|
||||||
|
-- -> (t -> E.SqlExpr (E.Value b)) -- ^ getter from query to searched element
|
||||||
|
-- -> t -- ^ query row
|
||||||
|
-- -> Set.Set a -- ^ needle collection
|
||||||
|
-- -> E.SqlExpr (E.Value Bool)
|
||||||
|
-- mkRegExFilterWith cast lenslike row criterias
|
||||||
|
-- | Set.null criterias = true
|
||||||
|
-- | otherwise = any ((~.) (lenslike row) . E.val . cast) criterias
|
||||||
|
|
||||||
mkDayFilter :: (t -> E.SqlExpr (E.Value UTCTime)) -- ^ getter from query to searched element
|
mkDayFilter :: (t -> E.SqlExpr (E.Value UTCTime)) -- ^ getter from query to searched element
|
||||||
-> t -- ^ query row
|
-> t -- ^ query row
|
||||||
@ -340,6 +483,17 @@ mkExistsFilter query row criterias
|
|||||||
| Set.null criterias = true
|
| Set.null criterias = true
|
||||||
| otherwise = any (E.exists . query row) $ Set.toList criterias
|
| otherwise = any (E.exists . query row) $ Set.toList criterias
|
||||||
|
|
||||||
|
mkExistsFilterWithComma :: PathPiece a
|
||||||
|
=> (Text -> a)
|
||||||
|
-> (t -> a -> E.SqlQuery ())
|
||||||
|
-> t
|
||||||
|
-> Set.Set Text
|
||||||
|
-> E.SqlExpr (E.Value Bool)
|
||||||
|
mkExistsFilterWithComma cast query row (foldMap commaSeparatedText -> criterias)
|
||||||
|
| Set.null criterias = true
|
||||||
|
| otherwise = any (E.exists . query row . cast) criterias
|
||||||
|
|
||||||
|
|
||||||
-- | Combine several filters, using logical or
|
-- | Combine several filters, using logical or
|
||||||
anyFilter :: Foldable f
|
anyFilter :: Foldable f
|
||||||
=> f (t -> cs -> E.SqlExpr (E.Value Bool))
|
=> f (t -> cs -> E.SqlExpr (E.Value Bool))
|
||||||
@ -396,6 +550,13 @@ selectExists query = do
|
|||||||
_other -> error "SELECT EXISTS ... returned zero or more than one rows"
|
_other -> error "SELECT EXISTS ... returned zero or more than one rows"
|
||||||
selectNotExists = fmap not . selectExists
|
selectNotExists = fmap not . selectExists
|
||||||
|
|
||||||
|
filterExists :: (MonadIO m, PersistEntity val, MonoFoldable mono, PersistField (Element mono))
|
||||||
|
=> EntityField val (Element mono) -> mono -> E.SqlReadT m [Element mono]
|
||||||
|
filterExists prj vs = fmap (fmap Ex.unValue) <$> Ex.select $ do
|
||||||
|
ent <- Ex.from Ex.table
|
||||||
|
Ex.where_ $ ent Ex.^. prj `Ex.in_` vals vs
|
||||||
|
return $ ent Ex.^. prj
|
||||||
|
|
||||||
|
|
||||||
class SqlHashable a
|
class SqlHashable a
|
||||||
instance SqlHashable Text
|
instance SqlHashable Text
|
||||||
@ -408,6 +569,12 @@ sha256 :: SqlHashable a => E.SqlExpr (E.Value a) -> E.SqlExpr (E.Value (Digest S
|
|||||||
sha256 = E.unsafeSqlFunction "digest" . (, E.val "sha256" :: E.SqlExpr (E.Value Text))
|
sha256 = E.unsafeSqlFunction "digest" . (, E.val "sha256" :: E.SqlExpr (E.Value Text))
|
||||||
|
|
||||||
|
|
||||||
|
isTrue :: E.SqlExpr (E.Value (Maybe Bool)) -> E.SqlExpr (E.Value Bool)
|
||||||
|
isTrue expr = E.unsafeSqlBinOp "IS TRUE" expr $ E.unsafeSqlValue ""
|
||||||
|
|
||||||
|
isFalse :: E.SqlExpr (E.Value (Maybe Bool)) -> E.SqlExpr (E.Value Bool)
|
||||||
|
isFalse expr = E.unsafeSqlBinOp "IS FALSE" expr $ E.unsafeSqlValue ""
|
||||||
|
|
||||||
maybe :: (PersistField a, PersistField b)
|
maybe :: (PersistField a, PersistField b)
|
||||||
=> E.SqlExpr (E.Value b)
|
=> E.SqlExpr (E.Value b)
|
||||||
-> (E.SqlExpr (E.Value a) -> E.SqlExpr (E.Value b))
|
-> (E.SqlExpr (E.Value a) -> E.SqlExpr (E.Value b))
|
||||||
@ -483,7 +650,7 @@ max, min :: PersistField a
|
|||||||
max a b = bool a b $ b E.>. a
|
max a b = bool a b $ b E.>. a
|
||||||
min a b = bool a b $ b E.<. a
|
min a b = bool a b $ b E.<. a
|
||||||
|
|
||||||
-- these alternatives for max/min ought to be more efficient; note that NULL is avoided by greatest/least
|
-- these alternatives for max/min ought to be more efficient; note that NULL is avoided by PostgreSQL greatest/least; for Bool: t > f
|
||||||
greatest :: PersistField a => E.SqlExpr (E.Value a) -> E.SqlExpr (E.Value a) -> E.SqlExpr (E.Value a)
|
greatest :: PersistField a => E.SqlExpr (E.Value a) -> E.SqlExpr (E.Value a) -> E.SqlExpr (E.Value a)
|
||||||
greatest a b = E.unsafeSqlFunction "GREATEST" $ E.toArgList (a,b)
|
greatest a b = E.unsafeSqlFunction "GREATEST" $ E.toArgList (a,b)
|
||||||
|
|
||||||
@ -522,9 +689,16 @@ infixl 8 ->.
|
|||||||
|
|
||||||
infixl 8 ->>.
|
infixl 8 ->>.
|
||||||
|
|
||||||
|
-- Unsafe variant, see Database.Esqueleto.PostgreSQL.JSON for a safe version!
|
||||||
(->>.) :: E.SqlExpr (E.Value a) -> Text -> E.SqlExpr (E.Value Text)
|
(->>.) :: E.SqlExpr (E.Value a) -> Text -> E.SqlExpr (E.Value Text)
|
||||||
(->>.) expr t = E.unsafeSqlBinOp "->>" expr $ E.val t
|
(->>.) expr t = E.unsafeSqlBinOp "->>" expr $ E.val t
|
||||||
|
|
||||||
|
infixl 8 ->>>.
|
||||||
|
|
||||||
|
-- Unsafe variant to obtain a DB key from a JSON field. Use with caution!
|
||||||
|
(->>>.) :: (PersistField (Key entity)) => E.SqlExpr (E.Value a) -> Text -> E.SqlExpr (E.Value (Maybe (Key entity)))
|
||||||
|
(->>>.) expr t = E.unsafeSqlCastAs "int" $ E.unsafeSqlBinOp "->>" expr $ E.val t
|
||||||
|
|
||||||
infixl 8 #>>.
|
infixl 8 #>>.
|
||||||
|
|
||||||
(#>>.) :: E.SqlExpr (E.Value a) -> Text -> E.SqlExpr (E.Value (Maybe Text))
|
(#>>.) :: E.SqlExpr (E.Value a) -> Text -> E.SqlExpr (E.Value (Maybe Text))
|
||||||
@ -540,6 +714,12 @@ unKey :: ( Coercible (Key entity) a
|
|||||||
=> E.SqlExpr (E.Value (Key entity)) -> E.SqlExpr (E.Value a)
|
=> E.SqlExpr (E.Value (Key entity)) -> E.SqlExpr (E.Value a)
|
||||||
unKey = E.veryUnsafeCoerceSqlExprValue
|
unKey = E.veryUnsafeCoerceSqlExprValue
|
||||||
|
|
||||||
|
-- | distinct version of `Database.Esqueleto.subSelectCount`
|
||||||
|
subSelectCountDistinct :: (Num a, PersistField a) => Ex.SqlQuery (Ex.SqlExpr (Ex.Value typ)) -> Ex.SqlExpr (Ex.Value a)
|
||||||
|
subSelectCountDistinct query = Ex.subSelectUnsafe (Ex.countDistinct <$> query)
|
||||||
|
|
||||||
|
-- PersistField a => SqlQuery (SqlExpr (Value a)) -> SqlExpr (Value a)
|
||||||
|
-- countDistinct :: Num a => SqlExpr (Value typ) -> SqlExpr (Value a)
|
||||||
|
|
||||||
selectCountRows :: (Num a, PersistField a, MonadIO m) => E.SqlQuery ignored -> E.SqlReadT m a
|
selectCountRows :: (Num a, PersistField a, MonadIO m) => E.SqlQuery ignored -> E.SqlReadT m a
|
||||||
selectCountRows q = do
|
selectCountRows q = do
|
||||||
@ -562,21 +742,38 @@ selectCountDistinct q = do
|
|||||||
selectMaybe :: (E.SqlSelect a r, MonadIO m) => E.SqlQuery a -> E.SqlReadT m (Maybe r)
|
selectMaybe :: (E.SqlSelect a r, MonadIO m) => E.SqlQuery a -> E.SqlReadT m (Maybe r)
|
||||||
selectMaybe = fmap listToMaybe . E.select . (<* E.limit 1)
|
selectMaybe = fmap listToMaybe . E.select . (<* E.limit 1)
|
||||||
|
|
||||||
|
-- | convert something that is like a text to text
|
||||||
|
str2text :: E.SqlString a => E.SqlExpr (E.Value a) -> E.SqlExpr (E.Value Text)
|
||||||
|
str2text = E.unsafeSqlCastAs "text"
|
||||||
|
|
||||||
|
str2text' :: E.SqlString a => E.SqlExpr (E.Value (Maybe a)) -> E.SqlExpr (E.Value (Maybe Text))
|
||||||
|
str2text' = E.unsafeSqlCastAs "text"
|
||||||
|
|
||||||
|
-- | cast numeric type to text, which is safe and allows for an inefficient but safe comparison of numbers stored as text and numbers
|
||||||
|
num2text :: Num n => E.SqlExpr (E.Value n) -> E.SqlExpr (E.Value Text)
|
||||||
|
num2text = E.unsafeSqlCastAs "text"
|
||||||
|
|
||||||
|
-- unsafe, use with care!
|
||||||
|
-- text2num :: E.SqlExpr (E.Value Text) -> E.SqlExpr (E.Value n)
|
||||||
|
-- text2num = E.unsafeSqlCastAs "int"
|
||||||
|
|
||||||
day :: E.SqlExpr (E.Value UTCTime) -> E.SqlExpr (E.Value Day)
|
day :: E.SqlExpr (E.Value UTCTime) -> E.SqlExpr (E.Value Day)
|
||||||
day = E.unsafeSqlCastAs "date"
|
day = E.unsafeSqlCastAs "date"
|
||||||
|
|
||||||
|
-- | cast text to day, truly unsafe
|
||||||
day' :: E.SqlExpr (E.Value Text) -> E.SqlExpr (E.Value Day)
|
day' :: E.SqlExpr (E.Value Text) -> E.SqlExpr (E.Value Day)
|
||||||
day' = E.unsafeSqlCastAs "date"
|
day' = E.unsafeSqlCastAs "date"
|
||||||
|
|
||||||
|
dayMaybe :: E.SqlExpr (E.Value (Maybe UTCTime)) -> E.SqlExpr (E.Value (Maybe Day))
|
||||||
|
dayMaybe = E.unsafeSqlCastAs "date"
|
||||||
|
|
||||||
interval :: CalendarDiffDays -> E.SqlExpr (E.Value Day) -- E.+=. requires both types to be the same, so we use Day
|
interval :: CalendarDiffDays -> E.SqlExpr (E.Value Day) -- E.+=. requires both types to be the same, so we use Day
|
||||||
-- interval _ = E.unsafeSqlCastAs "interval" $ E.unsafeSqlValue "'P2Y'" -- tested working example
|
-- interval _ = E.unsafeSqlCastAs "interval" $ E.unsafeSqlValue "'P2Y'" -- tested working example
|
||||||
interval = E.unsafeSqlCastAs "interval". E.unsafeSqlValue . wrapSqlString . Text.Builder.fromString . iso8601Show
|
interval = E.unsafeSqlCastAs "interval". E.unsafeSqlValue . wrapSqlString . Text.Builder.fromString . iso8601Show
|
||||||
where
|
where
|
||||||
singleQuote = Text.Builder.singleton '\''
|
singleQuote = Text.Builder.singleton '\''
|
||||||
wrapSqlString b = singleQuote <> b <> singleQuote
|
wrapSqlString b = singleQuote <> b <> singleQuote
|
||||||
|
|
||||||
|
|
||||||
infixl 6 `diffDays`, `diffTimes`
|
infixl 6 `diffDays`, `diffTimes`
|
||||||
|
|
||||||
diffDays :: E.SqlExpr (E.Value Day) -> E.SqlExpr (E.Value Day) -> E.SqlExpr (E.Value Int)
|
diffDays :: E.SqlExpr (E.Value Day) -> E.SqlExpr (E.Value Day) -> E.SqlExpr (E.Value Int)
|
||||||
@ -618,3 +815,16 @@ instance (PersistField a1, PersistField a2, PersistField b, Finite a1, Finite a2
|
|||||||
]
|
]
|
||||||
(E.else_ $ E.else_ $ E.veryUnsafeCoerceSqlExprValue (E.nothing :: E.SqlExpr (E.Value (Maybe ()))))
|
(E.else_ $ E.else_ $ E.veryUnsafeCoerceSqlExprValue (E.nothing :: E.SqlExpr (E.Value (Maybe ()))))
|
||||||
|
|
||||||
|
psqlVersion_ :: E.SqlExpr (E.Value Text)
|
||||||
|
psqlVersion_ = E.unsafeSqlFunction "VERSION" ()
|
||||||
|
|
||||||
|
-- Suspected to cause trouble. Needs more testing!
|
||||||
|
-- truncateTable :: (MonadIO m, BackendCompatible SqlBackend backend, PersistEntity record)
|
||||||
|
-- => record -> ReaderT backend m ()
|
||||||
|
-- truncateTable tbl = E.rawExecute ("TRUNCATE TABLE " <> P.tableName tbl <> " RESTART IDENTITY") []
|
||||||
|
|
||||||
|
truncateTable :: (MonadIO m, BackendCompatible SqlBackend backend, PersistEntity record) -- TODO: test this code
|
||||||
|
=> proxy record -> ReaderT backend m ()
|
||||||
|
truncateTable tbl =
|
||||||
|
let tblName :: Text = P.unEntityNameDB $ P.entityDB $ P.entityDef tbl
|
||||||
|
in E.rawExecute ("TRUNCATE TABLE " <> tblName <> " RESTART IDENTITY") []
|
||||||
@ -539,8 +539,11 @@ tagAccessPredicate AuthAdmin = cacheAPSchoolFunction SchoolAdmin (Just $ Right d
|
|||||||
return Authorized
|
return Authorized
|
||||||
|
|
||||||
tagAccessPredicate AuthSupervisor = APDB $ \_ _ mAuthId route _ -> case route of
|
tagAccessPredicate AuthSupervisor = APDB $ \_ _ mAuthId route _ -> case route of
|
||||||
ForProfileR cID -> checkSupervisor (mAuthId, cID)
|
ForProfileR cID -> checkSupervisor (mAuthId, cID)
|
||||||
ForProfileDataR cID -> checkSupervisor (mAuthId, cID)
|
ForProfileDataR cID -> checkSupervisor (mAuthId, cID)
|
||||||
|
FirmAllR -> checkAnySupervisor mAuthId
|
||||||
|
FirmUsersR fsh -> checkCompanySupervisor (mAuthId, fsh)
|
||||||
|
FirmSupersR fsh -> checkCompanySupervisor (mAuthId, fsh)
|
||||||
r -> $unsupportedAuthPredicate AuthSupervisor r
|
r -> $unsupportedAuthPredicate AuthSupervisor r
|
||||||
where
|
where
|
||||||
checkSupervisor sup@(mAuthId, cID) = $cachedHereBinary sup . exceptT return return $ do
|
checkSupervisor sup@(mAuthId, cID) = $cachedHereBinary sup . exceptT return return $ do
|
||||||
@ -549,6 +552,17 @@ tagAccessPredicate AuthSupervisor = APDB $ \_ _ mAuthId route _ -> case route of
|
|||||||
isSupervisor <- lift . existsBy $ UniqueUserSupervisor authId uid
|
isSupervisor <- lift . existsBy $ UniqueUserSupervisor authId uid
|
||||||
guardMExceptT isSupervisor (unauthorizedI MsgUnauthorizedSupervisor)
|
guardMExceptT isSupervisor (unauthorizedI MsgUnauthorizedSupervisor)
|
||||||
return Authorized
|
return Authorized
|
||||||
|
checkCompanySupervisor sup@(mAuthId, fsh) = $cachedHereBinary sup . exceptT return return $ do
|
||||||
|
authId <- maybeExceptT AuthenticationRequired $ return mAuthId
|
||||||
|
-- isSupervisor <- lift . existsBy $ UniqueUserCompany authId $ CompanyKey fsh
|
||||||
|
isSupervisor <- lift $ exists [UserCompanyUser ==. authId, UserCompanyCompany ==. CompanyKey fsh, UserCompanySupervisor ==. True]
|
||||||
|
guardMExceptT isSupervisor (unauthorizedI $ MsgUnauthorizedCompanySupervisor fsh)
|
||||||
|
return Authorized
|
||||||
|
checkAnySupervisor mAuthId = $cachedHereBinary mAuthId . exceptT return return $ do
|
||||||
|
authId <- maybeExceptT AuthenticationRequired $ return mAuthId
|
||||||
|
isSupervisor <- lift $ exists [UserSupervisorSupervisor ==. authId]
|
||||||
|
guardMExceptT isSupervisor (unauthorizedI MsgUnauthorizedAnySupervisor)
|
||||||
|
return Authorized
|
||||||
|
|
||||||
tagAccessPredicate AuthSystemExamOffice = cacheAPSystemFunction SystemExamOffice (Just $ Right diffHour) $ \mAuthId' _ _ examOfficeList -> if
|
tagAccessPredicate AuthSystemExamOffice = cacheAPSystemFunction SystemExamOffice (Just $ Right diffHour) $ \mAuthId' _ _ examOfficeList -> if
|
||||||
| maybe True (`Set.notMember` examOfficeList) mAuthId' -> Right $ if
|
| maybe True (`Set.notMember` examOfficeList) mAuthId' -> Right $ if
|
||||||
|
|||||||
@ -1,9 +1,14 @@
|
|||||||
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Sarah Vaupel <vaupel.sarah@campus.lmu.de>,Steffen Jost <jost@cip.ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>
|
-- SPDX-FileCopyrightText: 2022-23 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Sarah Vaupel <vaupel.sarah@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>
|
||||||
--
|
--
|
||||||
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
-- To add new language files:
|
||||||
|
-- 1. include new statement, e.g. mkMessageAddition ''UniWorX "Print" "messages/uniworx/categories/print" "de-de-formal"
|
||||||
|
-- 2. create appropriate translation files in the specified folder
|
||||||
|
-- 3. add constructor to list of module exports
|
||||||
|
|
||||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-unused-top-binds #-}
|
||||||
|
|
||||||
module Foundation.I18n
|
module Foundation.I18n
|
||||||
( appLanguages, appLanguagesOpts
|
( appLanguages, appLanguagesOpts
|
||||||
@ -20,6 +25,7 @@ module Foundation.I18n
|
|||||||
, UniWorXI18nMessage(..),UniWorXJobsHandlerMessage(..), UniWorXModelTypesMessage(..), UniWorXYesodMiddlewareMessage(..)
|
, UniWorXI18nMessage(..),UniWorXJobsHandlerMessage(..), UniWorXModelTypesMessage(..), UniWorXYesodMiddlewareMessage(..)
|
||||||
, UniWorXQualificationMessage(..)
|
, UniWorXQualificationMessage(..)
|
||||||
, UniWorXPrintMessage(..)
|
, UniWorXPrintMessage(..)
|
||||||
|
, UniWorXFirmMessage(..)
|
||||||
, UniWorXAvsMessage(..)
|
, UniWorXAvsMessage(..)
|
||||||
, UniWorXAuthorshipStatementMessage(..)
|
, UniWorXAuthorshipStatementMessage(..)
|
||||||
, ShortTermIdentifier(..)
|
, ShortTermIdentifier(..)
|
||||||
@ -33,10 +39,12 @@ module Foundation.I18n
|
|||||||
, StudyDegreeTerm(..)
|
, StudyDegreeTerm(..)
|
||||||
, ShortStudyFieldType(..)
|
, ShortStudyFieldType(..)
|
||||||
, StudyDegreeTermType(..)
|
, StudyDegreeTermType(..)
|
||||||
, ErrorResponseTitle(..)
|
, ErrorResponseTitle(..)
|
||||||
, UniWorXMessages(..)
|
, UniWorXMessages(..)
|
||||||
, uniworxMessages
|
, uniworxMessages
|
||||||
, unRenderMessage, unRenderMessage', unRenderMessageLenient
|
, unRenderMessage, unRenderMessage', unRenderMessageLenient
|
||||||
|
, SomeMessages(..)
|
||||||
|
, someMessages
|
||||||
, module Foundation.I18n.TH
|
, module Foundation.I18n.TH
|
||||||
) where
|
) where
|
||||||
|
|
||||||
@ -79,21 +87,30 @@ pluralDE num singularForm pluralForm
|
|||||||
| num == 1 = singularForm
|
| num == 1 = singularForm
|
||||||
| otherwise = pluralForm
|
| otherwise = pluralForm
|
||||||
|
|
||||||
-- pluralDEx :: (Eq a, Num a) => Char -> a -> Text -> Text
|
pluralDEx :: (Eq a, Num a) => Char -> a -> Text -> Text
|
||||||
-- -- ^ @pluralENs n "Monat" = pluralEN n "Monat" "Monate"@
|
pluralDEx c n t = pluralDE n t $ t `snoc` c
|
||||||
-- pluralDEx c n t = pluralDE n t $ t `snoc` c
|
|
||||||
|
|
||||||
-- -- | like `pluralDEe` but also prefixes with the number
|
-- | like `pluralDEx` but also prefixes with the number
|
||||||
-- pluralDExN :: (Eq a, Num a, Show a) => Char -> a -> Text -> Text
|
pluralDExN :: (Eq a, Num a, Show a) => Char -> a -> Text -> Text
|
||||||
-- pluralDExN c n t = tshow n <> cons ' ' (pluralDEx c n t)
|
pluralDExN c n t = tshow n <> cons ' ' (pluralDEx c n t)
|
||||||
|
|
||||||
pluralDEe :: (Eq a, Num a) => a -> Text -> Text
|
pluralDEe :: (Eq a, Num a) => a -> Text -> Text
|
||||||
-- ^ @pluralENs n "Monat" = pluralEN n "Monat" "Monate"@
|
-- ^ @pluralDEe n "Monat" = pluralDEe n "Monat" "Monate"@
|
||||||
pluralDEe n t = pluralDE n t $ t `snoc` 'e'
|
pluralDEe = pluralDEx 'e'
|
||||||
|
|
||||||
-- | like `pluralDEe` but also prefixes with the number
|
-- | like `pluralDEe` but also prefixes with the number
|
||||||
pluralDEeN :: (Eq a, Num a, Show a) => a -> Text -> Text
|
pluralDEeN :: (Eq a, Num a, Show a) => a -> Text -> Text
|
||||||
pluralDEeN n t = tshow n <> cons ' ' (pluralDEe n t)
|
pluralDEeN = pluralDExN 'e'
|
||||||
|
|
||||||
|
-- | postfix plural with an 'n'
|
||||||
|
pluralDEn :: (Eq a, Num a) => a -> Text -> Text
|
||||||
|
-- ^ @pluralENs n "Monat" = pluralEN n "Monat" "Monate"@
|
||||||
|
pluralDEn = pluralDEx 'n'
|
||||||
|
|
||||||
|
-- | like `pluralDEn` but also prefixes with the number
|
||||||
|
pluralDEnN :: (Eq a, Num a, Show a) => a -> Text -> Text
|
||||||
|
pluralDEnN = pluralDExN 'n'
|
||||||
|
|
||||||
|
|
||||||
noneOneMoreDE :: (Eq a, Num a)
|
noneOneMoreDE :: (Eq a, Num a)
|
||||||
=> a -- ^ Count
|
=> a -- ^ Count
|
||||||
@ -106,14 +123,14 @@ noneOneMoreDE num noneText singularForm pluralForm
|
|||||||
| num == 1 = singularForm
|
| num == 1 = singularForm
|
||||||
| otherwise = pluralForm
|
| otherwise = pluralForm
|
||||||
|
|
||||||
-- noneMoreDE :: (Eq a, Num a)
|
noneMoreDE :: (Eq a, Num a)
|
||||||
-- => a -- ^ Count
|
=> a -- ^ Count
|
||||||
-- -> Text -- ^ None
|
-> Text -- ^ None
|
||||||
-- -> Text -- ^ Some
|
-> Text -- ^ Some
|
||||||
-- -> Text
|
-> Text
|
||||||
-- noneMoreDE num noneText someText
|
noneMoreDE num noneText someText
|
||||||
-- | num == 0 = noneText
|
| num == 0 = noneText
|
||||||
-- | otherwise = someText
|
| otherwise = someText
|
||||||
|
|
||||||
pluralEN :: (Eq a, Num a)
|
pluralEN :: (Eq a, Num a)
|
||||||
=> a -- ^ Count
|
=> a -- ^ Count
|
||||||
@ -128,7 +145,7 @@ pluralENs :: (Eq a, Num a)
|
|||||||
=> a -- ^ Count
|
=> a -- ^ Count
|
||||||
-> Text -- ^ Singular
|
-> Text -- ^ Singular
|
||||||
-> Text
|
-> Text
|
||||||
-- ^ @pluralENs n "foo" = pluralEN n "foo" "foos"@
|
-- ^ @pluralENs n "foo" = pluralEN n "foo" "foos"@
|
||||||
pluralENs n t = pluralEN n t $ t `snoc` 's'
|
pluralENs n t = pluralEN n t $ t `snoc` 's'
|
||||||
|
|
||||||
-- | like `pluralENs` but also prefixes with the number
|
-- | like `pluralENs` but also prefixes with the number
|
||||||
@ -146,14 +163,14 @@ noneOneMoreEN num noneText singularForm pluralForm
|
|||||||
| num == 1 = singularForm
|
| num == 1 = singularForm
|
||||||
| otherwise = pluralForm
|
| otherwise = pluralForm
|
||||||
|
|
||||||
-- noneMoreEN :: (Eq a, Num a)
|
noneMoreEN :: (Eq a, Num a)
|
||||||
-- => a -- ^ Count
|
=> a -- ^ Count
|
||||||
-- -> Text -- ^ None
|
-> Text -- ^ None
|
||||||
-- -> Text -- ^ Some
|
-> Text -- ^ Some
|
||||||
-- -> Text
|
-> Text
|
||||||
-- noneMoreEN num noneText someText
|
noneMoreEN num noneText someText
|
||||||
-- | num == 0 = noneText
|
| num == 0 = noneText
|
||||||
-- | otherwise = someText
|
| otherwise = someText
|
||||||
|
|
||||||
_ordinalEN :: ToMessage a
|
_ordinalEN :: ToMessage a
|
||||||
=> a
|
=> a
|
||||||
@ -173,20 +190,20 @@ notEN :: Bool -> Text
|
|||||||
notEN = bool "not" ""
|
notEN = bool "not" ""
|
||||||
|
|
||||||
{- -- TODO: use this is message eventually
|
{- -- TODO: use this is message eventually
|
||||||
-- Commonly used plurals
|
-- Commonly used plurals
|
||||||
data Thing = Person | Examinee
|
data Thing = Person | Examinee
|
||||||
deriving (Eq)
|
deriving (Eq)
|
||||||
|
|
||||||
thingDE :: Int -> Thing -> Text
|
thingDE :: Int -> Thing -> Text
|
||||||
thingDE num = (tshow num <>) . Text.cons ' ' . thing
|
thingDE num = (tshow num <>) . Text.cons ' ' . thing
|
||||||
where
|
where
|
||||||
thing :: Thing -> Text
|
thing :: Thing -> Text
|
||||||
thing Person = pluralDE num "Person" "Personen"
|
thing Person = pluralDE num "Person" "Personen"
|
||||||
thing Examinee = pluralDE num "Prüfling" "Prüflinge"
|
thing Examinee = pluralDE num "Prüfling" "Prüflinge"
|
||||||
|
|
||||||
thingEN :: Int -> Thing -> Text
|
thingEN :: Int -> Thing -> Text
|
||||||
thingEN num t = tshow num <> Text.cons ' ' (thing t)
|
thingEN num t = tshow num <> Text.cons ' ' (thing t)
|
||||||
where
|
where
|
||||||
thing :: Thing -> Text
|
thing :: Thing -> Text
|
||||||
thing Person = pluralENs num "person"
|
thing Person = pluralENs num "person"
|
||||||
thing Examinee = pluralENs num "examinee"
|
thing Examinee = pluralENs num "examinee"
|
||||||
@ -197,6 +214,14 @@ maybeToMessage :: ToMessage m => Text -> Maybe m -> Text -> Text
|
|||||||
maybeToMessage _ Nothing _ = mempty
|
maybeToMessage _ Nothing _ = mempty
|
||||||
maybeToMessage before (Just x) after = before <> toMessage x <> after
|
maybeToMessage before (Just x) after = before <> toMessage x <> after
|
||||||
|
|
||||||
|
maybeBoolMessage :: Maybe Bool -> Text -> Text -> Text -> Text
|
||||||
|
maybeBoolMessage Nothing n _ _ = n
|
||||||
|
maybeBoolMessage (Just True) _ t _ = t
|
||||||
|
maybeBoolMessage (Just False) _ _ f = f
|
||||||
|
|
||||||
|
-- | Convenience function avoiding type signatures
|
||||||
|
boolText :: Text -> Text -> Bool -> Text
|
||||||
|
boolText = bool
|
||||||
|
|
||||||
newtype ShortTermIdentifier = ShortTermIdentifier TermIdentifier
|
newtype ShortTermIdentifier = ShortTermIdentifier TermIdentifier
|
||||||
deriving stock (Eq, Ord, Read, Show)
|
deriving stock (Eq, Ord, Read, Show)
|
||||||
@ -233,6 +258,7 @@ mkMessageAddition ''UniWorX "Send" "messages/uniworx/categories/send" "de-de-for
|
|||||||
mkMessageAddition ''UniWorX "YesodMiddleware" "messages/uniworx/categories/yesod_middleware" "de-de-formal"
|
mkMessageAddition ''UniWorX "YesodMiddleware" "messages/uniworx/categories/yesod_middleware" "de-de-formal"
|
||||||
mkMessageAddition ''UniWorX "User" "messages/uniworx/categories/user" "de-de-formal"
|
mkMessageAddition ''UniWorX "User" "messages/uniworx/categories/user" "de-de-formal"
|
||||||
mkMessageAddition ''UniWorX "Print" "messages/uniworx/categories/print" "de-de-formal"
|
mkMessageAddition ''UniWorX "Print" "messages/uniworx/categories/print" "de-de-formal"
|
||||||
|
mkMessageAddition ''UniWorX "Firm" "messages/uniworx/categories/firm" "de-de-formal"
|
||||||
mkMessageAddition ''UniWorX "Button" "messages/uniworx/utils/buttons" "de-de-formal"
|
mkMessageAddition ''UniWorX "Button" "messages/uniworx/utils/buttons" "de-de-formal"
|
||||||
mkMessageAddition ''UniWorX "Form" "messages/uniworx/utils/handler_form" "de-de-formal"
|
mkMessageAddition ''UniWorX "Form" "messages/uniworx/utils/handler_form" "de-de-formal"
|
||||||
mkMessageAddition ''UniWorX "TableColumn" "messages/uniworx/utils/table_column" "de-de-formal"
|
mkMessageAddition ''UniWorX "TableColumn" "messages/uniworx/utils/table_column" "de-de-formal"
|
||||||
@ -248,9 +274,28 @@ mkMessageVariant ''UniWorX ''ButtonMessage "messages/button" "de"
|
|||||||
mkMessageVariant ''UniWorX ''FrontendMessage "messages/frontend" "de-de-formal"
|
mkMessageVariant ''UniWorX ''FrontendMessage "messages/frontend" "de-de-formal"
|
||||||
|
|
||||||
embedRenderMessage ''UniWorX ''AvsLicence id -- required by UniWorXAvsMessages
|
embedRenderMessage ''UniWorX ''AvsLicence id -- required by UniWorXAvsMessages
|
||||||
|
|
||||||
mkMessageAddition ''UniWorX "Qualification" "messages/uniworx/categories/qualification" "de-de-formal"
|
mkMessageAddition ''UniWorX "Qualification" "messages/uniworx/categories/qualification" "de-de-formal"
|
||||||
mkMessageAddition ''UniWorX "Avs" "messages/uniworx/categories/avs" "de-de-formal"
|
mkMessageAddition ''UniWorX "Avs" "messages/uniworx/categories/avs" "de-de-formal"
|
||||||
|
|
||||||
|
embedRenderMessage ''UniWorX ''LmsStatus (uncurry ((<>) . (<> "Status")) . Text.splitAt 3)
|
||||||
|
|
||||||
|
|
||||||
|
newtype SomeMessages master = SomeMessages [SomeMessage master]
|
||||||
|
deriving newtype (Semigroup, Monoid)
|
||||||
|
|
||||||
|
instance master ~ master' => RenderMessage master (SomeMessages master') where
|
||||||
|
renderMessage a b (SomeMessages msgs) = Text.intercalate "\n " $ renderMessage a b <$> msgs
|
||||||
|
|
||||||
|
-- | convenienience function if all messages happen to belong to the exact same type
|
||||||
|
someMessages :: RenderMessage master msg => [msg] -> SomeMessages master
|
||||||
|
someMessages msgs = SomeMessages $ SomeMessage <$> msgs
|
||||||
|
|
||||||
|
|
||||||
|
instance RenderMessage UniWorX (Maybe LmsStatus) where -- useful for Filter with optionsFinite
|
||||||
|
renderMessage f ls (Just s) = renderMessage f ls s
|
||||||
|
renderMessage f ls Nothing = renderMessage f ls MsgLmsStateOpen
|
||||||
|
|
||||||
instance RenderMessage UniWorX AvsDataCardColor where
|
instance RenderMessage UniWorX AvsDataCardColor where
|
||||||
renderMessage _foundation _ls (AvsCardColorMisc t) = Text.cons '*' t
|
renderMessage _foundation _ls (AvsCardColorMisc t) = Text.cons '*' t
|
||||||
renderMessage f ls AvsCardColorGrün = renderMessage f ls MsgAvsCardColorGreen
|
renderMessage f ls AvsCardColorGrün = renderMessage f ls MsgAvsCardColorGreen
|
||||||
@ -316,6 +361,7 @@ appLanguagesOpts = do
|
|||||||
langOptions = map mkOption $ toList appLanguages
|
langOptions = map mkOption $ toList appLanguages
|
||||||
return $ mkOptionList langOptions
|
return $ mkOptionList langOptions
|
||||||
|
|
||||||
|
embedRenderMessage ''UniWorX ''HealthCheck id
|
||||||
embedRenderMessage ''UniWorX ''MessageStatus ("Message" <>)
|
embedRenderMessage ''UniWorX ''MessageStatus ("Message" <>)
|
||||||
embedRenderMessage ''UniWorX ''NotificationTrigger $ ("NotificationTrigger" <>) . concat . drop 1 . splitCamel
|
embedRenderMessage ''UniWorX ''NotificationTrigger $ ("NotificationTrigger" <>) . concat . drop 1 . splitCamel
|
||||||
embedRenderMessage ''UniWorX ''StudyFieldType id
|
embedRenderMessage ''UniWorX ''StudyFieldType id
|
||||||
@ -568,12 +614,12 @@ unRenderMessage = unRenderMessage' (==)
|
|||||||
|
|
||||||
unRenderMessageLenient :: forall a master. (Ord a, Finite a, RenderMessage master a) => master -> Text -> [a]
|
unRenderMessageLenient :: forall a master. (Ord a, Finite a, RenderMessage master a) => master -> Text -> [a]
|
||||||
unRenderMessageLenient = unRenderMessage' cmp
|
unRenderMessageLenient = unRenderMessage' cmp
|
||||||
where cmp = (==) `on` mk . under packed (filter Char.isAlphaNum . concatMap unidecode)
|
where cmp = (==) `on` mk . under packed (concatMap $ filter Char.isAlphaNum . unidecode)
|
||||||
|
|
||||||
|
|
||||||
instance Default DateTimeFormatter where
|
instance Default DateTimeFormatter where
|
||||||
def = mkDateTimeFormatter (getTimeLocale' []) def appTZ
|
def = mkDateTimeFormatter (getTimeLocale' []) def appTZ
|
||||||
|
|
||||||
instance RenderMessage UniWorX Address where
|
instance RenderMessage UniWorX Address where
|
||||||
renderMessage s l a@Address{addressName = Just aname} = aname <> cons ' ' (renderMessage s l a{addressName=Nothing})
|
renderMessage s l a@Address{addressName = Just aname} = aname <> cons ' ' (renderMessage s l a{addressName=Nothing})
|
||||||
renderMessage _ _ Address{addressEmail = mail} = "<" <> mail <> ">"
|
renderMessage _ _ Address{addressEmail = mail} = "<" <> mail <> ">"
|
||||||
|
|||||||
@ -87,9 +87,9 @@ breadcrumb (AdminUserR cID) = useRunDB . maybeT (i18nCrumb MsgBreadcrumbUser $ J
|
|||||||
uid <- decrypt cID
|
uid <- decrypt cID
|
||||||
User{..} <- MaybeT $ get uid
|
User{..} <- MaybeT $ get uid
|
||||||
return (userDisplayName, Just UsersR)
|
return (userDisplayName, Just UsersR)
|
||||||
breadcrumb (AdminUserDeleteR cID) = i18nCrumb MsgBreadcrumbUserDelete . Just $ AdminUserR cID
|
breadcrumb (AdminUserDeleteR cID) = i18nCrumb MsgBreadcrumbUserDelete . Just $ AdminUserR cID
|
||||||
breadcrumb (AdminHijackUserR cID) = i18nCrumb MsgBreadcrumbUserHijack . Just $ AdminUserR cID
|
breadcrumb (AdminHijackUserR cID) = i18nCrumb MsgBreadcrumbUserHijack . Just $ AdminUserR cID
|
||||||
breadcrumb (UserNotificationR cID) = useRunDB $ do
|
breadcrumb (UserNotificationR cID) = useRunDB $ do
|
||||||
mayList <- hasReadAccessTo UsersR
|
mayList <- hasReadAccessTo UsersR
|
||||||
if
|
if
|
||||||
| mayList
|
| mayList
|
||||||
@ -112,6 +112,7 @@ breadcrumb AdminTestPdfR = i18nCrumb MsgMenuAdminTest $ Just
|
|||||||
breadcrumb AdminErrMsgR = i18nCrumb MsgMenuAdminErrMsg $ Just AdminR
|
breadcrumb AdminErrMsgR = i18nCrumb MsgMenuAdminErrMsg $ Just AdminR
|
||||||
breadcrumb AdminTokensR = i18nCrumb MsgMenuAdminTokens $ Just AdminR
|
breadcrumb AdminTokensR = i18nCrumb MsgMenuAdminTokens $ Just AdminR
|
||||||
breadcrumb AdminCrontabR = i18nCrumb MsgBreadcrumbAdminCrontab $ Just AdminR
|
breadcrumb AdminCrontabR = i18nCrumb MsgBreadcrumbAdminCrontab $ Just AdminR
|
||||||
|
breadcrumb AdminJobsR = i18nCrumb MsgBreadcrumbAdminJobs $ Just AdminCrontabR
|
||||||
breadcrumb AdminAvsR = i18nCrumb MsgMenuAvs $ Just AdminR
|
breadcrumb AdminAvsR = i18nCrumb MsgMenuAvs $ Just AdminR
|
||||||
breadcrumb AdminAvsUserR{} = i18nCrumb MsgAvsPersonInfo $ Just AdminAvsR
|
breadcrumb AdminAvsUserR{} = i18nCrumb MsgAvsPersonInfo $ Just AdminAvsR
|
||||||
breadcrumb AdminLdapR = i18nCrumb MsgMenuLdap $ Just AdminR
|
breadcrumb AdminLdapR = i18nCrumb MsgMenuLdap $ Just AdminR
|
||||||
@ -120,12 +121,27 @@ breadcrumb ProblemUnreachableR = i18nCrumb MsgProblemsUnreachableHeading $ Just
|
|||||||
breadcrumb ProblemWithoutAvsId = i18nCrumb MsgProblemsNoAvsIdHeading $ Just AdminProblemsR
|
breadcrumb ProblemWithoutAvsId = i18nCrumb MsgProblemsNoAvsIdHeading $ Just AdminProblemsR
|
||||||
breadcrumb ProblemFbutNoR = i18nCrumb MsgProblemsRWithoutFHeading $ Just AdminProblemsR
|
breadcrumb ProblemFbutNoR = i18nCrumb MsgProblemsRWithoutFHeading $ Just AdminProblemsR
|
||||||
breadcrumb ProblemAvsSynchR = i18nCrumb MsgProblemsAvsSynchHeading $ Just AdminProblemsR
|
breadcrumb ProblemAvsSynchR = i18nCrumb MsgProblemsAvsSynchHeading $ Just AdminProblemsR
|
||||||
|
breadcrumb ProblemAvsErrorR = i18nCrumb MsgProblemsAvsErrorHeading $ Just AdminProblemsR
|
||||||
|
breadcrumb ConfigInterfacesR = i18nCrumb MsgConfigInterfacesHeading $ Just AdminProblemsR
|
||||||
|
|
||||||
breadcrumb PrintCenterR = i18nCrumb MsgMenuApc Nothing
|
breadcrumb FirmAllR = i18nCrumb MsgMenuFirms Nothing
|
||||||
|
breadcrumb FirmsCommR{} = i18nCrumb MsgMenuFirmsComm $ Just FirmAllR
|
||||||
|
breadcrumb FirmUsersR{} = i18nCrumb MsgMenuFirmUsers $ Just FirmAllR
|
||||||
|
breadcrumb (FirmSupersR fsh)= i18nCrumb MsgMenuFirmSupervisors $ Just $ FirmUsersR fsh
|
||||||
|
breadcrumb (FirmCommR fsh)= i18nCrumb MsgMenuFirmsComm $ Just $ FirmUsersR fsh
|
||||||
|
|
||||||
|
breadcrumb CommCenterR = i18nCrumb MsgMenuCommCenter Nothing
|
||||||
|
breadcrumb MailCenterR = i18nCrumb MsgMenuMailCenter $ Just CommCenterR
|
||||||
|
breadcrumb MailHtmlR{} = i18nCrumb MsgMenuMailHtml $ Just MailCenterR
|
||||||
|
breadcrumb MailPlainR{} = i18nCrumb MsgMenuMailPlain $ Just MailCenterR
|
||||||
|
breadcrumb (MailAttachmentR mid _) = i18nCrumb MsgMenuMailAttachment $ Just $ MailHtmlR mid
|
||||||
|
|
||||||
|
breadcrumb PrintCenterR = i18nCrumb MsgMenuApc $ Just CommCenterR
|
||||||
breadcrumb PrintSendR = i18nCrumb MsgMenuPrintSend $ Just PrintCenterR
|
breadcrumb PrintSendR = i18nCrumb MsgMenuPrintSend $ Just PrintCenterR
|
||||||
breadcrumb PrintDownloadR{} = i18nCrumb MsgMenuPrintDownload $ Just PrintCenterR
|
breadcrumb PrintDownloadR{} = i18nCrumb MsgMenuPrintDownload $ Just PrintCenterR
|
||||||
breadcrumb PrintAckR{} = i18nCrumb MsgMenuPrintSend $ Just PrintCenterR -- never displayed
|
breadcrumb PrintAckR{} = i18nCrumb MsgMenuPrintSend $ Just PrintCenterR -- never displayed
|
||||||
breadcrumb PrintAckDirectR{}= i18nCrumb MsgMenuPrintSend $ Just PrintCenterR -- never displayed
|
breadcrumb PrintAckDirectR{}= i18nCrumb MsgMenuPrintAck $ Just PrintCenterR
|
||||||
|
breadcrumb PrintLogR = i18nCrumb MsgMenuPrintLog $ Just PrintCenterR
|
||||||
|
|
||||||
breadcrumb SchoolListR = i18nCrumb MsgMenuSchoolList $ Just AdminR
|
breadcrumb SchoolListR = i18nCrumb MsgMenuSchoolList $ Just AdminR
|
||||||
breadcrumb (SchoolR ssh sRoute) = case sRoute of
|
breadcrumb (SchoolR ssh sRoute) = case sRoute of
|
||||||
@ -156,9 +172,10 @@ breadcrumb FaqR = i18nCrumb MsgBreadcrumbFaq $ Just InfoR
|
|||||||
breadcrumb HelpR = i18nCrumb MsgMenuHelp Nothing
|
breadcrumb HelpR = i18nCrumb MsgMenuHelp Nothing
|
||||||
|
|
||||||
|
|
||||||
breadcrumb HealthR = i18nCrumb MsgMenuHealth Nothing
|
breadcrumb HealthR = i18nCrumb MsgMenuHealth Nothing
|
||||||
breadcrumb InstanceR = i18nCrumb MsgMenuInstance Nothing
|
breadcrumb (HealthInterfaceR _) = i18nCrumb MsgMenuHealthInterface (Just HealthR)
|
||||||
breadcrumb StatusR = i18nCrumb MsgMenuHealth Nothing -- never displayed
|
breadcrumb InstanceR = i18nCrumb MsgMenuInstance Nothing
|
||||||
|
breadcrumb StatusR = i18nCrumb MsgMenuHealth Nothing -- never displayed
|
||||||
|
|
||||||
breadcrumb QualificationAllR = i18nCrumb MsgMenuQualifications Nothing
|
breadcrumb QualificationAllR = i18nCrumb MsgMenuQualifications Nothing
|
||||||
breadcrumb (QualificationSchoolR ssh ) = useRunDB . maybeT (i18nCrumb MsgBreadcrumbSchool . Just $ SchoolListR) $ do -- redirect only, used in other breadcrumbs
|
breadcrumb (QualificationSchoolR ssh ) = useRunDB . maybeT (i18nCrumb MsgBreadcrumbSchool . Just $ SchoolListR) $ do -- redirect only, used in other breadcrumbs
|
||||||
@ -177,16 +194,17 @@ breadcrumb (LmsR ssh qsh) = useRunDB . maybeT (i18nCrumb MsgBrea
|
|||||||
guardM . lift . existsBy $ SchoolQualificationShort ssh qsh
|
guardM . lift . existsBy $ SchoolQualificationShort ssh qsh
|
||||||
return (CI.original qsh, Just $ LmsSchoolR ssh)
|
return (CI.original qsh, Just $ LmsSchoolR ssh)
|
||||||
breadcrumb (LmsEditR ssh qsh) = i18nCrumb MsgMenuLmsEdit $ Just $ LmsR ssh qsh
|
breadcrumb (LmsEditR ssh qsh) = i18nCrumb MsgMenuLmsEdit $ Just $ LmsR ssh qsh
|
||||||
breadcrumb (LmsUsersR ssh qsh) = i18nCrumb MsgMenuLmsUsers $ Just $ LmsR ssh qsh
|
-- v2
|
||||||
breadcrumb (LmsUsersDirectR ssh qsh) = i18nCrumb MsgMenuLmsUsers $ Just $ LmsUsersR ssh qsh -- never displayed, TypedContent
|
breadcrumb (LmsLearnersR ssh qsh) = i18nCrumb MsgMenuLmsLearners $ Just $ LmsR ssh qsh
|
||||||
breadcrumb (LmsUserlistR ssh qsh) = i18nCrumb MsgMenuLmsUserlist $ Just $ LmsR ssh qsh
|
breadcrumb (LmsLearnersDirectR ssh qsh) = i18nCrumb MsgMenuLmsLearners $ Just $ LmsLearnersR ssh qsh -- never displayed, TypedContent
|
||||||
breadcrumb (LmsUserlistUploadR ssh qsh) = i18nCrumb MsgMenuLmsUpload $ Just $ LmsUserlistR ssh qsh
|
breadcrumb (LmsReportR ssh qsh) = i18nCrumb MsgMenuLmsReport $ Just $ LmsR ssh qsh
|
||||||
breadcrumb (LmsUserlistDirectR ssh qsh) = i18nCrumb MsgMenuLmsUpload $ Just $ LmsUserlistR ssh qsh -- never displayed
|
breadcrumb (LmsReportUploadR ssh qsh) = i18nCrumb MsgMenuLmsUpload $ Just $ LmsReportR ssh qsh
|
||||||
breadcrumb (LmsResultR ssh qsh) = i18nCrumb MsgMenuLmsResult $ Just $ LmsR ssh qsh
|
breadcrumb (LmsReportDirectR ssh qsh) = i18nCrumb MsgMenuLmsUpload $ Just $ LmsReportR ssh qsh -- never displayed
|
||||||
breadcrumb (LmsResultUploadR ssh qsh) = i18nCrumb MsgMenuLmsUpload $ Just $ LmsResultR ssh qsh
|
--
|
||||||
breadcrumb (LmsResultDirectR ssh qsh) = i18nCrumb MsgMenuLmsUpload $ Just $ LmsResultR ssh qsh -- never displayed
|
|
||||||
breadcrumb (LmsIdentR ssh qsh _ ) = breadcrumb $ LmsR ssh qsh -- just a redirect
|
breadcrumb (LmsIdentR ssh qsh _ ) = breadcrumb $ LmsR ssh qsh -- just a redirect
|
||||||
breadcrumb (LmsUserR _) = i18nCrumb MsgMenuLmsUser $ Just LmsAllR
|
breadcrumb (LmsUserR ssh _qsh u ) = i18nCrumb MsgMenuLmsUser $ Just $ LmsUserSchoolR u ssh
|
||||||
|
breadcrumb (LmsUserSchoolR u _ ) = i18nCrumb MsgMenuLmsUserSchool $ Just $ LmsUserAllR u
|
||||||
|
breadcrumb (LmsUserAllR _ ) = i18nCrumb MsgMenuLmsUserAll $ Just LmsAllR
|
||||||
-- breadcrumb (LmsFakeR ssh qsh) = i18nCrumb MsgMenuLmsFake $ Just $ LmsR ssh qsh -- TODO: remove in production
|
-- breadcrumb (LmsFakeR ssh qsh) = i18nCrumb MsgMenuLmsFake $ Just $ LmsR ssh qsh -- TODO: remove in production
|
||||||
|
|
||||||
breadcrumb ProfileR = i18nCrumb MsgBreadcrumbProfile Nothing
|
breadcrumb ProfileR = i18nCrumb MsgBreadcrumbProfile Nothing
|
||||||
@ -283,7 +301,7 @@ breadcrumb (CourseR tid ssh csh (TutorialR tutn sRoute)) = case sRoute of
|
|||||||
TUsersR -> useRunDB . maybeT (i18nCrumb MsgBreadcrumbTutorial . Just $ CourseR tid ssh csh CTutorialListR) $ do
|
TUsersR -> useRunDB . maybeT (i18nCrumb MsgBreadcrumbTutorial . Just $ CourseR tid ssh csh CTutorialListR) $ do
|
||||||
guardM . lift . hasReadAccessTo $ CTutorialR tid ssh csh tutn TUsersR
|
guardM . lift . hasReadAccessTo $ CTutorialR tid ssh csh tutn TUsersR
|
||||||
return (CI.original tutn, Just $ CourseR tid ssh csh CTutorialListR)
|
return (CI.original tutn, Just $ CourseR tid ssh csh CTutorialListR)
|
||||||
TAddUserR -> i18nCrumb MsgMenuTutorialAddMembers . Just $ CTutorialR tid ssh csh tutn TUsersR
|
TAddUserR -> i18nCrumb MsgMenuTutorialAddMembers . Just $ CTutorialR tid ssh csh tutn TUsersR
|
||||||
TEditR -> i18nCrumb MsgMenuTutorialEdit . Just $ CTutorialR tid ssh csh tutn TUsersR
|
TEditR -> i18nCrumb MsgMenuTutorialEdit . Just $ CTutorialR tid ssh csh tutn TUsersR
|
||||||
TDeleteR -> i18nCrumb MsgMenuTutorialDelete . Just $ CTutorialR tid ssh csh tutn TUsersR
|
TDeleteR -> i18nCrumb MsgMenuTutorialDelete . Just $ CTutorialR tid ssh csh tutn TUsersR
|
||||||
TCommR -> i18nCrumb MsgMenuTutorialComm . Just $ CTutorialR tid ssh csh tutn TUsersR
|
TCommR -> i18nCrumb MsgMenuTutorialComm . Just $ CTutorialR tid ssh csh tutn TUsersR
|
||||||
@ -441,6 +459,14 @@ defNavLink navLabel navRoute = NavLink {..}
|
|||||||
navQuick' = mempty
|
navQuick' = mempty
|
||||||
navForceActive = False
|
navForceActive = False
|
||||||
|
|
||||||
|
defNavLinkModal :: (RenderMessage UniWorX msg, HasRoute UniWorX route) => msg -> route -> NavLink
|
||||||
|
defNavLinkModal navLabel navRoute = NavLink {..}
|
||||||
|
where
|
||||||
|
navAccess' = NavAccessTrue
|
||||||
|
navType = NavTypeLink { navModal = True}
|
||||||
|
navQuick' = mempty
|
||||||
|
navForceActive = False
|
||||||
|
|
||||||
navBaseRoute :: NavLink -> Route UniWorX
|
navBaseRoute :: NavLink -> Route UniWorX
|
||||||
navBaseRoute NavLink{navRoute} = urlRoute navRoute
|
navBaseRoute NavLink{navRoute} = urlRoute navRoute
|
||||||
|
|
||||||
@ -735,6 +761,18 @@ defaultLinks = fmap catMaybes . mapM runMaybeT $ -- Define the menu items of the
|
|||||||
, navForceActive = False
|
, navForceActive = False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
, return NavHeader
|
||||||
|
{ navHeaderRole = NavHeaderPrimary
|
||||||
|
, navIcon = IconCompany
|
||||||
|
, navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuFirms
|
||||||
|
, navRoute = FirmAllR
|
||||||
|
, navAccess' = NavAccessTrue
|
||||||
|
, navType = NavTypeLink { navModal = False }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
}
|
||||||
, return NavHeader
|
, return NavHeader
|
||||||
{ navHeaderRole = NavHeaderPrimary
|
{ navHeaderRole = NavHeaderPrimary
|
||||||
, navIcon = IconPrintCenter
|
, navIcon = IconPrintCenter
|
||||||
@ -1191,6 +1229,14 @@ pageActions (AdminUserR cID) = return
|
|||||||
}
|
}
|
||||||
, navChildren = []
|
, navChildren = []
|
||||||
}
|
}
|
||||||
|
, NavPageActionPrimary
|
||||||
|
{ navLink = defNavLink MsgMenuUserEdit $ ForProfileR cID
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
, NavPageActionPrimary
|
||||||
|
{ navLink = defNavLinkModal MsgUserHijack $ AdminHijackUserR cID
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
]
|
]
|
||||||
pageActions InfoR = return
|
pageActions InfoR = return
|
||||||
[ NavPageActionPrimary
|
[ NavPageActionPrimary
|
||||||
@ -1296,6 +1342,17 @@ pageActions HealthR = return
|
|||||||
}
|
}
|
||||||
, navChildren = []
|
, navChildren = []
|
||||||
}
|
}
|
||||||
|
, NavPageActionPrimary
|
||||||
|
{ navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuHealthInterface
|
||||||
|
, navRoute = HealthInterfaceR []
|
||||||
|
, navAccess' = NavAccessTrue
|
||||||
|
, navType = NavTypeLink { navModal = False }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
]
|
]
|
||||||
pageActions InstanceR = return
|
pageActions InstanceR = return
|
||||||
[ NavPageActionPrimary
|
[ NavPageActionPrimary
|
||||||
@ -1407,6 +1464,12 @@ pageActions (ForProfileR cID) = return
|
|||||||
, navChildren = []
|
, navChildren = []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
pageActions (ForProfileDataR cID) = return
|
||||||
|
[ NavPageActionPrimary
|
||||||
|
{ navLink = defNavLink MsgAdminUserHeading $ AdminUserR cID
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
]
|
||||||
pageActions TermShowR = do
|
pageActions TermShowR = do
|
||||||
participantsSecondary <- pageQuickActions NavQuickViewPageActionSecondary ParticipantsListR
|
participantsSecondary <- pageQuickActions NavQuickViewPageActionSecondary ParticipantsListR
|
||||||
return
|
return
|
||||||
@ -2319,23 +2382,16 @@ pageActions ParticipantsListR = return
|
|||||||
]
|
]
|
||||||
pageActions (LmsR sid qsh) = return
|
pageActions (LmsR sid qsh) = return
|
||||||
[ NavPageActionPrimary
|
[ NavPageActionPrimary
|
||||||
{ navLink = defNavLink MsgMenuLmsUsers $ LmsUsersR sid qsh
|
{ navLink = defNavLink MsgMenuLmsLearners $ LmsLearnersR sid qsh
|
||||||
, navChildren =
|
, navChildren =
|
||||||
[ defNavLink MsgMenuLmsDirectDownload $ LmsUsersDirectR sid qsh
|
[ defNavLink MsgMenuLmsDirectDownload $ LmsLearnersDirectR sid qsh
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
, NavPageActionPrimary
|
, NavPageActionPrimary
|
||||||
{ navLink = defNavLink MsgMenuLmsUserlist $ LmsUserlistR sid qsh
|
{ navLink = defNavLink MsgMenuLmsReport $ LmsReportR sid qsh
|
||||||
, navChildren =
|
, navChildren =
|
||||||
[ defNavLink MsgMenuLmsUpload $ LmsUserlistUploadR sid qsh
|
[ defNavLink MsgMenuLmsUpload $ LmsReportUploadR sid qsh
|
||||||
, defNavLink MsgMenuLmsDirectUpload $ LmsUserlistDirectR sid qsh
|
, defNavLink MsgMenuLmsDirectUpload $ LmsReportDirectR sid qsh
|
||||||
]
|
|
||||||
}
|
|
||||||
, NavPageActionPrimary
|
|
||||||
{ navLink = defNavLink MsgMenuLmsResult $ LmsResultR sid qsh
|
|
||||||
, navChildren =
|
|
||||||
[ defNavLink MsgMenuLmsUpload $ LmsResultUploadR sid qsh
|
|
||||||
, defNavLink MsgMenuLmsDirectUpload $ LmsResultDirectR sid qsh
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
, NavPageActionSecondary {
|
, NavPageActionSecondary {
|
||||||
@ -2358,6 +2414,18 @@ pageActions ApiDocsR = return
|
|||||||
, navChildren = []
|
, navChildren = []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
pageActions (FirmUsersR fsh) = return
|
||||||
|
[ NavPageActionPrimary
|
||||||
|
{ navLink = defNavLink MsgTableCompanyNrSupers $ FirmSupersR fsh
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
pageActions (FirmSupersR fsh) = return
|
||||||
|
[ NavPageActionPrimary
|
||||||
|
{ navLink = defNavLink MsgTableCompanyNrUsers $ FirmUsersR fsh
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
]
|
||||||
pageActions PrintCenterR = do
|
pageActions PrintCenterR = do
|
||||||
openDays <- useRunDB $ Ex.select $ do
|
openDays <- useRunDB $ Ex.select $ do
|
||||||
pj <- Ex.from $ Ex.table @PrintJob
|
pj <- Ex.from $ Ex.table @PrintJob
|
||||||
@ -2393,8 +2461,93 @@ pageActions PrintCenterR = do
|
|||||||
, navForceActive = False
|
, navForceActive = False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
printLog = NavPageActionSecondary
|
||||||
|
{ navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuPrintLog
|
||||||
|
, navRoute = PrintLogR
|
||||||
|
, navAccess' = NavAccessTrue
|
||||||
|
, navType = NavTypeLink { navModal = False }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printAck = NavPageActionSecondary
|
||||||
|
{ navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuPrintAck
|
||||||
|
, navRoute = PrintAckDirectR
|
||||||
|
, navAccess' = NavAccessTrue
|
||||||
|
, navType = NavTypeLink { navModal = False }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
}
|
||||||
dayLinks <- mapM toDayAck $ Map.toAscList dayMap
|
dayLinks <- mapM toDayAck $ Map.toAscList dayMap
|
||||||
return $ manualSend : take 9 dayLinks
|
return $ manualSend : printLog : printAck : take 9 dayLinks
|
||||||
|
|
||||||
|
pageActions CommCenterR = return
|
||||||
|
[ NavPageActionPrimary
|
||||||
|
{ navLink = defNavLink MsgMenuMailCenter MailCenterR
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
, NavPageActionPrimary
|
||||||
|
{ navLink = defNavLink MsgMenuApc PrintCenterR
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
pageActions (MailHtmlR smid) = do
|
||||||
|
sid <- decrypt smid
|
||||||
|
usrNotiSettings <- useRunDB $ runMaybeT $ do
|
||||||
|
sm <- MaybeT $ get sid
|
||||||
|
uid <- hoistMaybe $ sentMailRecipient sm
|
||||||
|
User{userDisplayName} <- MaybeT $ get uid
|
||||||
|
uuid <- liftHandler $ encrypt uid
|
||||||
|
return NavPageActionPrimary
|
||||||
|
{ navLink = defNavLink (MsgNotificationSettingsHeading userDisplayName) $ UserNotificationR uuid
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
let linkPlain = NavPageActionPrimary
|
||||||
|
{ navLink = defNavLink MsgMenuMailPlain $ MailPlainR smid
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
return $ msnoc [linkPlain] usrNotiSettings
|
||||||
|
pageActions (MailPlainR smid) = do
|
||||||
|
sid <- decrypt smid
|
||||||
|
usrNotiSettings <- useRunDB $ runMaybeT $ do
|
||||||
|
sm <- MaybeT $ get sid
|
||||||
|
uid <- hoistMaybe $ sentMailRecipient sm
|
||||||
|
User{userDisplayName} <- MaybeT $ get uid
|
||||||
|
uuid <- liftHandler $ encrypt uid
|
||||||
|
return NavPageActionPrimary
|
||||||
|
{ navLink = defNavLink (MsgNotificationSettingsHeading userDisplayName) $ UserNotificationR uuid
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
let linkHtml = NavPageActionPrimary
|
||||||
|
{ navLink = defNavLink MsgMenuMailHtml $ MailHtmlR smid
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
return $ msnoc [linkHtml] usrNotiSettings
|
||||||
|
|
||||||
|
pageActions AdminCrontabR = return
|
||||||
|
[ NavPageActionPrimary
|
||||||
|
{ navLink = defNavLink MsgMenuAdminJobs AdminJobsR
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
pageActions AdminProblemsR = return
|
||||||
|
[ NavPageActionPrimary
|
||||||
|
{ navLink = defNavLink MsgConfigInterfacesHeading ConfigInterfacesR
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
, NavPageActionPrimary
|
||||||
|
{ navLink = defNavLink MsgProblemsAvsSynchHeading ProblemAvsSynchR
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
, NavPageActionSecondary
|
||||||
|
{ navLink = defNavLink MsgProblemsAvsErrorHeading ProblemAvsErrorR
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
pageActions _ = return []
|
pageActions _ = return []
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ module Foundation.Type
|
|||||||
, _memcachedLocalARC
|
, _memcachedLocalARC
|
||||||
, SMTPPool
|
, SMTPPool
|
||||||
, _appSettings', _appStatic, _appConnPool, _appSmtpPool, _appLdapPool, _appWidgetMemcached, _appHttpManager, _appLogger, _appLogSettings, _appCryptoIDKey, _appClusterID, _appInstanceID, _appJobState, _appSessionStore, _appSecretBoxKey, _appJSONWebKeySet, _appHealthReport, _appMemcached, _appUploadCache, _appVerpSecret, _appAuthKey, _appPersonalisedSheetFilesSeedKey, _appVolatileClusterSettingsCache, _appAvsQuery
|
, _appSettings', _appStatic, _appConnPool, _appSmtpPool, _appLdapPool, _appWidgetMemcached, _appHttpManager, _appLogger, _appLogSettings, _appCryptoIDKey, _appClusterID, _appInstanceID, _appJobState, _appSessionStore, _appSecretBoxKey, _appJSONWebKeySet, _appHealthReport, _appMemcached, _appUploadCache, _appVerpSecret, _appAuthKey, _appPersonalisedSheetFilesSeedKey, _appVolatileClusterSettingsCache, _appAvsQuery
|
||||||
, DB, Form, MsgRenderer, MailM, DBFile
|
, DB, DBRead, Form, MsgRenderer, MailM, DBFile
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Import.NoFoundation
|
import Import.NoFoundation
|
||||||
@ -43,7 +43,7 @@ import Data.Time.Clock.POSIX (POSIXTime)
|
|||||||
import GHC.Fingerprint (Fingerprint)
|
import GHC.Fingerprint (Fingerprint)
|
||||||
import Handler.Sheet.PersonalisedFiles.Types (PersonalisedSheetFilesSeedKey)
|
import Handler.Sheet.PersonalisedFiles.Types (PersonalisedSheetFilesSeedKey)
|
||||||
|
|
||||||
import Utils.Avs (AvsQuery)
|
import Utils.Avs (AvsQuery())
|
||||||
|
|
||||||
|
|
||||||
type SMTPPool = Pool SMTPConnection
|
type SMTPPool = Pool SMTPConnection
|
||||||
@ -123,8 +123,9 @@ instance HasCookieSettings RegisteredCookie UniWorX where
|
|||||||
instance (MonadHandler m, HandlerSite m ~ UniWorX) => ReadLogSettings m where
|
instance (MonadHandler m, HandlerSite m ~ UniWorX) => ReadLogSettings m where
|
||||||
readLogSettings = liftIO . readTVarIO =<< getsYesod (view _appLogSettings)
|
readLogSettings = liftIO . readTVarIO =<< getsYesod (view _appLogSettings)
|
||||||
|
|
||||||
|
|
||||||
type DB = YesodDB UniWorX
|
type DB = YesodDB UniWorX
|
||||||
|
type DBRead = ReaderT SqlReadBackend (HandlerFor UniWorX)
|
||||||
type Form x = Html -> MForm (HandlerFor UniWorX) (FormResult x, WidgetFor UniWorX ())
|
type Form x = Html -> MForm (HandlerFor UniWorX) (FormResult x, WidgetFor UniWorX ())
|
||||||
type MsgRenderer = MsgRendererS UniWorX -- see Utils
|
type MsgRenderer = MsgRendererS UniWorX -- see Utils
|
||||||
type MailM a = MailT (HandlerFor UniWorX) a
|
type MailM a = MailT (HandlerFor UniWorX) a
|
||||||
|
|||||||
@ -107,7 +107,7 @@ authenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend
|
|||||||
| not isDummy -> res <$ update uid [ UserLastAuthentication =. Just now ]
|
| not isDummy -> res <$ update uid [ UserLastAuthentication =. Just now ]
|
||||||
_other -> return res
|
_other -> return res
|
||||||
|
|
||||||
$logDebugS "auth" $ tshow Creds{..}
|
$logDebugS "auth" $ tshow Creds{..}
|
||||||
ldapPool' <- getsYesod $ view _appLdapPool
|
ldapPool' <- getsYesod $ view _appLdapPool
|
||||||
|
|
||||||
flip catches excHandlers $ case ldapPool' of
|
flip catches excHandlers $ case ldapPool' of
|
||||||
@ -153,9 +153,9 @@ _upsertCampusUserMode mMode cs@Creds{..}
|
|||||||
|
|
||||||
defaultOther = apHash
|
defaultOther = apHash
|
||||||
|
|
||||||
ldapLookupAndUpsert :: forall m. (MonadHandler m, HandlerSite m ~ UniWorX, MonadMask m, MonadUnliftIO m) => Text -> SqlPersistT m (Entity User)
|
ldapLookupAndUpsert :: forall m. (MonadHandler m, HandlerSite m ~ UniWorX, MonadMask m, MonadUnliftIO m) => Text -> SqlPersistT m (Entity User)
|
||||||
ldapLookupAndUpsert ident =
|
ldapLookupAndUpsert ident =
|
||||||
getsYesod (view _appLdapPool) >>= \case
|
getsYesod (view _appLdapPool) >>= \case
|
||||||
Nothing -> throwM $ CampusUserLdapError $ LdapHostNotResolved "No LDAP configuration in Foundation."
|
Nothing -> throwM $ CampusUserLdapError $ LdapHostNotResolved "No LDAP configuration in Foundation."
|
||||||
Just ldapPool ->
|
Just ldapPool ->
|
||||||
campusUser'' ldapPool campusUserFailoverMode ident >>= \case
|
campusUser'' ldapPool campusUserFailoverMode ident >>= \case
|
||||||
@ -182,22 +182,21 @@ upsertCampusUser upsertMode ldapData = do
|
|||||||
userDefaultConf <- getsYesod $ view _appUserDefaults
|
userDefaultConf <- getsYesod $ view _appUserDefaults
|
||||||
|
|
||||||
(newUser,userUpdate) <- decodeUser now userDefaultConf upsertMode ldapData
|
(newUser,userUpdate) <- decodeUser now userDefaultConf upsertMode ldapData
|
||||||
--TODO: newUser should be associated with a company and company supervisor through Handler.Utils.Company.upsertUserCompany, but this is called by upsertAvsUser already - conflict?
|
|
||||||
|
|
||||||
oldUsers <- for (userLdapPrimaryKey newUser) $ \pKey -> selectKeysList [ UserLdapPrimaryKey ==. Just pKey ] []
|
oldUsers <- for (userLdapPrimaryKey newUser) $ \pKey -> selectKeysList [ UserLdapPrimaryKey ==. Just pKey ] []
|
||||||
|
|
||||||
user@(Entity userId userRec) <- case oldUsers of
|
user@(Entity userId userRec) <- case oldUsers of
|
||||||
Just [oldUserId] -> updateGetEntity oldUserId userUpdate
|
Just [oldUserId] -> updateGetEntity oldUserId userUpdate
|
||||||
_other -> upsertBy (UniqueAuthentication (newUser ^. _userIdent)) newUser userUpdate
|
_other -> upsertBy (UniqueAuthentication (newUser ^. _userIdent)) newUser userUpdate
|
||||||
unless (validDisplayName (newUser ^. _userTitle)
|
unless (validDisplayName (newUser ^. _userTitle)
|
||||||
(newUser ^. _userFirstName)
|
(newUser ^. _userFirstName)
|
||||||
(newUser ^. _userSurname)
|
(newUser ^. _userSurname)
|
||||||
(userRec ^. _userDisplayName)) $
|
(userRec ^. _userDisplayName)) $
|
||||||
update userId [ UserDisplayName =. (newUser ^. _userDisplayName) ]
|
update userId [ UserDisplayName =. (newUser ^. _userDisplayName) ] -- update invalid display names only
|
||||||
when (validEmail' (userRec ^. _userEmail)) $ do
|
when (validEmail' (userRec ^. _userEmail)) $ do -- RECALL: userRec already contains basic updates
|
||||||
let emUps = [ UserDisplayEmail =. (newUser ^. _userEmail) | not (validEmail' (userRec ^. _userDisplayEmail)) ]
|
let emUps = [ UserDisplayEmail =. (newUser ^. _userEmail) | not (validEmail' (userRec ^. _userDisplayEmail)) ]
|
||||||
++ [ UserAuthentication =. AuthLDAP | is _AuthNoLogin (userRec ^. _userAuthentication) ]
|
++ [ UserAuthentication =. AuthLDAP | is _AuthNoLogin (userRec ^. _userAuthentication) ]
|
||||||
unless (null emUps) $ update userId emUps
|
update userId emUps -- update already checks whether list is empty
|
||||||
-- Attempt to update ident, too:
|
-- Attempt to update ident, too:
|
||||||
unless (validEmail' (userRec ^. _userIdent)) $
|
unless (validEmail' (userRec ^. _userIdent)) $
|
||||||
void $ maybeCatchAll (update userId [ UserIdent =. (newUser ^. _userEmail) ] >> return (Just ()))
|
void $ maybeCatchAll (update userId [ UserIdent =. (newUser ^. _userEmail) ] >> return (Just ()))
|
||||||
@ -228,10 +227,10 @@ decodeUserTest mbIdent ldapData = do
|
|||||||
|
|
||||||
|
|
||||||
decodeUser :: (MonadThrow m) => UTCTime -> UserDefaultConf -> UpsertCampusUserMode -> Ldap.AttrList [] -> m (User,_)
|
decodeUser :: (MonadThrow m) => UTCTime -> UserDefaultConf -> UpsertCampusUserMode -> Ldap.AttrList [] -> m (User,_)
|
||||||
decodeUser now UserDefaultConf{..} upsertMode ldapData = do
|
decodeUser now UserDefaultConf{..} upsertMode ldapData = do
|
||||||
let
|
let
|
||||||
userTelephone = decodeLdap ldapUserTelephone
|
userTelephone = decodeLdap ldapUserTelephone <&> canonicalPhone
|
||||||
userMobile = decodeLdap ldapUserMobile
|
userMobile = decodeLdap ldapUserMobile <&> canonicalPhone
|
||||||
userCompanyPersonalNumber = decodeLdap ldapUserFraportPersonalnummer
|
userCompanyPersonalNumber = decodeLdap ldapUserFraportPersonalnummer
|
||||||
userCompanyDepartment = decodeLdap ldapUserFraportAbteilung
|
userCompanyDepartment = decodeLdap ldapUserFraportAbteilung
|
||||||
|
|
||||||
@ -267,7 +266,7 @@ decodeUser now UserDefaultConf{..} upsertMode ldapData = do
|
|||||||
-- -> return $ CI.mk userEmail
|
-- -> return $ CI.mk userEmail
|
||||||
| otherwise
|
| otherwise
|
||||||
-> throwM CampusUserInvalidEmail
|
-> throwM CampusUserInvalidEmail
|
||||||
|
|
||||||
userLdapPrimaryKey <- if
|
userLdapPrimaryKey <- if
|
||||||
| [bs] <- ldapMap !!! ldapPrimaryKey
|
| [bs] <- ldapMap !!! ldapPrimaryKey
|
||||||
, Right userLdapPrimaryKey'' <- Text.decodeUtf8' bs
|
, Right userLdapPrimaryKey'' <- Text.decodeUtf8' bs
|
||||||
@ -306,13 +305,13 @@ decodeUser now UserDefaultConf{..} upsertMode ldapData = do
|
|||||||
, userPrefersPostal = userDefaultPrefersPostal
|
, userPrefersPostal = userDefaultPrefersPostal
|
||||||
, ..
|
, ..
|
||||||
}
|
}
|
||||||
userUpdate =
|
userUpdate =
|
||||||
[ UserLastAuthentication =. Just now | isLogin ] ++
|
[ UserLastAuthentication =. Just now | isLogin ] ++
|
||||||
[ UserEmail =. userEmail | validEmail' userEmail ] ++
|
[ UserEmail =. userEmail | validEmail' userEmail ] ++
|
||||||
[
|
[
|
||||||
-- UserDisplayName =. userDisplayName -- not updated here, since users are allowed to change their DisplayName; see line 272
|
-- UserDisplayName =. userDisplayName -- not updated here, since users are allowed to change their DisplayName; see line 191
|
||||||
UserFirstName =. userFirstName
|
UserFirstName =. userFirstName
|
||||||
, UserSurname =. userSurname
|
, UserSurname =. userSurname
|
||||||
, UserLastLdapSynchronisation =. Just now
|
, UserLastLdapSynchronisation =. Just now
|
||||||
, UserLdapPrimaryKey =. userLdapPrimaryKey
|
, UserLdapPrimaryKey =. userLdapPrimaryKey
|
||||||
, UserMobile =. userMobile
|
, UserMobile =. userMobile
|
||||||
|
|||||||
@ -9,9 +9,9 @@ module Foundation.Yesod.ErrorHandler
|
|||||||
import Import.NoFoundation hiding (errorHandler)
|
import Import.NoFoundation hiding (errorHandler)
|
||||||
|
|
||||||
import Foundation.Type
|
import Foundation.Type
|
||||||
-- import Foundation.I18n
|
import Foundation.I18n
|
||||||
import Foundation.Authorization
|
import Foundation.Authorization
|
||||||
-- import Foundation.SiteLayout
|
import Foundation.SiteLayout
|
||||||
import Foundation.Routes
|
import Foundation.Routes
|
||||||
import Foundation.DB
|
import Foundation.DB
|
||||||
|
|
||||||
@ -20,15 +20,15 @@ import qualified Data.Text as Text
|
|||||||
|
|
||||||
import qualified Network.Wai as W
|
import qualified Network.Wai as W
|
||||||
|
|
||||||
-- import System.Exit -- DEBUG: just for testing
|
import System.Exit -- DEBUG: just for testing
|
||||||
-- import System.Posix.Process -- DEBUG: just for testing
|
import System.Posix.Process -- DEBUG: just for testing
|
||||||
|
|
||||||
errorHandler :: ( MonadSecretBox (HandlerFor UniWorX)
|
errorHandler :: ( MonadSecretBox (HandlerFor UniWorX)
|
||||||
-- , MonadSecretBox (WidgetFor UniWorX)
|
, MonadSecretBox (WidgetFor UniWorX)
|
||||||
, MonadSecretBox (ExceptT EncodedSecretBoxException (HandlerFor UniWorX))
|
, MonadSecretBox (ExceptT EncodedSecretBoxException (HandlerFor UniWorX))
|
||||||
, MonadAuth (HandlerFor UniWorX)
|
, MonadAuth (HandlerFor UniWorX)
|
||||||
, BearerAuthSite UniWorX
|
, BearerAuthSite UniWorX
|
||||||
-- , YesodPersistBackend UniWorX ~ SqlBackend
|
, YesodPersistBackend UniWorX ~ SqlBackend
|
||||||
)
|
)
|
||||||
=> ErrorResponse -> HandlerFor UniWorX TypedContent
|
=> ErrorResponse -> HandlerFor UniWorX TypedContent
|
||||||
errorHandler err = do
|
errorHandler err = do
|
||||||
@ -72,39 +72,39 @@ errorHandler err = do
|
|||||||
setSessionJson SessionError sessErr
|
setSessionJson SessionError sessErr
|
||||||
|
|
||||||
selectRep $ do
|
selectRep $ do
|
||||||
-- provideRep $ do
|
provideRep $ do
|
||||||
-- mr <- getMessageRender
|
mr <- getMessageRender
|
||||||
-- let
|
let
|
||||||
-- encrypted :: Text -> WidgetFor UniWorX () -> WidgetFor UniWorX ()
|
encrypted :: Text -> WidgetFor UniWorX () -> WidgetFor UniWorX ()
|
||||||
-- encrypted plaintextJson plaintext = do
|
encrypted plaintextJson plaintext = do
|
||||||
-- let displayEncrypted ciphertext =
|
let displayEncrypted ciphertext =
|
||||||
-- [whamlet|
|
[whamlet|
|
||||||
-- $newline never
|
$newline never
|
||||||
-- <p>_{MsgErrorResponseEncrypted}
|
<p>_{MsgErrorResponseEncrypted}
|
||||||
-- <pre .literal-error>
|
<pre .literal-error>
|
||||||
-- #{ciphertext}
|
#{ciphertext}
|
||||||
-- |]
|
|]
|
||||||
-- if
|
if
|
||||||
-- | isEncrypted && shouldEncrypt -> displayEncrypted plaintextJson
|
| isEncrypted && shouldEncrypt -> displayEncrypted plaintextJson
|
||||||
-- | shouldEncrypt -> displayEncrypted =<< encodedSecretBox SecretBoxPretty plaintextJson
|
| shouldEncrypt -> displayEncrypted =<< encodedSecretBox SecretBoxPretty plaintextJson
|
||||||
-- | otherwise -> plaintext
|
| otherwise -> plaintext
|
||||||
|
|
||||||
-- errPage = case err of
|
errPage = case err of
|
||||||
-- NotFound -> [whamlet|<p>_{MsgErrorResponseNotFound}|]
|
NotFound -> [whamlet|<p>_{MsgErrorResponseNotFound}|]
|
||||||
-- InternalError err'
|
InternalError err'
|
||||||
-- | "Crash Button" `isPrefixOf` err' -> liftIO $ exitImmediately ExitSuccess -- DEBUG: just for Testing
|
| "Crash Button" `isPrefixOf` err' -> liftIO $ exitImmediately ExitSuccess -- DEBUG: just for Testing
|
||||||
-- | otherwise -> encrypted err' [whamlet|<p .literal-error>#{fromMaybe err' decrypted}|]
|
| otherwise -> encrypted err' [whamlet|<p .literal-error>#{fromMaybe err' decrypted}|]
|
||||||
-- InvalidArgs errs -> [whamlet|
|
InvalidArgs errs -> [whamlet|
|
||||||
-- <ul>
|
<ul>
|
||||||
-- $forall err' <- errs
|
$forall err' <- errs
|
||||||
-- <li .literal-error>
|
<li .literal-error>
|
||||||
-- #{err'}
|
#{err'}
|
||||||
-- |]
|
|]
|
||||||
-- NotAuthenticated -> [whamlet|<p>_{MsgErrorResponseNotAuthenticated}|]
|
NotAuthenticated -> [whamlet|<p>_{MsgErrorResponseNotAuthenticated}|]
|
||||||
-- PermissionDenied err' -> [whamlet|<p .errMsg>#{err'}|]
|
PermissionDenied err' -> [whamlet|<p .errMsg>#{err'}|]
|
||||||
-- BadMethod method -> [whamlet|<p>_{MsgErrorResponseBadMethod (decodeUtf8 method)}|]
|
BadMethod method -> [whamlet|<p>_{MsgErrorResponseBadMethod (decodeUtf8 method)}|]
|
||||||
-- siteLayout (toWgt . mr $ ErrorResponseTitle err) $ do
|
siteLayout (toWgt . mr $ ErrorResponseTitle err) $ do
|
||||||
-- errPage
|
errPage
|
||||||
provideRep $ case err of
|
provideRep $ case err of
|
||||||
PermissionDenied err' -> return err'
|
PermissionDenied err' -> return err'
|
||||||
InternalError err'
|
InternalError err'
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
|
-- SPDX-FileCopyrightText: 2022-24 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
|
||||||
--
|
--
|
||||||
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
@ -8,24 +8,29 @@ module Handler.Admin
|
|||||||
|
|
||||||
import Import
|
import Import
|
||||||
|
|
||||||
import Jobs
|
|
||||||
-- import Data.Either
|
-- import Data.Either
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
import qualified Data.Text as Text
|
||||||
-- import qualified Data.Text.Lazy.Encoding as LBS
|
-- import qualified Data.Text.Lazy.Encoding as LBS
|
||||||
|
|
||||||
-- import qualified Control.Monad.Catch as Catch
|
-- import qualified Control.Monad.Catch as Catch
|
||||||
-- import Servant.Client (ClientError(..), ResponseF(..))
|
-- import Servant.Client (ClientError(..), ResponseF(..))
|
||||||
-- import Text.Blaze.Html (preEscapedToHtml)
|
-- import Text.Blaze.Html (preEscapedToHtml)
|
||||||
|
|
||||||
|
import Database.Persist.Sql (updateWhereCount)
|
||||||
import Database.Esqueleto.Experimental ((:&)(..))
|
import Database.Esqueleto.Experimental ((:&)(..))
|
||||||
import qualified Database.Esqueleto.Experimental as E
|
import qualified Database.Esqueleto.Experimental as E
|
||||||
|
import qualified Database.Esqueleto.Legacy as EL (on) -- needed for dbTable
|
||||||
import qualified Database.Esqueleto.Utils as E
|
import qualified Database.Esqueleto.Utils as E
|
||||||
|
|
||||||
import Handler.Utils.DateTime
|
import Jobs
|
||||||
|
import Handler.Utils
|
||||||
import Handler.Utils.Avs
|
import Handler.Utils.Avs
|
||||||
import Handler.Utils.Widgets
|
|
||||||
import Handler.Utils.Users
|
import Handler.Utils.Users
|
||||||
import Handler.Utils.Qualification
|
-- import Handler.Utils.Company
|
||||||
|
import Handler.Health.Interface
|
||||||
|
import Handler.Users (AllUsersAction(..))
|
||||||
|
|
||||||
import Handler.Admin.Test as Handler.Admin
|
import Handler.Admin.Test as Handler.Admin
|
||||||
import Handler.Admin.ErrorMessage as Handler.Admin
|
import Handler.Admin.ErrorMessage as Handler.Admin
|
||||||
@ -34,45 +39,39 @@ import Handler.Admin.Crontab as Handler.Admin
|
|||||||
import Handler.Admin.Avs as Handler.Admin
|
import Handler.Admin.Avs as Handler.Admin
|
||||||
import Handler.Admin.Ldap as Handler.Admin
|
import Handler.Admin.Ldap as Handler.Admin
|
||||||
|
|
||||||
|
-- avoids repetition of local definitions
|
||||||
|
single :: (k,a) -> Map k a
|
||||||
|
single = uncurry Map.singleton
|
||||||
|
|
||||||
|
|
||||||
|
-- Types and Template Haskell
|
||||||
|
data ProblemTableAction = ProblemTableMarkSolved
|
||||||
|
| ProblemTableMarkUnsolved
|
||||||
|
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic)
|
||||||
|
deriving anyclass (Universe, Finite)
|
||||||
|
|
||||||
|
nullaryPathPiece ''ProblemTableAction $ camelToPathPiece' 2
|
||||||
|
embedRenderMessage ''UniWorX ''ProblemTableAction id
|
||||||
|
|
||||||
|
data ProblemTableActionData = ProblemTableMarkSolvedData
|
||||||
|
| ProblemTableMarkUnsolvedData -- Placeholder, remove later
|
||||||
|
deriving (Eq, Ord, Read, Show, Generic)
|
||||||
|
|
||||||
|
|
||||||
|
-- Handlers
|
||||||
getAdminR :: Handler Html
|
getAdminR :: Handler Html
|
||||||
getAdminR = redirect AdminProblemsR
|
getAdminR = redirect AdminProblemsR
|
||||||
|
|
||||||
getAdminProblemsR :: Handler Html
|
getAdminProblemsR, postAdminProblemsR :: Handler Html
|
||||||
getAdminProblemsR = do
|
getAdminProblemsR = handleAdminProblems Nothing
|
||||||
|
|
||||||
|
handleAdminProblems :: Maybe Widget -> Handler Html
|
||||||
|
handleAdminProblems mbProblemTable = do
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
let nowaday = utctDay now
|
let nowaday = utctDay now
|
||||||
cutOffPrintDays = 7
|
cutOffOldDays = 1
|
||||||
cutOffPrintJob = addLocalDays (-cutOffPrintDays) now
|
cutOffOldTime = toMidnight $ addDays (-cutOffOldDays) nowaday
|
||||||
cutOffAvsSynch = Just $ addUTCTime (-nominalHour) now -- update at most once per hour
|
|
||||||
|
|
||||||
(usersAreReachable, driversHaveAvsIds, rDriversHaveFs, noStalePrintJobs) <- runDB $ (,,,)
|
|
||||||
<$> areAllUsersReachable
|
|
||||||
<*> allDriversHaveAvsId nowaday
|
|
||||||
<*> allRDriversHaveFs nowaday
|
|
||||||
<*> (not <$> exists [PrintJobAcknowledged ==. Nothing, PrintJobCreated <=. cutOffPrintJob])
|
|
||||||
diffLics <- try retrieveDifferingLicences >>= \case
|
|
||||||
-- (Left (UnsupportedContentType "text/html" resp)) -> Left $ text2widget "Html received"
|
|
||||||
(Left e) -> return $ Left $ text2widget $ tshow (e :: SomeException)
|
|
||||||
(Right AvsLicenceDifferences{..}) -> do
|
|
||||||
let problemIds = avsLicenceDiffRevokeAll <> avsLicenceDiffGrantVorfeld <> avsLicenceDiffRevokeRollfeld <> avsLicenceDiffGrantRollfeld
|
|
||||||
-- mapM_ (queueJob' . flip JobSynchroniseAvsId cutOffAvsSynch) problemIds
|
|
||||||
runDBJobs . forM_ problemIds $ queueDBJob . flip JobSynchroniseAvsId cutOffAvsSynch
|
|
||||||
return $ Right
|
|
||||||
( Set.size avsLicenceDiffRevokeAll
|
|
||||||
, Set.size avsLicenceDiffGrantVorfeld
|
|
||||||
, Set.size avsLicenceDiffRevokeRollfeld
|
|
||||||
, Set.size avsLicenceDiffGrantRollfeld
|
|
||||||
)
|
|
||||||
-- Attempt to format results in a nicer way failed, since rendering Html within a modal destroyed the page layout itself
|
|
||||||
-- let procDiffLics (to0, to1, to2) = Right (Set.size to0, Set.size to1, Set.size to2)
|
|
||||||
-- diffLics <- (procDiffLics <$> retrieveDifferingLicences) `catches`
|
|
||||||
-- [ Catch.Handler (\case (UnsupportedContentType "text/html;charset=utf-8" Response{responseBody})
|
|
||||||
-- -> return $ Left $ toWidget $ preEscapedToHtml $ fromRight "Response UTF8-decoding error" $ LBS.decodeUtf8' responseBody
|
|
||||||
-- ex -> return $ Left $ text2widget $ tshow ex)
|
|
||||||
-- , Catch.Handler (\(ex::SomeException) -> return $ Left $ text2widget $ tshow ex)
|
|
||||||
-- ]
|
|
||||||
|
|
||||||
-- we abuse messageTooltip for colored icons here
|
-- we abuse messageTooltip for colored icons here
|
||||||
msgSuccessTooltip <- messageI Success MsgMessageSuccess
|
msgSuccessTooltip <- messageI Success MsgMessageSuccess
|
||||||
msgWarningTooltip <- messageI Warning MsgMessageWarning
|
msgWarningTooltip <- messageI Warning MsgMessageWarning
|
||||||
@ -82,23 +81,97 @@ getAdminProblemsR = do
|
|||||||
flagWarning = messageTooltip . bool msgWarningTooltip msgSuccessTooltip
|
flagWarning = messageTooltip . bool msgWarningTooltip msgSuccessTooltip
|
||||||
flagNonZero :: Int -> Widget
|
flagNonZero :: Int -> Widget
|
||||||
flagNonZero n | n <= 0 = flagError True
|
flagNonZero n | n <= 0 = flagError True
|
||||||
| otherwise = messageTooltip =<< handlerToWidget (messageI Error (MsgProblemsDriverSynch n))
|
| otherwise = messageTooltip =<< handlerToWidget (messageI Error (MsgProblemsDriverSynch n))
|
||||||
|
|
||||||
|
(usersAreReachable, driversHaveAvsIds, rDriversHaveFs, noStalePrintJobs, noBadAPCids, (interfaceOks, interfaceTable)) <- runDB $ (,,,,,)
|
||||||
|
<$> areAllUsersReachable
|
||||||
|
<*> allDriversHaveAvsId now
|
||||||
|
<*> allRDriversHaveFs now
|
||||||
|
<*> (not <$> exists [PrintJobAcknowledged ==. Nothing, PrintJobCreated <. cutOffOldTime])
|
||||||
|
<*> (not <$> exists [PrintAcknowledgeProcessed ==. False])
|
||||||
|
<*> mkInterfaceLogTable mempty
|
||||||
|
let interfacesBadNr = length $ filter (not . snd) interfaceOks
|
||||||
|
-- interfacesOk = all snd interfaceOks
|
||||||
|
|
||||||
|
diffLics <- try retrieveDifferingLicences >>= \case
|
||||||
|
-- (Left (UnsupportedContentType "text/html" resp)) -> Left $ text2widget "Html received"
|
||||||
|
(Left e) -> return $ Left $ text2widget $ tshow (e :: SomeException)
|
||||||
|
(Right (AvsLicenceDifferences{..},_)) -> do
|
||||||
|
let problemIds = avsLicenceDiffRevokeAll <> avsLicenceDiffGrantVorfeld <> avsLicenceDiffRevokeRollfeld <> avsLicenceDiffGrantRollfeld
|
||||||
|
void $ runDB $ queueAvsUpdateByAID problemIds $ Just nowaday
|
||||||
|
return $ Right
|
||||||
|
( Set.size avsLicenceDiffRevokeAll
|
||||||
|
, Set.size avsLicenceDiffGrantVorfeld
|
||||||
|
, Set.size avsLicenceDiffRevokeRollfeld
|
||||||
|
, Set.size avsLicenceDiffGrantRollfeld
|
||||||
|
)
|
||||||
|
-- Attempt to format results in a nicer way failed, since rendering Html within a modal destroyed the page layout itself
|
||||||
|
-- let procDiffLics (to0, to1, to2) = Right (Set.size to0, Set.size to1, Set.size to2)
|
||||||
|
-- diffLics <- (procDiffLics . fst <$> retrieveDifferingLicences) `catches`
|
||||||
|
-- [ Catch.Handler (\case (UnsupportedContentType "text/html;charset=utf-8" Response{responseBody})
|
||||||
|
-- -> return $ Left $ toWidget $ preEscapedToHtml $ fromRight "Response UTF8-decoding error" $ LBS.decodeUtf8' responseBody
|
||||||
|
-- ex -> return $ Left $ text2widget $ tshow ex)
|
||||||
|
-- , Catch.Handler (\(ex::SomeException) -> return $ Left $ text2widget $ tshow ex)
|
||||||
|
-- ]
|
||||||
|
|
||||||
rerouteMail <- getsYesod $ view _appMailRerouteTo
|
rerouteMail <- getsYesod $ view _appMailRerouteTo
|
||||||
|
problemLogTable <- maybeM (snd <$> runDB mkProblemLogTable) return $ return mbProblemTable -- formResult only processed in POST-Handler
|
||||||
|
|
||||||
siteLayoutMsg MsgProblemsHeading $ do
|
siteLayoutMsg MsgProblemsHeading $ do
|
||||||
setTitleI MsgProblemsHeading
|
setTitleI MsgProblemsHeading
|
||||||
$(widgetFile "admin-problems")
|
$(widgetFile "admin-problems")
|
||||||
|
|
||||||
|
postAdminProblemsR = do
|
||||||
|
(problemLogRes, problemLogTable) <- runDB mkProblemLogTable
|
||||||
|
formResult problemLogRes procProblems
|
||||||
|
handleAdminProblems $ Just problemLogTable
|
||||||
|
where
|
||||||
|
procProblems :: (ProblemTableActionData, Set ProblemLogId) -> Handler ()
|
||||||
|
procProblems (ProblemTableMarkSolvedData , pids) = actUpdate True pids
|
||||||
|
procProblems (ProblemTableMarkUnsolvedData, pids) = actUpdate False pids
|
||||||
|
|
||||||
getProblemUnreachableR :: Handler Html
|
actUpdate markdone pids = do
|
||||||
getProblemUnreachableR = do
|
mauid <- maybeAuthId
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
let (pls_fltr,newv,msg) | markdone = (ProblemLogSolved ==. Nothing, Just now, MsgAdminProblemsSolved)
|
||||||
|
| otherwise = (ProblemLogSolved !=. Nothing, Nothing , MsgAdminProblemsReopened)
|
||||||
|
(fromIntegral -> oks) <- runDB $ updateWhereCount [pls_fltr, ProblemLogId <-. toList pids]
|
||||||
|
[ProblemLogSolved =. newv, ProblemLogSolver =. mauid]
|
||||||
|
let no_req = Set.size pids
|
||||||
|
mkind = if oks < no_req || no_req <= 0 then Warning else Success
|
||||||
|
addMessageI mkind $ msg oks
|
||||||
|
when (oks > 0) $ reloadKeepGetParams AdminProblemsR -- reload to update all tables
|
||||||
|
|
||||||
|
getProblemUnreachableR, postProblemUnreachableR :: Handler Html
|
||||||
|
getProblemUnreachableR = postProblemUnreachableR
|
||||||
|
postProblemUnreachableR = do
|
||||||
unreachables <- runDB retrieveUnreachableUsers
|
unreachables <- runDB retrieveUnreachableUsers
|
||||||
|
|
||||||
|
-- the following form is a nearly identicaly copy from Handler.Users:
|
||||||
|
((noreachUsersRes, noreachUsersWgt'), noreachUsersEnctype) <- runFormPost . identifyForm FIDUnreachableUsersAction $ buttonForm
|
||||||
|
let noreachUsersWgt = wrapForm noreachUsersWgt' def
|
||||||
|
{ formSubmit = FormNoSubmit
|
||||||
|
, formAction = Just $ SomeRoute ProblemUnreachableR
|
||||||
|
, formEncoding = noreachUsersEnctype
|
||||||
|
}
|
||||||
|
formResult noreachUsersRes $ \case
|
||||||
|
AllUsersLdapSync -> do
|
||||||
|
forM_ unreachables $ \Entity{entityKey=uid} -> void . queueJob $ JobSynchroniseLdapUser uid
|
||||||
|
addMessageI Success . MsgSynchroniseLdapUserQueued $ length unreachables
|
||||||
|
redirect ProblemUnreachableR
|
||||||
|
AllUsersAvsSync -> do
|
||||||
|
n <- runDB $ queueAvsUpdateByUID (entityKey <$> unreachables) Nothing
|
||||||
|
addMessageI Success . MsgSynchroniseAvsUserQueued $ fromIntegral n
|
||||||
|
redirect ProblemUnreachableR
|
||||||
|
|
||||||
siteLayoutMsg MsgProblemsUnreachableHeading $ do
|
siteLayoutMsg MsgProblemsUnreachableHeading $ do
|
||||||
setTitleI MsgProblemsUnreachableHeading
|
setTitleI MsgProblemsUnreachableHeading
|
||||||
[whamlet|
|
[whamlet|
|
||||||
<section>
|
<section>
|
||||||
#{length unreachables} _{MsgProblemsUnreachableBody}
|
<h3>_{MsgProblemsUnreachableButtons}
|
||||||
|
^{noreachUsersWgt}
|
||||||
|
<section>
|
||||||
|
#{length unreachables} _{MsgProblemsUnreachableBody}
|
||||||
<ul>
|
<ul>
|
||||||
$forall usr <- unreachables
|
$forall usr <- unreachables
|
||||||
<li>
|
<li>
|
||||||
@ -106,10 +179,9 @@ getProblemUnreachableR = do
|
|||||||
|]
|
|]
|
||||||
|
|
||||||
getProblemFbutNoR :: Handler Html
|
getProblemFbutNoR :: Handler Html
|
||||||
getProblemFbutNoR = do
|
getProblemFbutNoR = do
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
let nowaday = utctDay now
|
rnofs <- runDB $ E.select $ retrieveDriversRWithoutF now
|
||||||
rnofs <- runDB $ E.select $ retrieveDriversRWithoutF nowaday
|
|
||||||
siteLayoutMsg MsgProblemsRWithoutFHeading $ do
|
siteLayoutMsg MsgProblemsRWithoutFHeading $ do
|
||||||
setTitleI MsgProblemsRWithoutFHeading
|
setTitleI MsgProblemsRWithoutFHeading
|
||||||
[whamlet|
|
[whamlet|
|
||||||
@ -122,10 +194,9 @@ getProblemFbutNoR = do
|
|||||||
|]
|
|]
|
||||||
|
|
||||||
getProblemWithoutAvsId :: Handler Html
|
getProblemWithoutAvsId :: Handler Html
|
||||||
getProblemWithoutAvsId = do
|
getProblemWithoutAvsId = do
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
let nowaday = utctDay now
|
rnofs <- runDB $ E.select $ retrieveDriversWithoutAvsId now
|
||||||
rnofs <- runDB $ E.select $ retrieveDriversWithoutAvsId nowaday
|
|
||||||
siteLayoutMsg MsgProblemsNoAvsIdHeading $ do
|
siteLayoutMsg MsgProblemsNoAvsIdHeading $ do
|
||||||
setTitleI MsgProblemsNoAvsIdHeading
|
setTitleI MsgProblemsNoAvsIdHeading
|
||||||
[whamlet|
|
[whamlet|
|
||||||
@ -139,59 +210,66 @@ getProblemWithoutAvsId = do
|
|||||||
|
|
||||||
{-
|
{-
|
||||||
mkUnreachableUsersTable = do
|
mkUnreachableUsersTable = do
|
||||||
let dbtSQLQuery user -> do
|
let dbtSQLQuery user -> do
|
||||||
E.where_ $ E.isNothing (user E.^. UserPostAddress)
|
E.where_ $ E.isNothing (user E.^. UserPostAddress)
|
||||||
E.&&. E.not_ ((user E.^. UserEmail) `E.like` E.val "%@%.%")
|
E.&&. E.not_ ((user E.^. UserEmail) `E.like` E.val "%@%.%")
|
||||||
pure user
|
pure user
|
||||||
dbtRowKey = (E.^. UserId)
|
dbtRowKey = (E.^. UserId)
|
||||||
dbtProj = dbtProjId
|
dbtProj = dbtProjId
|
||||||
dbtColonnade =
|
dbtColonnade =
|
||||||
-}
|
-}
|
||||||
|
|
||||||
areAllUsersReachable :: DB Bool
|
areAllUsersReachable :: DB Bool
|
||||||
-- areAllUsersReachable = isNothing <$> E.selectOne retrieveUnreachableUsers'
|
-- areAllUsersReachable = E.selectNotExists retrieveUnreachableUsers' -- works and would be more efficient, but we cannot check proper email validity within DB alone
|
||||||
-- areAllUsersReachable = E.selectNotExists retrieveUnreachableUsers'
|
|
||||||
areAllUsersReachable = null <$> retrieveUnreachableUsers
|
areAllUsersReachable = null <$> retrieveUnreachableUsers
|
||||||
|
|
||||||
-- retrieveUnreachableUsers' :: E.SqlQuery (E.SqlExpr (Entity User))
|
-- retrieveUnreachableUsers' :: E.SqlQuery (E.SqlExpr (Entity User))
|
||||||
-- retrieveUnreachableUsers' = do
|
-- retrieveUnreachableUsers' = do
|
||||||
-- user <- E.from $ E.table @User
|
-- user <- E.from $ E.table @User
|
||||||
-- E.where_ $ E.isNothing (user E.^. UserPostAddress)
|
-- E.where_ $ E.isNothing (user E.^. UserPostAddress)
|
||||||
-- E.&&. (E.isNothing (user E.^. UserCompanyDepartment) E.||. user E.^. UserCompanyPersonalNumber `E.ilike` E.justVal "E%")
|
-- E.&&. (E.isNothing (user E.^. UserCompanyDepartment) E.||. user E.^. UserCompanyPersonalNumber `E.ilike` E.justVal "E%")
|
||||||
-- E.&&. E.not_ ((user E.^. UserDisplayEmail) `E.like` E.val "%@%.%")
|
-- E.&&. E.not_ ((user E.^. UserDisplayEmail) `E.like` E.val "%@%.%")
|
||||||
-- E.&&. E.not_ ((user E.^. UserEmail) `E.like` E.val "%@%.%")
|
-- E.&&. E.not_ ((user E.^. UserEmail) `E.like` E.val "%@%.%")
|
||||||
-- return user
|
-- return user
|
||||||
|
|
||||||
retrieveUnreachableUsers :: DB [Entity User]
|
retrieveUnreachableUsers :: DB [Entity User]
|
||||||
retrieveUnreachableUsers = do
|
retrieveUnreachableUsers = do
|
||||||
emailOnlyUsers <- E.select $ do
|
emailOnlyUsers <- E.select $ do
|
||||||
user <- E.from $ E.table @User
|
user <- E.from $ E.table @User
|
||||||
E.where_ $ E.isNothing (user E.^. UserPostAddress)
|
E.where_ $ E.isNothing (user E.^. UserPostAddress)
|
||||||
E.&&. (E.isNothing (user E.^. UserCompanyDepartment) E.||. user E.^. UserCompanyPersonalNumber `E.ilike` E.justVal "E%")
|
E.&&. (E.isNothing (user E.^. UserCompanyDepartment) E.||. user E.^. UserCompanyPersonalNumber `E.ilike` E.justVal "E%")
|
||||||
|
E.&&. E.notExists (do
|
||||||
|
(cmp :& usrCmp) <- E.from $ E.table @Company `E.innerJoin` E.table @UserCompany
|
||||||
|
`E.on` (\(cmp :& usrCmp) -> cmp E.^. CompanyId E.==. usrCmp E.^. UserCompanyCompany)
|
||||||
|
E.where_ $ user E.^. UserId E.==. usrCmp E.^. UserCompanyUser
|
||||||
|
E.&&. usrCmp E.^. UserCompanyUseCompanyAddress
|
||||||
|
E.&&. E.isJust (cmp E.^. CompanyPostAddress)
|
||||||
|
)
|
||||||
return user
|
return user
|
||||||
return $ filter hasInvalidEmail emailOnlyUsers
|
filterM hasInvalidEmail emailOnlyUsers
|
||||||
where
|
-- filterM hasInvalifPostal -- probably not worth it, since Utils.Postal.validPostAddress is pretty weak anyway
|
||||||
hasInvalidEmail = isNothing . getEmailAddress . entityVal
|
where
|
||||||
|
hasInvalidEmail = fmap isNothing . getUserEmail
|
||||||
|
|
||||||
allDriversHaveAvsId :: Day -> DB Bool
|
|
||||||
|
allDriversHaveAvsId :: UTCTime -> DB Bool
|
||||||
-- allDriversHaveAvsId = fmap isNothing . E.selectOne . retrieveDriversWithoutAvsId
|
-- allDriversHaveAvsId = fmap isNothing . E.selectOne . retrieveDriversWithoutAvsId
|
||||||
allDriversHaveAvsId = E.selectNotExists . retrieveDriversWithoutAvsId
|
allDriversHaveAvsId = E.selectNotExists . retrieveDriversWithoutAvsId
|
||||||
|
|
||||||
{-
|
{-
|
||||||
-- | Returns users more than once if they own multiple avs-related valid licences, but no AvsID is known
|
-- | Returns users more than once if they own multiple avs-related valid licences, but no AvsID is known
|
||||||
retrieveDriversWithoutAvsId' :: Day -> E.SqlQuery (E.SqlExpr (Entity User))
|
retrieveDriversWithoutAvsId' :: Day -> E.SqlQuery (E.SqlExpr (Entity User))
|
||||||
retrieveDriversWithoutAvsId' nowaday = do
|
retrieveDriversWithoutAvsId' nowaday = do
|
||||||
(usr :& qualUsr :& qual) <- E.from $ E.table @User
|
(usr :& qualUsr :& qual) <- E.from $ E.table @User
|
||||||
`E.innerJoin` E.table @QualificationUser
|
`E.innerJoin` E.table @QualificationUser
|
||||||
`E.on` (\(usr :& qualUsr) -> usr E.^. UserId E.==. qualUsr E.^. QualificationUserUser)
|
`E.on` (\(usr :& qualUsr) -> usr E.^. UserId E.==. qualUsr E.^. QualificationUserUser)
|
||||||
`E.innerJoin` E.table @Qualification
|
`E.innerJoin` E.table @Qualification
|
||||||
`E.on` (\(_usr :& qualUsr :& qual) -> qual E.^. QualificationId E.==. qualUsr E.^. QualificationUserQualification)
|
`E.on` (\(_usr :& qualUsr :& qual) -> qual E.^. QualificationId E.==. qualUsr E.^. QualificationUserQualification)
|
||||||
E.where_ $ -- is avs licence
|
E.where_ $ -- is avs licence
|
||||||
E.isJust (qual E.^. QualificationAvsLicence)
|
E.isJust (qual E.^. QualificationAvsLicence)
|
||||||
E.&&. (qualUsr & validQualification nowaday)
|
E.&&. (qualUsr & validQualification nowaday)
|
||||||
E.&&. -- AvsId is unknown
|
E.&&. -- AvsId is unknown
|
||||||
E.notExists (do
|
E.notExists (do
|
||||||
avsUsr <- E.from $ E.table @UserAvs
|
avsUsr <- E.from $ E.table @UserAvs
|
||||||
E.where_ $ avsUsr E.^. UserAvsUser E.==. usr E.^. UserId
|
E.where_ $ avsUsr E.^. UserAvsUser E.==. usr E.^. UserId
|
||||||
)
|
)
|
||||||
@ -199,21 +277,21 @@ retrieveDriversWithoutAvsId' nowaday = do
|
|||||||
-}
|
-}
|
||||||
|
|
||||||
-- | Returns users at most once, even if they own multiple avs-related licences, but no AvsID is known
|
-- | Returns users at most once, even if they own multiple avs-related licences, but no AvsID is known
|
||||||
retrieveDriversWithoutAvsId :: Day -> E.SqlQuery (E.SqlExpr (Entity User))
|
retrieveDriversWithoutAvsId :: UTCTime -> E.SqlQuery (E.SqlExpr (Entity User))
|
||||||
retrieveDriversWithoutAvsId nowaday = do
|
retrieveDriversWithoutAvsId now = do
|
||||||
usr <- E.from $ E.table @User
|
usr <- E.from $ E.table @User
|
||||||
E.where_ $
|
E.where_ $
|
||||||
E.exists (do -- a valid avs licence
|
E.exists (do -- a valid avs licence
|
||||||
(qual :& qualUsr) <- E.from (E.table @Qualification
|
(qual :& qualUsr) <- E.from (E.table @Qualification
|
||||||
`E.innerJoin` E.table @QualificationUser
|
`E.innerJoin` E.table @QualificationUser
|
||||||
`E.on` (\(qual :& qualUsr) -> qual E.^. QualificationId E.==. qualUsr E.^. QualificationUserQualification))
|
`E.on` (\(qual :& qualUsr) -> qual E.^. QualificationId E.==. qualUsr E.^. QualificationUserQualification))
|
||||||
E.where_ $ -- is avs licence
|
E.where_ $ -- is avs licence
|
||||||
E.isJust (qual E.^. QualificationAvsLicence)
|
E.isJust (qual E.^. QualificationAvsLicence)
|
||||||
E.&&. (qualUsr & validQualification nowaday) -- currently valid
|
E.&&. (qualUsr & validQualification now) -- currently valid
|
||||||
E.&&. -- matches user
|
E.&&. -- matches user
|
||||||
(qualUsr E.^. QualificationUserUser E.==. usr E.^. UserId)
|
(qualUsr E.^. QualificationUserUser E.==. usr E.^. UserId)
|
||||||
)
|
)
|
||||||
E.&&.
|
E.&&.
|
||||||
E.notExists (do -- a known AvsId
|
E.notExists (do -- a known AvsId
|
||||||
avsUsr <- E.from $ E.table @UserAvs
|
avsUsr <- E.from $ E.table @UserAvs
|
||||||
E.where_ $ avsUsr E.^. UserAvsUser E.==. usr E.^. UserId
|
E.where_ $ avsUsr E.^. UserAvsUser E.==. usr E.^. UserId
|
||||||
@ -221,22 +299,134 @@ retrieveDriversWithoutAvsId nowaday = do
|
|||||||
return usr
|
return usr
|
||||||
|
|
||||||
|
|
||||||
allRDriversHaveFs :: Day -> DB Bool
|
allRDriversHaveFs :: UTCTime -> DB Bool
|
||||||
-- allRDriversHaveFs = fmap isNothing . E.selectOne . retrieveDriversRWithoutF
|
-- allRDriversHaveFs = fmap isNothing . E.selectOne . retrieveDriversRWithoutF
|
||||||
allRDriversHaveFs = E.selectNotExists . retrieveDriversRWithoutF
|
allRDriversHaveFs = E.selectNotExists . retrieveDriversRWithoutF
|
||||||
|
|
||||||
-- | Returns users at most once, even if they own multiple avs-related licences, but no AvsID is known
|
-- | Returns users at most once, even if they own multiple avs-related licences, but no AvsID is known
|
||||||
retrieveDriversRWithoutF :: Day -> E.SqlQuery (E.SqlExpr (Entity User))
|
retrieveDriversRWithoutF :: UTCTime -> E.SqlQuery (E.SqlExpr (Entity User))
|
||||||
retrieveDriversRWithoutF nowaday = do
|
retrieveDriversRWithoutF now = do
|
||||||
usr <- E.from $ E.table @User
|
usr <- E.from $ E.table @User
|
||||||
let hasValidQual lic = do
|
let hasValidQual lic = do
|
||||||
(qual :& qualUsr) <- E.from (E.table @Qualification
|
(qual :& qualUsr) <- E.from (E.table @Qualification
|
||||||
`E.innerJoin` E.table @QualificationUser
|
`E.innerJoin` E.table @QualificationUser
|
||||||
`E.on` (\(qual :& qualUsr) -> qual E.^. QualificationId E.==. qualUsr E.^. QualificationUserQualification))
|
`E.on` (\(qual :& qualUsr) -> qual E.^. QualificationId E.==. qualUsr E.^. QualificationUserQualification))
|
||||||
E.where_ $ (qual E.^. QualificationAvsLicence E.==. E.justVal lic) -- matches licence
|
E.where_ $ (qual E.^. QualificationAvsLicence E.==. E.justVal lic) -- matches licence
|
||||||
E.&&. (qualUsr E.^. QualificationUserUser E.==. usr E.^. UserId) -- matches user
|
E.&&. (qualUsr E.^. QualificationUserUser E.==. usr E.^. UserId) -- matches user
|
||||||
E.&&. (qualUsr & validQualification nowaday) -- currently valid
|
E.&&. (qualUsr & validQualification now) -- currently valid
|
||||||
E.where_ $ E.exists (hasValidQual AvsLicenceRollfeld)
|
E.where_ $ E.exists (hasValidQual AvsLicenceRollfeld)
|
||||||
E.&&. E.notExists (hasValidQual AvsLicenceVorfeld)
|
E.&&. E.notExists (hasValidQual AvsLicenceVorfeld)
|
||||||
return usr
|
return usr
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type ProblemLogTableExpr = E.SqlExpr (Entity ProblemLog) `E.LeftOuterJoin` E.SqlExpr (Maybe (Entity User)) `E.LeftOuterJoin` E.SqlExpr (Maybe (Entity User))
|
||||||
|
queryProblem :: ProblemLogTableExpr -> E.SqlExpr (Entity ProblemLog)
|
||||||
|
queryProblem = $(E.sqlLOJproj 3 1)
|
||||||
|
|
||||||
|
querySolver :: ProblemLogTableExpr -> E.SqlExpr (Maybe (Entity User))
|
||||||
|
querySolver = $(E.sqlLOJproj 3 2)
|
||||||
|
|
||||||
|
queryUser :: ProblemLogTableExpr -> E.SqlExpr (Maybe (Entity User))
|
||||||
|
queryUser = $(E.sqlLOJproj 3 3)
|
||||||
|
|
||||||
|
type ProblemLogTableData = DBRow (Entity ProblemLog, Maybe (Entity User), Maybe (Entity User))
|
||||||
|
resultProblem :: Lens' ProblemLogTableData (Entity ProblemLog)
|
||||||
|
resultProblem = _dbrOutput . _1
|
||||||
|
|
||||||
|
resultSolver :: Traversal' ProblemLogTableData (Entity User)
|
||||||
|
resultSolver = _dbrOutput . _2 . _Just
|
||||||
|
|
||||||
|
resultUser :: Traversal' ProblemLogTableData (Entity User)
|
||||||
|
resultUser = _dbrOutput . _3 . _Just
|
||||||
|
|
||||||
|
mkProblemLogTable :: DB (FormResult (ProblemTableActionData, Set ProblemLogId), Widget)
|
||||||
|
mkProblemLogTable = do
|
||||||
|
-- problem_types <- E.select $ do
|
||||||
|
-- ap <- E.from $ E.table @ProblemLog
|
||||||
|
-- let res = ap E.^. ProblemLogInfo E.->>. "problem"
|
||||||
|
-- E.groupBy res
|
||||||
|
-- return res
|
||||||
|
over _1 postprocess <$> dbTable validator DBTable{..}
|
||||||
|
where
|
||||||
|
-- TODO: query to collect all occurring problem types to use as tooltip for the problem filter, so that these don't run out of synch
|
||||||
|
dbtIdent = "problem-log" :: Text
|
||||||
|
dbtSQLQuery = \(problem `E.LeftOuterJoin` solver `E.LeftOuterJoin` usr) -> do
|
||||||
|
-- EL.on (usr E.?. UserId E.==. E.text2num (problem E.^. ProblemLogInfo E.->>. "user")) -- works
|
||||||
|
EL.on (usr E.?. UserId E.==. problem E.^. ProblemLogInfo E.->>>. "user")
|
||||||
|
EL.on (solver E.?. UserId E.==. problem E.^. ProblemLogSolver)
|
||||||
|
return (problem, solver, usr)
|
||||||
|
dbtRowKey = queryProblem >>> (E.^. ProblemLogId)
|
||||||
|
dbtProj = dbtProjFilteredPostId
|
||||||
|
dbtColonnade = formColonnade $ mconcat
|
||||||
|
[ dbSelect (applying _2) id $ return . view (resultProblem . _entityKey)
|
||||||
|
, sortable (Just "time") (i18nCell MsgAdminProblemCreated) $ \( view $ resultProblem . _entityVal . _problemLogTime -> t) -> dateTimeCell t
|
||||||
|
, sortable (Just "info") (i18nCell MsgAdminProblemInfo) $ \( view $ resultProblem . _entityVal . _problemLogAdminProblem -> p) -> adminProblemCell p
|
||||||
|
-- , sortable (Just "firm") (i18nCell MsgTableCompany) $ \(preview $ resultProblem . _entityVal . _problemLogAdminProblem . _adminProblemCompany -> c) -> cellMaybe companyIdCell c
|
||||||
|
, sortable (Just "firm") (i18nCell MsgTableCompany) $ \( view $ resultProblem . _entityVal . _problemLogAdminProblem -> p) -> cellMaybe companyIdCell $ join (p ^? _adminProblemCompanyOld) <|> (p ^? _adminProblemCompany)
|
||||||
|
, sortable (Just "user") (i18nCell MsgAdminProblemUser) $ \(preview resultUser -> u) -> maybeCell u $ cellHasUserLink AdminUserR
|
||||||
|
, sortable (Just "solved") (i18nCell MsgAdminProblemSolved) $ \( view $ resultProblem . _entityVal . _problemLogSolved -> t) -> cellMaybe dateTimeCell t
|
||||||
|
, sortable (Just "solver") (i18nCell MsgAdminProblemSolver) $ \(preview resultSolver -> u) -> maybeCell u $ cellHasUserLink AdminUserR
|
||||||
|
]
|
||||||
|
dbtSorting = mconcat
|
||||||
|
[ single ("time" , SortColumn $ queryProblem >>> (E.^. ProblemLogTime))
|
||||||
|
, single ("info" , SortColumn $ queryProblem >>> (E.^. ProblemLogInfo))
|
||||||
|
-- , single ("firm" , SortColumn ((E.->>. "company" ).(queryProblem >>> (E.^. ProblemLogInfo))))
|
||||||
|
, single ("firm" , SortColumn $ \r -> queryProblem r E.^. ProblemLogInfo E.->>. "company")
|
||||||
|
, single ("user" , sortUserNameBareM queryUser)
|
||||||
|
, single ("solved", SortColumn $ queryProblem >>> (E.^. ProblemLogSolved))
|
||||||
|
, single ("solver", sortUserNameBareM querySolver)
|
||||||
|
]
|
||||||
|
dbtFilter = mconcat
|
||||||
|
[ single ("user" , FilterColumn . E.mkContainsFilterWithCommaPlus Just $ views (to queryUser) (E.?. UserDisplayName))
|
||||||
|
, single ("solver" , FilterColumn . E.mkContainsFilterWithCommaPlus Just $ views (to querySolver) (E.?. UserDisplayName))
|
||||||
|
, single ("company" , FilterColumn . E.mkContainsFilter $ views (to queryProblem) ((E.->>. "company").(E.^. ProblemLogInfo)))
|
||||||
|
, single ("solved" , FilterColumn . E.mkExactFilterLast $ views (to queryProblem) (E.isJust . (E.^. ProblemLogSolved)))
|
||||||
|
-- , single ("problem" , FilterColumn . E.mkContainsFilter $ views (to queryProblem) ((E.->>. "problem").(E.^. ProblemLogInfo))) -- not stored in plaintext!
|
||||||
|
, single ("problem" , mkFilterProjectedPost $ \(getLast -> criterion) dbr -> -- falls es nicht schnell genug ist: in dbtProj den Anzeigetext nur einmal berechnen
|
||||||
|
ifNothingM criterion True $ \(crit::Text) -> do
|
||||||
|
let problem = dbr ^. resultProblem . _entityVal . _problemLogAdminProblem
|
||||||
|
protxt <- adminProblem2Text problem
|
||||||
|
return $ crit `Text.isInfixOf` protxt
|
||||||
|
)
|
||||||
|
]
|
||||||
|
dbtFilterUI mPrev = mconcat
|
||||||
|
[ prismAForm (singletonFilter "user" . maybePrism _PathPiece) mPrev $ aopt (hoistField lift textField) (fslI MsgAdminProblemUser & setTooltip MsgTableFilterCommaPlus)
|
||||||
|
, prismAForm (singletonFilter "solver" . maybePrism _PathPiece) mPrev $ aopt (hoistField lift textField) (fslI MsgAdminProblemSolver & setTooltip MsgTableFilterCommaPlusShort)
|
||||||
|
, prismAForm (singletonFilter "problem" . maybePrism _PathPiece) mPrev $ aopt (hoistField lift textField) (fslI MsgAdminProblemInfo)
|
||||||
|
, prismAForm (singletonFilter "company" . maybePrism _PathPiece) mPrev $ aopt (hoistField lift textField) (fslI MsgTableCompanyShort)
|
||||||
|
, prismAForm (singletonFilter "solved" . maybePrism _PathPiece) mPrev $ aopt (boolField . Just $ SomeMessage MsgBoolIrrelevant) (fslI MsgAdminProblemSolved)
|
||||||
|
]
|
||||||
|
acts :: Map ProblemTableAction (AForm Handler ProblemTableActionData)
|
||||||
|
acts = mconcat
|
||||||
|
[ singletonMap ProblemTableMarkSolved $ pure ProblemTableMarkSolvedData
|
||||||
|
, singletonMap ProblemTableMarkUnsolved $ pure ProblemTableMarkUnsolvedData
|
||||||
|
]
|
||||||
|
dbtParams = DBParamsForm
|
||||||
|
{ dbParamsFormMethod = POST
|
||||||
|
, dbParamsFormAction = Nothing
|
||||||
|
, dbParamsFormAttrs = []
|
||||||
|
, dbParamsFormSubmit = FormSubmit
|
||||||
|
, dbParamsFormAdditional
|
||||||
|
= renderAForm FormStandard
|
||||||
|
$ (, mempty) . First . Just
|
||||||
|
<$> multiActionA acts (fslI MsgTableAction) (Just ProblemTableMarkSolved)
|
||||||
|
, dbParamsFormEvaluate = liftHandler . runFormPost
|
||||||
|
, dbParamsFormResult = id
|
||||||
|
, dbParamsFormIdent = def
|
||||||
|
}
|
||||||
|
dbtCsvEncode = noCsvEncode
|
||||||
|
dbtCsvDecode = Nothing
|
||||||
|
dbtExtraReps = []
|
||||||
|
dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout }
|
||||||
|
validator = def & defaultSorting [SortAscBy "time"]
|
||||||
|
& defaultFilter (singletonMap "solved" [toPathPiece False])
|
||||||
|
postprocess :: FormResult (First ProblemTableActionData, DBFormResult ProblemLogId Bool ProblemLogTableData)
|
||||||
|
-> FormResult ( ProblemTableActionData, Set ProblemLogId)
|
||||||
|
postprocess inp = do
|
||||||
|
(First (Just act), usrMap) <- inp
|
||||||
|
let usrSet = Map.keysSet . Map.filter id $ getDBFormResult (const False) usrMap
|
||||||
|
return (act, usrSet)
|
||||||
|
|
||||||
|
-- adminProblemCell :: IsDBTable m a => AdminProblem -> DBCell m a -- moved to Handler.Utils
|
||||||
|
-- msgAdminProblem :: AdminProblem -> DB (SomeMessages UniWorX) -- moved to Handler.Utils
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -6,23 +6,38 @@
|
|||||||
|
|
||||||
module Handler.Admin.Crontab
|
module Handler.Admin.Crontab
|
||||||
( getAdminCrontabR
|
( getAdminCrontabR
|
||||||
|
, getAdminJobsR
|
||||||
|
, postAdminJobsR
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Import
|
import Import
|
||||||
import Jobs
|
import Jobs
|
||||||
import Handler.Utils.DateTime
|
import Handler.Utils
|
||||||
|
|
||||||
import qualified Data.Aeson.Encode.Pretty as Pretty
|
-- import Data.Aeson (fromJSON)
|
||||||
import Data.Aeson.Encode.Pretty (encodePrettyToTextBuilder')
|
-- import qualified Data.Aeson as Aeson
|
||||||
|
-- import qualified Data.Aeson.Types as Aeson
|
||||||
|
import qualified Data.Aeson.Encode.Pretty as Pretty
|
||||||
|
|
||||||
|
-- import qualified Data.CaseInsensitive as CI
|
||||||
import qualified Data.Text as Text
|
import qualified Data.Text as Text
|
||||||
import qualified Data.Text.Lazy.Builder as Text.Builder
|
import qualified Data.Text.Lazy.Builder as Text.Builder
|
||||||
|
|
||||||
|
import qualified Data.Set as Set
|
||||||
|
import qualified Data.Map as Map
|
||||||
import qualified Data.HashSet as HashSet
|
import qualified Data.HashSet as HashSet
|
||||||
import qualified Data.HashMap.Strict as HashMap
|
import qualified Data.HashMap.Strict as HashMap
|
||||||
|
|
||||||
import qualified Data.UUID as UUID
|
import qualified Data.UUID as UUID
|
||||||
|
|
||||||
|
import Database.Persist.Sql (deleteWhereCount)
|
||||||
|
import qualified Database.Esqueleto.Legacy as E
|
||||||
|
import qualified Database.Esqueleto.Utils as E
|
||||||
|
-- import Database.Esqueleto.Utils.TH
|
||||||
|
|
||||||
|
-- Number of minutes a job must have been locked already to allow forced deletion
|
||||||
|
jobDeleteLockMinutes :: Int
|
||||||
|
jobDeleteLockMinutes = 3
|
||||||
|
|
||||||
deriveJSON defaultOptions
|
deriveJSON defaultOptions
|
||||||
{ constructorTagModifier = camelToPathPiece' 1
|
{ constructorTagModifier = camelToPathPiece' 1
|
||||||
@ -89,10 +104,136 @@ getAdminCrontabR = do
|
|||||||
provideJson mCrontab'
|
provideJson mCrontab'
|
||||||
provideRep . return . Text.Builder.toLazyText $ doEnc mCrontab'
|
provideRep . return . Text.Builder.toLazyText $ doEnc mCrontab'
|
||||||
where
|
where
|
||||||
doEnc :: _ => a -> _
|
doEnc :: ToJSON a => a -> _
|
||||||
doEnc = encodePrettyToTextBuilder' Pretty.defConfig
|
doEnc = Pretty.encodePrettyToTextBuilder' Pretty.defConfig
|
||||||
{ Pretty.confIndent = Pretty.Spaces 2
|
{ Pretty.confIndent = Pretty.Spaces 2
|
||||||
, Pretty.confCompare = comparing $ \t -> ( t `elem` ["instruction", "job", "notification"]
|
, Pretty.confCompare = comparing $ \t -> ( t `elem` ["instruction", "job", "notification"]
|
||||||
, Text.splitOn "-" t
|
, Text.splitOn "-" t
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data JobTableAction = ActJobDelete
|
||||||
|
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic)
|
||||||
|
|
||||||
|
instance Universe JobTableAction
|
||||||
|
instance Finite JobTableAction
|
||||||
|
nullaryPathPiece ''JobTableAction $ camelToPathPiece' 1
|
||||||
|
embedRenderMessage ''UniWorX ''JobTableAction id
|
||||||
|
|
||||||
|
newtype JobTableActionData = ActJobDeleteData
|
||||||
|
{ jobDeleteLocked :: Bool
|
||||||
|
}
|
||||||
|
deriving (Eq, Ord, Read, Show, Generic)
|
||||||
|
|
||||||
|
|
||||||
|
getAdminJobsR, postAdminJobsR :: Handler Html
|
||||||
|
getAdminJobsR = postAdminJobsR
|
||||||
|
postAdminJobsR = do
|
||||||
|
let
|
||||||
|
jobsDBTable = DBTable{..}
|
||||||
|
where
|
||||||
|
resultJob :: Lens' (DBRow (Entity QueuedJob)) (Entity QueuedJob)
|
||||||
|
resultJob = _dbrOutput
|
||||||
|
|
||||||
|
dbtIdent :: Text
|
||||||
|
dbtIdent = "queued-jobs"
|
||||||
|
|
||||||
|
dbtSQLQuery = return
|
||||||
|
dbtRowKey = (E.^. QueuedJobId)
|
||||||
|
dbtProj = dbtProjId
|
||||||
|
dbtColonnade = mconcat
|
||||||
|
[ dbSelect (applying _2) id (return . view (resultJob . _entityKey))
|
||||||
|
, sortable (Just "job") (i18nCell MsgTableJob) $ \(view $ resultJob . _entityVal -> QueuedJob{..}) -> cellMaybe textCell $ getJobName queuedJobContent
|
||||||
|
, sortable (Just "creation-time") (i18nCell MsgTableCreationTime) $ \(view $ resultJob . _entityVal -> QueuedJob{..}) -> dateTimeCell queuedJobCreationTime
|
||||||
|
, sortable (Just "content") (i18nCell MsgTableJobContent) $ \(view $ resultJob . _entityVal -> QueuedJob{..}) -> cell [whamlet|#{doEnc queuedJobContent}|] & addCellClass ("json"::Text)
|
||||||
|
, sortable (Just "lock-time") (i18nCell MsgTableJobLockTime) $ \(view $ resultJob . _entityVal -> QueuedJob{..}) -> cellMaybe dateTimeCell queuedJobLockTime
|
||||||
|
, sortable (Just "lock-instance") (i18nCell MsgTableJobLockInstance) $ \(view $ resultJob . _entityVal -> QueuedJob{..}) -> cellMaybe (stringCell . show) queuedJobLockInstance
|
||||||
|
, sortable (Just "creation-instance") (i18nCell MsgTableJobCreationInstance) $ \(view $ resultJob . _entityVal -> QueuedJob{..}) -> stringCell $ show queuedJobCreationInstance
|
||||||
|
]
|
||||||
|
dbtSorting = Map.fromList
|
||||||
|
[ ("creation-time" , SortColumnNullsInv (E.^. QueuedJobCreationTime))
|
||||||
|
, ("job" , SortColumn (\v -> v E.^. QueuedJobContent E.->>. "job"))
|
||||||
|
, ("content" , SortColumn (E.^. QueuedJobContent))
|
||||||
|
, ("lock-time" , SortColumnNullsInv (E.^. QueuedJobLockTime))
|
||||||
|
, ("lock-instance" , SortColumn (E.^. QueuedJobLockInstance))
|
||||||
|
, ("creation-instance", SortColumn (E.^. QueuedJobCreationInstance))
|
||||||
|
]
|
||||||
|
dbtFilter = Map.fromList
|
||||||
|
[
|
||||||
|
("job", FilterColumn $ E.mkContainsFilter (\v -> v E.^. QueuedJobContent E.->>. "job"))
|
||||||
|
]
|
||||||
|
dbtFilterUI = \mPrev -> mconcat
|
||||||
|
[
|
||||||
|
prismAForm (singletonFilter "job" . maybePrism _PathPiece) mPrev $ aopt (hoistField lift textField) (fslI MsgTableJob)
|
||||||
|
]
|
||||||
|
dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout }
|
||||||
|
acts :: Map JobTableAction (AForm Handler JobTableActionData)
|
||||||
|
acts = Map.singleton ActJobDelete $ ActJobDeleteData
|
||||||
|
<$> areq checkBoxField (fslI $ MsgActJobDeleteForce jobDeleteLockMinutes) Nothing
|
||||||
|
dbtParams = DBParamsForm
|
||||||
|
{ dbParamsFormAdditional =
|
||||||
|
renderAForm FormStandard
|
||||||
|
$ (, mempty) . First . Just
|
||||||
|
<$> multiActionA acts (fslI MsgTableAction) Nothing
|
||||||
|
, dbParamsFormMethod = POST
|
||||||
|
, dbParamsFormAction = Nothing -- Just $ SomeRoute currentRoute
|
||||||
|
, dbParamsFormAttrs = []
|
||||||
|
, dbParamsFormSubmit = FormSubmit
|
||||||
|
, dbParamsFormEvaluate = liftHandler . runFormPost
|
||||||
|
, dbParamsFormResult = id
|
||||||
|
, dbParamsFormIdent = def
|
||||||
|
}
|
||||||
|
dbtCsvEncode = noCsvEncode
|
||||||
|
dbtCsvDecode = Nothing
|
||||||
|
dbtExtraReps = []
|
||||||
|
-- jobsDBTableValidator :: PSValidator (MForm Handler) (FormResult (First JobTableAction, DBFormResult QueuedJobId Bool (DBRow (Entity QueuedJob))))
|
||||||
|
jobsDBTableValidator = def
|
||||||
|
& defaultSorting [SortDescBy "creation-time"]
|
||||||
|
postprocess :: FormResult (First JobTableActionData, DBFormResult QueuedJobId Bool (DBRow (Entity QueuedJob)))
|
||||||
|
-> FormResult (JobTableActionData, Set QueuedJobId)
|
||||||
|
postprocess inp = do
|
||||||
|
(First (Just act), jobMap) <- inp
|
||||||
|
let jobSet = Map.keysSet . Map.filter id $ getDBFormResult (const False) jobMap
|
||||||
|
return (act, jobSet)
|
||||||
|
(jobActRes, jobsTable) <- runDB (over _1 postprocess <$> dbTable jobsDBTableValidator jobsDBTable)
|
||||||
|
|
||||||
|
formResult jobActRes $ \case
|
||||||
|
(ActJobDeleteData{jobDeleteLocked}, jobIds) -> do
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
let cutoff :: UTCTime
|
||||||
|
cutoff = addUTCTime (nominalMinute * fromIntegral (negate jobDeleteLockMinutes)) now
|
||||||
|
jobReq = length jobIds
|
||||||
|
lockCriteria
|
||||||
|
| jobDeleteLocked =
|
||||||
|
[ QueuedJobLockTime ==. Nothing ] ||.
|
||||||
|
[ QueuedJobLockTime <=. Just cutoff ]
|
||||||
|
| otherwise =
|
||||||
|
[ QueuedJobLockTime ==. Nothing
|
||||||
|
, QueuedJobLockInstance ==. Nothing
|
||||||
|
]
|
||||||
|
rmvd <- runDB $ fromIntegral <$> deleteWhereCount
|
||||||
|
((QueuedJobId <-. Set.toList jobIds) : lockCriteria)
|
||||||
|
|
||||||
|
addMessageI (bool Success Warning $ rmvd < jobReq) (MsgTableJobActDeleteFeedback rmvd jobReq)
|
||||||
|
reloadKeepGetParams AdminJobsR
|
||||||
|
|
||||||
|
|
||||||
|
siteLayoutMsg MsgMenuAdminJobs $ do
|
||||||
|
setTitleI MsgMenuAdminJobs
|
||||||
|
[whamlet|
|
||||||
|
^{jobsTable}
|
||||||
|
|]
|
||||||
|
where
|
||||||
|
doEnc :: ToJSON a => a -> _
|
||||||
|
doEnc = Pretty.encodePrettyToTextBuilder' Pretty.defConfig
|
||||||
|
{ Pretty.confIndent = Pretty.Spaces 2
|
||||||
|
, Pretty.confCompare = comparing $ \t -> ( t `elem` ["job", "notification"]
|
||||||
|
, Text.splitOn "-" t
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
getJobName :: Value -> Maybe Text
|
||||||
|
getJobName (Object o)
|
||||||
|
| Just (String s) <- HashMap.lookup "job" o = Just s -- (kebabToCamel s)
|
||||||
|
getJobName _ = Nothing
|
||||||
@ -28,7 +28,9 @@ import Text.Hamlet
|
|||||||
-- import Handler.Utils.I18n
|
-- import Handler.Utils.I18n
|
||||||
|
|
||||||
import Handler.Admin.Test.Download (testDownload)
|
import Handler.Admin.Test.Download (testDownload)
|
||||||
|
import qualified Database.Esqueleto.Experimental as E (selectOne, unValue)
|
||||||
|
import qualified Database.Esqueleto.PostgreSQL as E (now_)
|
||||||
|
import qualified Database.Esqueleto.Utils as E (psqlVersion_)
|
||||||
|
|
||||||
-- BEGIN - Buttons needed only here
|
-- BEGIN - Buttons needed only here
|
||||||
data ButtonCreate = CreateMath | CreateInf | CrashApp -- Dummy for Example
|
data ButtonCreate = CreateMath | CreateInf | CrashApp -- Dummy for Example
|
||||||
@ -112,9 +114,11 @@ postAdminTestR = do
|
|||||||
let emailWidget' = wrapForm emailWidget def
|
let emailWidget' = wrapForm emailWidget def
|
||||||
{ formAction = Just . SomeRoute $ AdminTestR
|
{ formAction = Just . SomeRoute $ AdminTestR
|
||||||
, formEncoding = emailEnctype
|
, formEncoding = emailEnctype
|
||||||
, formAttrs = [("uw-async-form", "")]
|
, formAttrs = [asyncSubmitAttr] -- equivalent to [("uw-async-form", "")]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
$logInfoS "TEST" $ "Admin Test Page was retrieved at " <> tshow now <> "." -- to ensure that we can read the right log.
|
||||||
|
|
||||||
let demoFormAction (_i,_b,_d) = addMessage Info "All ok."
|
let demoFormAction (_i,_b,_d) = addMessage Info "All ok."
|
||||||
((demoResult, formWidget),formEnctype) <- runFormPost $ makeDemoForm 7
|
((demoResult, formWidget),formEnctype) <- runFormPost $ makeDemoForm 7
|
||||||
@ -222,10 +226,15 @@ postAdminTestR = do
|
|||||||
usrCryptoFileName <- maybeM (return "no-user_id") (fmap toPathPiece . mkCryptoFilnameUser) maybeAuthId
|
usrCryptoFileName <- maybeM (return "no-user_id") (fmap toPathPiece . mkCryptoFilnameUser) maybeAuthId
|
||||||
usrCryptoUUID <- maybeM (return "no-user_id") (fmap toPathPiece . mkCryptoUUIDUser) maybeAuthId
|
usrCryptoUUID <- maybeM (return "no-user_id") (fmap toPathPiece . mkCryptoUUIDUser) maybeAuthId
|
||||||
|
|
||||||
|
UniWorX{ appSettings' = AppSettings{..} } <- getYesod
|
||||||
|
|
||||||
|
psqlVersion <- runDBRead $ E.selectOne $ return E.psqlVersion_
|
||||||
|
dbTime <- runDBRead $ E.selectOne $ return E.now_
|
||||||
|
|
||||||
let locallyDefinedPageHeading = [whamlet|Admin TestPage for Uni2work|]
|
let locallyDefinedPageHeading = [whamlet|Admin TestPage for Uni2work|]
|
||||||
siteLayout locallyDefinedPageHeading $ do
|
siteLayout locallyDefinedPageHeading $ do
|
||||||
-- defaultLayout $ do
|
-- defaultLayout $ do
|
||||||
setTitle "Uni2work Admin Testpage"
|
setTitle "Uni2work Admin Testpage"
|
||||||
|
|
||||||
$(i18nWidgetFile "admin-test")
|
$(i18nWidgetFile "admin-test")
|
||||||
|
|
||||||
@ -311,19 +320,42 @@ postAdminTestR = do
|
|||||||
|]
|
|]
|
||||||
i18n $ MsgPrintDebugForStupid "DebugForStupid"
|
i18n $ MsgPrintDebugForStupid "DebugForStupid"
|
||||||
|
|
||||||
|
[whamlet|
|
||||||
|
<section>
|
||||||
|
<h2> Some Active App Settings
|
||||||
|
<dl .deflist>
|
||||||
|
<dt .deflist__dt> appJobCronInterval
|
||||||
|
<dd .deflist__dd>#{tshow appJobCronInterval}
|
||||||
|
<dt .deflist__dt> appSynchroniseLdapUsersWithin
|
||||||
|
<dd .deflist__dd>#{tshow appSynchroniseLdapUsersWithin}
|
||||||
|
<dt .deflist__dt> appSynchroniseAvsUsersWithin
|
||||||
|
<dd .deflist__dd>#{tshow appSynchroniseAvsUsersWithin}
|
||||||
|
|]
|
||||||
|
|
||||||
|
[whamlet|
|
||||||
|
<section>
|
||||||
|
<h2> PostgreSQL Information
|
||||||
|
<dl .deflist>
|
||||||
|
$maybe pver <- psqlVersion
|
||||||
|
<dt .deflist__dt>DB Version
|
||||||
|
<dd .deflist__dd>#{E.unValue pver}
|
||||||
|
$maybe ptme <- dbTime
|
||||||
|
<dt .deflist__dt>DB Time
|
||||||
|
<dd .deflist__dd>#{tshow (E.unValue ptme)}
|
||||||
|
|]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
getAdminTestPdfR :: Handler TypedContent
|
getAdminTestPdfR :: Handler TypedContent
|
||||||
getAdminTestPdfR = do
|
getAdminTestPdfR = do
|
||||||
usr <- requireAuth -- to determine language and recipient for test
|
usr <- requireAuth -- to determine language and recipient for test
|
||||||
qual <- fromMaybeM
|
qual <- fromMaybeM
|
||||||
(addMessage Error "Keine Qualifikation in der Datenbank zur Erzeugung eines Test-PDFs gefunden." >> redirect AdminTestR)
|
(addMessage Error "Keine Qualifikation in der Datenbank zur Erzeugung eines Test-PDFs gefunden." >> redirect AdminTestR)
|
||||||
(runDB $ selectFirst [] [Asc QualificationAvsLicence, Asc QualificationShorthand])
|
(runDB $ selectFirst [] [Asc QualificationAvsLicence, Asc QualificationShorthand])
|
||||||
encRecipient :: CryptoUUIDUser <- encrypt $ usr ^. _entityKey
|
encRecipient :: CryptoUUIDUser <- encrypt $ usr ^. _entityKey
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
let nowaday = utctDay now
|
let nowaday = utctDay now
|
||||||
letter = LetterRenewQualificationF
|
letter = LetterRenewQualification
|
||||||
{ lmsLogin = LmsIdent "abcdefgh"
|
{ lmsLogin = LmsIdent "abcdefgh"
|
||||||
, lmsPin = "12345678"
|
, lmsPin = "12345678"
|
||||||
, qualHolderID = usr ^. _entityKey
|
, qualHolderID = usr ^. _entityKey
|
||||||
@ -335,15 +367,17 @@ getAdminTestPdfR = do
|
|||||||
, qualShort = qual ^. _qualificationShorthand . _CI
|
, qualShort = qual ^. _qualificationShorthand . _CI
|
||||||
, qualSchool = qual ^. _qualificationSchool
|
, qualSchool = qual ^. _qualificationSchool
|
||||||
, qualDuration = qual ^. _qualificationValidDuration
|
, qualDuration = qual ^. _qualificationValidDuration
|
||||||
|
, qualRenewAuto = qual ^. _qualificationElearningRenews
|
||||||
|
, qualELimit = qual ^. _qualificationElearningLimit
|
||||||
, isReminder = False
|
, isReminder = False
|
||||||
}
|
}
|
||||||
apcIdent <- letterApcIdent letter encRecipient now
|
apcIdent <- letterApcIdent letter encRecipient now
|
||||||
renderLetterPDF usr letter apcIdent >>= \case
|
renderLetterPDF usr letter apcIdent Nothing >>= \case
|
||||||
Left err -> sendResponseStatus internalServerError500 $ "PDF generation failed: \n" <> err
|
Left err -> sendResponseStatus internalServerError500 $ "PDF generation failed: \n" <> err
|
||||||
Right pdf -> do
|
Right pdf -> do
|
||||||
liftIO $ LBS.writeFile "/tmp/generated.pdf" pdf
|
liftIO $ LBS.writeFile "/tmp/generated.pdf" pdf
|
||||||
encryptPDF "tomatenmarmelade" pdf >>= \case
|
encryptPDF "tomatenmarmelade" pdf >>= \case
|
||||||
Left err -> sendResponseStatus internalServerError500 $ "PDFtk error: \n" <> err
|
Left err -> sendResponseStatus internalServerError500 $ "PDFtk error: \n" <> err
|
||||||
Right encPdf -> do
|
Right encPdf -> do
|
||||||
liftIO $ LBS.writeFile "/tmp/crypted.pdf" encPdf
|
liftIO $ LBS.writeFile "/tmp/crypted.pdf" encPdf
|
||||||
sendByteStringAsFile "demoPDF.pdf" (LBS.toStrict pdf) now
|
sendByteStringAsFile "demoPDF.pdf" (LBS.toStrict pdf) now
|
||||||
|
|||||||
152
src/Handler/CommCenter.hs
Normal file
152
src/Handler/CommCenter.hs
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
-- SPDX-FileCopyrightText: 2024 Steffen Jost <s.jost@fraport.de>
|
||||||
|
--
|
||||||
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||||
|
|
||||||
|
module Handler.CommCenter
|
||||||
|
( getCommCenterR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
import Handler.Utils
|
||||||
|
|
||||||
|
-- import qualified Data.Set as Set
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
-- import qualified Data.Text as Text
|
||||||
|
import Data.Text.Lens (packed)
|
||||||
|
|
||||||
|
-- import Database.Persist.Sql (updateWhereCount)
|
||||||
|
-- import Database.Esqueleto.Experimental ((:&)(..))
|
||||||
|
import qualified Database.Esqueleto.Legacy as EL (on) -- only `on` and `from` are different, needed for dbTable using Esqueleto.Legacy
|
||||||
|
import qualified Database.Esqueleto.Experimental as E
|
||||||
|
import qualified Database.Esqueleto.Utils as E
|
||||||
|
import qualified Database.Esqueleto.PostgreSQL as E
|
||||||
|
import Database.Esqueleto.Utils.TH
|
||||||
|
|
||||||
|
|
||||||
|
-- avoids repetition of local definitions
|
||||||
|
single :: (k,a) -> Map k a
|
||||||
|
single = uncurry Map.singleton
|
||||||
|
|
||||||
|
|
||||||
|
data CCTableAction = CCActDummy -- just a dummy, since we don't now yet which actions we will be needing
|
||||||
|
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic)
|
||||||
|
|
||||||
|
instance Universe CCTableAction
|
||||||
|
instance Finite CCTableAction
|
||||||
|
nullaryPathPiece ''CCTableAction $ camelToPathPiece' 2
|
||||||
|
embedRenderMessage ''UniWorX ''CCTableAction id
|
||||||
|
|
||||||
|
data CCTableActionData = CCActDummyData
|
||||||
|
deriving (Eq, Ord, Read, Show, Generic)
|
||||||
|
|
||||||
|
|
||||||
|
-- SJ: I don't know how to use E.unionAll_ with dbTable, so we simulate it by a FullOuterJoin with constant False ON-clause instead
|
||||||
|
type CCTableExpr =
|
||||||
|
( (E.SqlExpr (Maybe (Entity User)) `E.InnerJoin` E.SqlExpr (Maybe (Entity SentMail)))
|
||||||
|
`E.FullOuterJoin` (E.SqlExpr (Maybe (Entity User)) `E.InnerJoin` E.SqlExpr (Maybe (Entity PrintJob)))
|
||||||
|
)
|
||||||
|
|
||||||
|
queryRecipientMail :: CCTableExpr -> E.SqlExpr (Maybe (Entity User))
|
||||||
|
queryRecipientMail = $(sqlIJproj 2 1) . $(sqlFOJproj 2 1)
|
||||||
|
|
||||||
|
queryMail :: CCTableExpr -> E.SqlExpr (Maybe (Entity SentMail))
|
||||||
|
queryMail = $(sqlIJproj 2 2) . $(sqlFOJproj 2 1)
|
||||||
|
|
||||||
|
queryRecipientPrint :: CCTableExpr -> E.SqlExpr (Maybe (Entity User))
|
||||||
|
queryRecipientPrint = $(sqlIJproj 2 1) . $(sqlFOJproj 2 2)
|
||||||
|
|
||||||
|
queryPrint :: CCTableExpr -> E.SqlExpr (Maybe (Entity PrintJob))
|
||||||
|
queryPrint = $(sqlIJproj 2 2) . $(sqlFOJproj 2 2)
|
||||||
|
|
||||||
|
type CCTableData = DBRow (Maybe (Entity User), Maybe (Entity SentMail), Maybe (Entity User), Maybe (Entity PrintJob))
|
||||||
|
|
||||||
|
resultRecipientMail :: Traversal' CCTableData (Entity User)
|
||||||
|
resultRecipientMail = _dbrOutput . _1 . _Just
|
||||||
|
|
||||||
|
resultMail :: Traversal' CCTableData (Entity SentMail)
|
||||||
|
resultMail = _dbrOutput . _2 . _Just
|
||||||
|
|
||||||
|
resultRecipientPrint :: Traversal' CCTableData (Entity User)
|
||||||
|
resultRecipientPrint = _dbrOutput . _3 . _Just
|
||||||
|
|
||||||
|
resultPrint :: Traversal' CCTableData (Entity PrintJob)
|
||||||
|
resultPrint = _dbrOutput . _4 . _Just
|
||||||
|
|
||||||
|
|
||||||
|
mkCCTable :: DB (Any, Widget)
|
||||||
|
mkCCTable = do
|
||||||
|
let
|
||||||
|
dbtSQLQuery :: CCTableExpr -> E.SqlQuery (E.SqlExpr (Maybe (Entity User)), E.SqlExpr (Maybe (Entity SentMail)), E.SqlExpr (Maybe (Entity User)), E.SqlExpr (Maybe (Entity PrintJob)))
|
||||||
|
dbtSQLQuery ((recipientMail `E.InnerJoin` mail) `E.FullOuterJoin` (recipientPrint `E.InnerJoin` printJob)) = do
|
||||||
|
EL.on $ recipientMail E.?. UserId E.==. E.joinV (mail E.?. SentMailRecipient)
|
||||||
|
EL.on $ recipientPrint E.?. UserId E.==. E.joinV (printJob E.?. PrintJobRecipient)
|
||||||
|
-- EL.on $ recipientMail E.?. UserId E.==. recipientPrint E.?. UserId E.&&. E.false -- simulating E.unionAll_ by a constant false full outer join, since it is unclear how dbTable could handle E.unionAll_
|
||||||
|
EL.on E.false -- simulating E.unionAll_ by a constant false full outer join, since it is unclear how dbTable could handle E.unionAll_
|
||||||
|
-- E.where_ $ E.isJust (recipientMail E.?. UserId) E.||. E.isJust (recipientPrint E.?. UserId) -- not needed for full outer join
|
||||||
|
-- return (E.coalesce[recipientMail, recipientPrint], mail, print) -- coalesce only works on values, not entities
|
||||||
|
return (recipientMail, mail, recipientPrint, printJob)
|
||||||
|
-- dbtRowKey = (,) <$> views (to queryMail) (E.?. SentMailId) <*> views (to queryPrint) (E.?. PrintJobId)
|
||||||
|
dbtRowKey ((_recipientMail `E.InnerJoin` mail) `E.FullOuterJoin` (_recipientPrint `E.InnerJoin` printJob)) = (mail E.?. SentMailId, printJob E.?. PrintJobId)
|
||||||
|
|
||||||
|
dbtProj = dbtProjId
|
||||||
|
dbtColonnade = dbColonnade $ mconcat -- prefer print over email in the impossible case that both are Just
|
||||||
|
[ sortable (Just "date") (i18nCell MsgPrintJobCreated) $ \row ->
|
||||||
|
let tprint = row ^? resultPrint . _entityVal . _printJobCreated
|
||||||
|
tmail = row ^? resultMail . _entityVal . _sentMailSentAt
|
||||||
|
in maybeCell (tprint <|> tmail) dateTimeCell
|
||||||
|
, sortable (Just "recipient") (i18nCell MsgPrintRecipient) $ \row ->
|
||||||
|
let uprint = row ^? resultRecipientPrint
|
||||||
|
umail = row ^? resultRecipientMail
|
||||||
|
in maybeCell (uprint <|> umail) $ cellHasUserLink AdminUserR
|
||||||
|
, sortable Nothing (i18nCell MsgCommBody) $ \row -> if
|
||||||
|
| (Just k) <- row ^? resultPrint . _entityKey
|
||||||
|
-> anchorCellM (PrintDownloadR <$> encrypt k) $ toWgt (iconLetterOrEmail True ) <> text2widget "-link"
|
||||||
|
| (Just k) <- row ^? resultMail . _entityKey
|
||||||
|
-> anchorCellM (MailHtmlR <$> encrypt k) $ toWgt (iconLetterOrEmail False) <> text2widget "-link"
|
||||||
|
| otherwise
|
||||||
|
-> mempty
|
||||||
|
, sortable Nothing (i18nCell MsgCommSubject) $ \row ->
|
||||||
|
let tsubject = row ^? resultPrint . _entityVal . _printJobFilename . packed
|
||||||
|
msubject = row ^? resultMail . _entityVal . _sentMailHeaders . _mailHeaders' . _mailHeader' "Subject"
|
||||||
|
in maybeCell (tsubject <|> msubject) textCell
|
||||||
|
]
|
||||||
|
dbtSorting = mconcat
|
||||||
|
[ singletonMap "date" $ SortColumn $ \row -> E.coalesce [queryPrint row E.?. PrintJobCreated, queryMail row E.?. SentMailSentAt]
|
||||||
|
, singletonMap "recipient" $ SortColumns $ \row ->
|
||||||
|
[ SomeExprValue $ E.coalesce [queryRecipientPrint row E.?. UserSurname , queryRecipientMail row E.?. UserSurname ]
|
||||||
|
, SomeExprValue $ E.coalesce [queryRecipientPrint row E.?. UserDisplayName, queryRecipientMail row E.?. UserDisplayName]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
dbtFilter = mconcat
|
||||||
|
[ single ("sent" , FilterColumn . E.mkDayFilterTo
|
||||||
|
$ \row -> E.coalesceDefault [queryPrint row E.?. PrintJobCreated, queryMail row E.?. SentMailSentAt] E.now_) -- either one is guaranteed to be non-null, default never used
|
||||||
|
, single ("recipient" , FilterColumn . E.mkContainsFilterWithCommaPlus Just
|
||||||
|
$ \row -> E.coalesce [queryRecipientPrint row E.?. UserDisplayName, queryRecipientMail row E.?. UserDisplayName])
|
||||||
|
, single ("subject" , FilterColumn . E.mkContainsFilterWithCommaPlus Just
|
||||||
|
$ \row -> E.coalesce [E.str2text' $ queryPrint row E.?. PrintJobFilename
|
||||||
|
,E.str2text' $ queryMail row E.?. SentMailHeaders ])
|
||||||
|
]
|
||||||
|
dbtFilterUI mPrev = mconcat
|
||||||
|
[ prismAForm (singletonFilter "date" . maybePrism _PathPiece) mPrev $ aopt (hoistField lift dayField) (fslI MsgPrintJobCreated)
|
||||||
|
, prismAForm (singletonFilter "recipient" . maybePrism _PathPiece) mPrev $ aopt (hoistField lift textField) (fslI MsgPrintRecipient & setTooltip MsgTableFilterCommaPlus)
|
||||||
|
, prismAForm (singletonFilter "subject" . maybePrism _PathPiece) mPrev $ aopt (hoistField lift textField) (fslI MsgCommSubject & setTooltip MsgTableFilterCommaPlusShort)
|
||||||
|
]
|
||||||
|
dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout}
|
||||||
|
dbtIdent :: Text
|
||||||
|
dbtIdent = "comms"
|
||||||
|
dbtCsvEncode = noCsvEncode
|
||||||
|
dbtCsvDecode = Nothing
|
||||||
|
dbtExtraReps = []
|
||||||
|
dbtParams = def
|
||||||
|
psValidator = def & defaultSorting [SortDescBy "date"]
|
||||||
|
dbTable psValidator DBTable{..}
|
||||||
|
|
||||||
|
getCommCenterR :: Handler Html
|
||||||
|
getCommCenterR = do
|
||||||
|
(_, ccTable) <- runDB mkCCTable
|
||||||
|
siteLayoutMsg MsgMenuCommCenter $ do
|
||||||
|
setTitleI MsgMenuCommCenter
|
||||||
|
$(widgetFile "comm-center")
|
||||||
|
|
||||||
@ -64,8 +64,10 @@ postCCommR tid ssh csh = do
|
|||||||
|
|
||||||
return (cid, tuts, exams, sheets)
|
return (cid, tuts, exams, sheets)
|
||||||
|
|
||||||
|
let heading = SomeMessage . prependCourseTitle tid ssh csh $ SomeMessage MsgCommCourseHeading
|
||||||
commR CommunicationRoute
|
commR CommunicationRoute
|
||||||
{ crHeading = SomeMessage . prependCourseTitle tid ssh csh $ SomeMessage MsgCommCourseHeading
|
{ crHeading = heading
|
||||||
|
, crTitle = heading
|
||||||
, crUltDest = SomeRoute $ CourseR tid ssh csh CCommR
|
, crUltDest = SomeRoute $ CourseR tid ssh csh CCommR
|
||||||
, crJobs = crJobsCourseCommunication cid
|
, crJobs = crJobsCourseCommunication cid
|
||||||
, crTestJobs = crTestJobsCourseCommunication cid
|
, crTestJobs = crTestJobsCourseCommunication cid
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Sarah Vaupel <vaupel.sarah@campus.lmu.de>,Steffen Jost <jost@cip.ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>
|
-- SPDX-FileCopyrightText: 2022-24 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Sarah Vaupel <vaupel.sarah@campus.lmu.de>,Steffen Jost <jost@cip.ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>,Winnie Ros <winnie.ros@campus.lmu.de>
|
||||||
--
|
--
|
||||||
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
@ -46,12 +46,13 @@ data CourseForm = CourseForm
|
|||||||
, cfRegTo :: Maybe UTCTime
|
, cfRegTo :: Maybe UTCTime
|
||||||
, cfDeRegUntil :: Maybe UTCTime
|
, cfDeRegUntil :: Maybe UTCTime
|
||||||
, cfLecturers :: [Either (UserEmail, Maybe LecturerType) (UserId, LecturerType)]
|
, cfLecturers :: [Either (UserEmail, Maybe LecturerType) (UserId, LecturerType)]
|
||||||
|
, cfQualis :: [(QualificationId, Int)]
|
||||||
}
|
}
|
||||||
|
|
||||||
makeLenses_ ''CourseForm
|
makeLenses_ ''CourseForm
|
||||||
|
|
||||||
courseToForm :: Entity Course -> [Lecturer] -> Map UserEmail (InvitationDBData Lecturer) -> CourseForm
|
courseToForm :: Entity Course -> [Lecturer] -> Map UserEmail (InvitationDBData Lecturer) -> [CourseQualification] -> CourseForm
|
||||||
courseToForm (Entity cid Course{..}) lecs lecInvites = CourseForm
|
courseToForm (Entity cid Course{..}) lecs lecInvites qualis = CourseForm
|
||||||
{ cfCourseId = Just cid
|
{ cfCourseId = Just cid
|
||||||
, cfName = courseName
|
, cfName = courseName
|
||||||
, cfDesc = courseDescription
|
, cfDesc = courseDescription
|
||||||
@ -69,6 +70,9 @@ courseToForm (Entity cid Course{..}) lecs lecInvites = CourseForm
|
|||||||
, cfDeRegUntil = courseDeregisterUntil
|
, cfDeRegUntil = courseDeregisterUntil
|
||||||
, cfLecturers = [Right (lecturerUser, lecturerType) | Lecturer{..} <- lecs]
|
, cfLecturers = [Right (lecturerUser, lecturerType) | Lecturer{..} <- lecs]
|
||||||
++ [Left (email, mType) | (email, InvDBDataLecturer mType) <- Map.toList lecInvites ]
|
++ [Left (email, mType) | (email, InvDBDataLecturer mType) <- Map.toList lecInvites ]
|
||||||
|
-- TODO: Filterung nach aktueller Schule, da ansonsten ein Sicherheitleck droht! Siehe #150
|
||||||
|
, cfQualis = [ (courseQualificationQualification, courseQualificationSortOrder)
|
||||||
|
| CourseQualification{..} <- qualis, courseQualificationCourse == cid ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -81,17 +85,19 @@ makeCourseForm miButtonAction template = identifyForm FIDcourse . validateFormDB
|
|||||||
MsgRenderer mr <- getMsgRenderer
|
MsgRenderer mr <- getMsgRenderer
|
||||||
|
|
||||||
uid <- liftHandler requireAuthId
|
uid <- liftHandler requireAuthId
|
||||||
(lecturerSchools, adminSchools, oldSchool) <- liftHandler . runDB $ do
|
(userSchools, elegibleQualifications) :: ([SchoolId], OptionList QualificationId) <- liftHandler . runDB $ do
|
||||||
lecturerSchools <- map (userFunctionSchool . entityVal) <$> selectList [UserFunctionUser ==. uid, UserFunctionFunction <-. [SchoolLecturer]] []
|
lecturerSchools <- map (userFunctionSchool . entityVal) <$> selectList [UserFunctionUser ==. uid, UserFunctionFunction <-. [SchoolLecturer]] []
|
||||||
protoAdminSchools <- map (userFunctionSchool . entityVal) <$> selectList [UserFunctionUser ==. uid, UserFunctionFunction <-. [SchoolAdmin]] []
|
protoAdminSchools <- map (userFunctionSchool . entityVal) <$> selectList [UserFunctionUser ==. uid, UserFunctionFunction <-. [SchoolAdmin]] [] -- default rights
|
||||||
adminSchools <- filterM (hasWriteAccessTo . flip SchoolR SchoolEditR) protoAdminSchools
|
adminSchools <- filterM (hasWriteAccessTo . flip SchoolR SchoolEditR) protoAdminSchools -- and user as admin rights active right now
|
||||||
oldSchool <- forM (cfCourseId =<< template) $ fmap courseSchool . getJust
|
oldSchool <- forM (cfCourseId =<< template) $ fmap courseSchool . getJust
|
||||||
return (lecturerSchools, adminSchools, oldSchool)
|
let elegibleSchools = Set.fromList $ lecturerSchools ++ adminSchools
|
||||||
let userSchools = nubOrd . maybe id (:) oldSchool $ lecturerSchools ++ adminSchools
|
userSchools = Set.toList $ maybe id Set.insert oldSchool elegibleSchools
|
||||||
|
elegibleQualifications <- selectList [QualificationSchool <-. Set.toList elegibleSchools] [Asc QualificationName, Asc QualificationSchool]
|
||||||
|
return (userSchools, qualificationsOptionList elegibleQualifications)
|
||||||
|
|
||||||
(termsField, userTerms) <- liftHandler $ case template of
|
(termsField, userTerms) <- liftHandler $ case template of
|
||||||
-- Change of term is only allowed if user may delete the course (i.e. no participants) or admin
|
-- Change of term is only allowed if user may delete the course (i.e. no participants) or admin
|
||||||
(Just cform) | (Just cid) <- cfCourseId cform -> do -- edit existing course
|
(Just cform) | (Just cid) <- cfCourseId cform -> do -- edit existing course& \c
|
||||||
_courseOld@Course{..} <- runDB $ get404 cid
|
_courseOld@Course{..} <- runDB $ get404 cid
|
||||||
mayEditTerm <- isAuthorized TermEditR True
|
mayEditTerm <- isAuthorized TermEditR True
|
||||||
mayDelete <- isAuthorized (CourseR courseTerm courseSchool courseShorthand CDeleteR) True
|
mayDelete <- isAuthorized (CourseR courseTerm courseSchool courseShorthand CDeleteR) True
|
||||||
@ -102,51 +108,7 @@ makeCourseForm miButtonAction template = identifyForm FIDcourse . validateFormDB
|
|||||||
-> return (termsSetField [cfTerm cform], [cfTerm cform])
|
-> return (termsSetField [cfTerm cform], [cfTerm cform])
|
||||||
_allOtherCases -> (termsAllowedField, ) . Set.toList <$> runDB getActiveTerms
|
_allOtherCases -> (termsAllowedField, ) . Set.toList <$> runDB getActiveTerms
|
||||||
|
|
||||||
let miAdd :: ListPosition -> Natural -> ListLength -> (Text -> Text) -> FieldView UniWorX -> Maybe (Form (Map ListPosition (Either UserEmail UserId) -> FormResult (Map ListPosition (Either UserEmail UserId))))
|
let lecturerForm :: AForm Handler [Either (UserEmail, Maybe LecturerType) (UserId, LecturerType)]
|
||||||
miAdd _ _ _ nudge btn = Just $ \csrf -> do
|
|
||||||
(addRes, addView) <- mpreq (multiUserInvitationField $ MUILookupAnyUser Nothing) (fslI MsgCourseLecturerEmail & addName (nudge "email") & addPlaceholder (mr MsgLdapIdentificationOrEmail)) Nothing
|
|
||||||
let addRes'' = addRes <&> \newDat oldDat -> if
|
|
||||||
| existing <- newDat `Set.intersection` Set.fromList (Map.elems oldDat)
|
|
||||||
, not $ Set.null existing
|
|
||||||
-> FormFailure [mr MsgCourseLecturerAlreadyAdded]
|
|
||||||
| otherwise
|
|
||||||
-> FormSuccess . Map.fromList . zip [maybe 0 (succ . fst) $ Map.lookupMax oldDat ..] $ Set.toList newDat
|
|
||||||
addView' = $(widgetFile "course/lecturerMassInput/add")
|
|
||||||
return (addRes'', addView')
|
|
||||||
|
|
||||||
miCell :: ListPosition -> Either UserEmail UserId -> Maybe (Maybe LecturerType) -> (Text -> Text) -> Form (Maybe LecturerType)
|
|
||||||
miCell _ (Right lid) defType nudge = \csrf -> do
|
|
||||||
(lrwRes,lrwView) <- mreq (selectField optionsFinite) (fslI MsgCourseLecturerType & addName (nudge "lecturer-type")) (join defType)
|
|
||||||
usr <- liftHandler . runDB $ get404 lid
|
|
||||||
let lrwView' = $(widgetFile "course/lecturerMassInput/cellKnown")
|
|
||||||
return (Just <$> lrwRes,lrwView')
|
|
||||||
miCell _ (Left lEmail) defType nudge = \csrf -> do
|
|
||||||
(lrwRes,lrwView) <- mopt (selectField optionsFinite) ("" & addName (nudge "lecturer-type")) defType
|
|
||||||
invWarnMsg <- messageIconI Info IconEmail MsgEmailInvitationWarning
|
|
||||||
let lrwView' = $(widgetFile "course/lecturerMassInput/cellInvitation")
|
|
||||||
return (lrwRes,lrwView')
|
|
||||||
|
|
||||||
miDelete :: Map ListPosition (Either UserEmail UserId) -- ^ Current shape
|
|
||||||
-> ListPosition -- ^ Coordinate to delete
|
|
||||||
-> MaybeT (MForm (HandlerFor UniWorX)) (Map ListPosition ListPosition)
|
|
||||||
miDelete = miDeleteList
|
|
||||||
|
|
||||||
miAddEmpty :: ListPosition -> Natural -> ListLength -> Set ListPosition
|
|
||||||
miAddEmpty _ _ _ = Set.empty
|
|
||||||
|
|
||||||
miLayout :: ListLength
|
|
||||||
-> Map ListPosition (Either UserEmail UserId, FormResult (Maybe LecturerType)) -- ^ massInput state
|
|
||||||
-> Map ListPosition Widget -- ^ Cell widgets
|
|
||||||
-> Map ListPosition (FieldView UniWorX) -- ^ Deletion buttons
|
|
||||||
-> Map (Natural, ListPosition) Widget -- ^ Addition widgets
|
|
||||||
-> Widget
|
|
||||||
miLayout lLength _ cellWdgts delButtons addWdgts = $(widgetFile "course/lecturerMassInput/layout")
|
|
||||||
|
|
||||||
miIdent :: Text
|
|
||||||
miIdent = "lecturers"
|
|
||||||
|
|
||||||
|
|
||||||
lecturerForm :: AForm Handler [Either (UserEmail, Maybe LecturerType) (UserId, LecturerType)]
|
|
||||||
lecturerForm = formToAForm . over (mapped._2) pure . over (mapped._1.mapped) (map liftEither . Map.elems) $ massInput
|
lecturerForm = formToAForm . over (mapped._2) pure . over (mapped._1.mapped) (map liftEither . Map.elems) $ massInput
|
||||||
MassInput{..}
|
MassInput{..}
|
||||||
(fslI MsgCourseLecturers & setTooltip MsgCourseLecturerRightsIdentical)
|
(fslI MsgCourseLecturers & setTooltip MsgCourseLecturerRightsIdentical)
|
||||||
@ -163,6 +125,79 @@ makeCourseForm miButtonAction template = identifyForm FIDcourse . validateFormDB
|
|||||||
unliftEither (Right (lid , lType )) = (Right lid , Just lType)
|
unliftEither (Right (lid , lType )) = (Right lid , Just lType)
|
||||||
unliftEither (Left (lEmail, mLType)) = (Left lEmail, mLType )
|
unliftEither (Left (lEmail, mLType)) = (Left lEmail, mLType )
|
||||||
|
|
||||||
|
miAdd :: ListPosition -> Natural -> ListLength -> (Text -> Text) -> FieldView UniWorX -> Maybe (Form (Map ListPosition (Either UserEmail UserId) -> FormResult (Map ListPosition (Either UserEmail UserId))))
|
||||||
|
miAdd _ _ _ nudge btn = Just $ \csrf -> do
|
||||||
|
(addRes, addView) <- mpreq (multiUserInvitationField $ MUILookupAnyUser Nothing) (fslI MsgCourseLecturerEmail & addName (nudge "email") & addPlaceholder (mr MsgLdapIdentificationOrEmail)) Nothing
|
||||||
|
let addRes'' = addRes <&> \newDat oldDat -> if
|
||||||
|
| existing <- newDat `Set.intersection` Set.fromList (Map.elems oldDat)
|
||||||
|
, not $ Set.null existing
|
||||||
|
-> FormFailure [mr MsgCourseLecturerAlreadyAdded]
|
||||||
|
| otherwise
|
||||||
|
-> FormSuccess . Map.fromList . zip [maybe 0 (succ . fst) $ Map.lookupMax oldDat ..] $ Set.toList newDat
|
||||||
|
addView' = $(widgetFile "course/lecturerMassInput/add")
|
||||||
|
return (addRes'', addView')
|
||||||
|
|
||||||
|
miCell :: ListPosition -> Either UserEmail UserId -> Maybe (Maybe LecturerType) -> (Text -> Text) -> Form (Maybe LecturerType)
|
||||||
|
miCell _ (Right lid) defType nudge = \csrf -> do
|
||||||
|
(lrwRes,lrwView) <- mreq (selectField optionsFinite) (fslI MsgCourseLecturerType & addName (nudge "lecturer-type")) (join defType)
|
||||||
|
usr <- liftHandler . runDB $ get404 lid
|
||||||
|
let lrwView' = $(widgetFile "course/lecturerMassInput/cellKnown")
|
||||||
|
return (Just <$> lrwRes,lrwView')
|
||||||
|
miCell _ (Left lEmail) defType nudge = \csrf -> do
|
||||||
|
(lrwRes,lrwView) <- mopt (selectField optionsFinite) ("" & addName (nudge "lecturer-type")) defType
|
||||||
|
invWarnMsg <- messageIconI Info IconEmail MsgEmailInvitationWarning
|
||||||
|
let lrwView' = $(widgetFile "course/lecturerMassInput/cellInvitation")
|
||||||
|
return (lrwRes,lrwView')
|
||||||
|
|
||||||
|
miDelete :: Map ListPosition (Either UserEmail UserId) -- ^ Current shape
|
||||||
|
-> ListPosition -- ^ Coordinate to delete
|
||||||
|
-> MaybeT (MForm (HandlerFor UniWorX)) (Map ListPosition ListPosition)
|
||||||
|
miDelete = miDeleteList
|
||||||
|
|
||||||
|
miAddEmpty :: ListPosition -> Natural -> ListLength -> Set ListPosition
|
||||||
|
miAddEmpty _ _ _ = Set.empty
|
||||||
|
|
||||||
|
miLayout :: ListLength
|
||||||
|
-> Map ListPosition (Either UserEmail UserId, FormResult (Maybe LecturerType)) -- ^ massInput state
|
||||||
|
-> Map ListPosition Widget -- ^ Cell widgets
|
||||||
|
-> Map ListPosition (FieldView UniWorX) -- ^ Deletion buttons
|
||||||
|
-> Map (Natural, ListPosition) Widget -- ^ Addition widgets
|
||||||
|
-> Widget
|
||||||
|
miLayout lLength _ cellWdgts delButtons addWdgts = $(widgetFile "course/lecturerMassInput/layout")
|
||||||
|
|
||||||
|
miIdent :: Text
|
||||||
|
miIdent = "lecturers"
|
||||||
|
|
||||||
|
qualificationsForm :: Maybe [(QualificationId, Int)] -> AForm Handler [(QualificationId, Int)] -- filter by admin school done later through upsertCourseQualifications
|
||||||
|
qualificationsForm = massInputAccumEditA miAdd miEdit miButtonAction miLayout miIdent (fslI $ MsgCourseQualifications 9) False
|
||||||
|
where
|
||||||
|
miIdent :: Text
|
||||||
|
miIdent = "qualifications"
|
||||||
|
|
||||||
|
miAdd :: (Text -> Text) -> FieldView UniWorX -> Form ([(QualificationId,Int)] -> FormResult [(QualificationId,Int)])
|
||||||
|
miAdd nudge submitView csrf = do
|
||||||
|
(formRes, formView) <- aCourseQualiForm nudge Nothing csrf
|
||||||
|
let addRes = formRes <&> \newDat@(newQid,oldOrd) (unzip -> (oldQids,oldOrds)) ->
|
||||||
|
let qidBad = guardMonoid (newQid `elem` oldQids) [mr MsgCourseEditQualificationFailExists]
|
||||||
|
ordBad = guardMonoid (oldOrd `elem` oldOrds) [mr MsgCourseEditQualificationFailOrder ]
|
||||||
|
problems = qidBad ++ ordBad
|
||||||
|
in if null problems
|
||||||
|
then FormSuccess $ pure newDat
|
||||||
|
else FormFailure problems
|
||||||
|
return (addRes, $(widgetFile "widgets/massinput/courseQualifications/add"))
|
||||||
|
|
||||||
|
miEdit :: (Text -> Text) -> (QualificationId, Int) -> Form (QualificationId, Int)
|
||||||
|
miEdit nudge = aCourseQualiForm nudge . Just
|
||||||
|
|
||||||
|
miLayout :: MassInputLayout ListLength (QualificationId,Int) (QualificationId, Int)
|
||||||
|
miLayout lLength _ cellWdgts delButtons addWdgts = $(widgetFile "widgets/massinput/courseQualifications/layout")
|
||||||
|
|
||||||
|
aCourseQualiForm :: (Text -> Text) -> Maybe (QualificationId, Int) -> Form (QualificationId, Int)
|
||||||
|
aCourseQualiForm nudge mTemplate csrf = do
|
||||||
|
(cquRes, cquView) <- mpreq (selectField $ pure elegibleQualifications) ("" & addName (nudge "cquali")) (view _1 <$> mTemplate)
|
||||||
|
(ordRes, ordView) <- mpreq intField ("" & addName (nudge "cqordr")) (view _2 <$> mTemplate)
|
||||||
|
return ((,) <$> cquRes <*> ordRes, $(widgetFile "widgets/massinput/courseQualifications/form"))
|
||||||
|
|
||||||
(newVisFrom,newRegFrom,newRegTo,newDeRegUntil) <- case template of
|
(newVisFrom,newRegFrom,newRegTo,newDeRegUntil) <- case template of
|
||||||
(Just cform) | (Just _cid) <- cfCourseId cform -> return (Nothing,Nothing,Nothing,Nothing)
|
(Just cform) | (Just _cid) <- cfCourseId cform -> return (Nothing,Nothing,Nothing,Nothing)
|
||||||
_allIOtherCases -> do
|
_allIOtherCases -> do
|
||||||
@ -208,6 +243,7 @@ makeCourseForm miButtonAction template = identifyForm FIDcourse . validateFormDB
|
|||||||
& setTooltip MsgCourseDeregisterUntilTip) (deepAlt (cfDeRegUntil <$> template) newDeRegUntil)
|
& setTooltip MsgCourseDeregisterUntilTip) (deepAlt (cfDeRegUntil <$> template) newDeRegUntil)
|
||||||
<* aformSection MsgCourseFormSectionAdministration
|
<* aformSection MsgCourseFormSectionAdministration
|
||||||
<*> lecturerForm
|
<*> lecturerForm
|
||||||
|
<*> qualificationsForm (cfQualis <$> template)
|
||||||
return (result, widget)
|
return (result, widget)
|
||||||
|
|
||||||
|
|
||||||
@ -227,6 +263,10 @@ validateCourse = do
|
|||||||
unless userAdmin $ do
|
unless userAdmin $ do
|
||||||
guardValidation MsgCourseUserMustBeLecturer
|
guardValidation MsgCourseUserMustBeLecturer
|
||||||
$ anyOf (traverse . _Right . _1) (== uid) cfLecturers
|
$ anyOf (traverse . _Right . _1) (== uid) cfLecturers
|
||||||
|
guardValidation MsgCourseEditQualificationFailExists
|
||||||
|
$ not $ hasDuplicates $ fst <$> cfQualis
|
||||||
|
guardValidation MsgCourseEditQualificationFailOrder
|
||||||
|
$ not $ hasDuplicates $ snd <$> cfQualis
|
||||||
|
|
||||||
warnValidation MsgCourseShorthandTooLong
|
warnValidation MsgCourseShorthandTooLong
|
||||||
$ length (CI.original cfShort) <= 10
|
$ length (CI.original cfShort) <= 10
|
||||||
@ -279,9 +319,12 @@ getCourseNewR = do
|
|||||||
, E.desc $ courseCreated course] -- most recent created course
|
, E.desc $ courseCreated course] -- most recent created course
|
||||||
E.limit 1
|
E.limit 1
|
||||||
return course
|
return course
|
||||||
template <- case listToMaybe oldCourses of
|
template <- case oldCourses of
|
||||||
(Just oldTemplate) ->
|
(oldTemplate:_) -> runDB $ do
|
||||||
let newTemplate = courseToForm oldTemplate mempty mempty in
|
mbLecs <- oldTemplate & \course -> map entityVal <$> selectList [LecturerCourse ==. entityKey course] [Asc LecturerType]
|
||||||
|
mbLecInvites <- oldTemplate & sourceInvitationsF . entityKey
|
||||||
|
mbQualis <- oldTemplate & \course -> map entityVal <$> selectList [CourseQualificationCourse ==. entityKey course] [Asc CourseQualificationSortOrder]
|
||||||
|
let newTemplate = courseToForm oldTemplate mbLecs mbLecInvites mbQualis
|
||||||
return $ Just $ newTemplate
|
return $ Just $ newTemplate
|
||||||
{ cfCourseId = Nothing
|
{ cfCourseId = Nothing
|
||||||
, cfTerm = TermKey $ termFromRational 0 -- invalid, will be ignored; undefined won't work due to strictness
|
, cfTerm = TermKey $ termFromRational 0 -- invalid, will be ignored; undefined won't work due to strictness
|
||||||
@ -289,11 +332,11 @@ getCourseNewR = do
|
|||||||
, cfRegTo = Nothing
|
, cfRegTo = Nothing
|
||||||
, cfDeRegUntil = Nothing
|
, cfDeRegUntil = Nothing
|
||||||
}
|
}
|
||||||
Nothing -> do
|
[] -> do
|
||||||
(tidOk,sshOk,cshOk) <- runDB $ (,,)
|
(tidOk,sshOk,cshOk) <- runDB $ (,,)
|
||||||
<$> ifMaybeM mbTid True existsKey
|
<$> ifNothingM mbTid True existsKey
|
||||||
<*> ifMaybeM mbSsh True existsKey
|
<*> ifNothingM mbSsh True existsKey
|
||||||
<*> ifMaybeM mbCsh True (\csh -> not . null <$> selectKeysList [CourseShorthand ==. csh] [LimitTo 1])
|
<*> ifNothingM mbCsh True (\csh -> not . null <$> selectKeysList [CourseShorthand ==. csh] [LimitTo 1])
|
||||||
unless tidOk $ addMessageI Warning $ MsgNoSuchTerm $ fromJust mbTid -- safe, since tidOk==True otherwise
|
unless tidOk $ addMessageI Warning $ MsgNoSuchTerm $ fromJust mbTid -- safe, since tidOk==True otherwise
|
||||||
unless sshOk $ addMessageI Warning $ MsgNoSuchSchool $ fromJust mbSsh -- safe, since sshOk==True otherwise
|
unless sshOk $ addMessageI Warning $ MsgNoSuchSchool $ fromJust mbSsh -- safe, since sshOk==True otherwise
|
||||||
unless cshOk $ addMessageI Warning $ MsgNoSuchCourseShorthand $ fromJust mbCsh
|
unless cshOk $ addMessageI Warning $ MsgNoSuchCourseShorthand $ fromJust mbCsh
|
||||||
@ -314,10 +357,11 @@ pgCEditR tid ssh csh = do
|
|||||||
mbCourse <- getBy (TermSchoolCourseShort tid ssh csh)
|
mbCourse <- getBy (TermSchoolCourseShort tid ssh csh)
|
||||||
mbLecs <- for mbCourse $ \course -> map entityVal <$> selectList [LecturerCourse ==. entityKey course] [Asc LecturerType]
|
mbLecs <- for mbCourse $ \course -> map entityVal <$> selectList [LecturerCourse ==. entityKey course] [Asc LecturerType]
|
||||||
mbLecInvites <- for mbCourse $ sourceInvitationsF . entityKey
|
mbLecInvites <- for mbCourse $ sourceInvitationsF . entityKey
|
||||||
return $ (,,) <$> mbCourse <*> mbLecs <*> mbLecInvites
|
mbQualis <- for mbCourse $ \course -> map entityVal <$> selectList [CourseQualificationCourse ==. entityKey course] [Asc CourseQualificationSortOrder]
|
||||||
|
return $ (,,,) <$> mbCourse <*> mbLecs <*> mbLecInvites <*> mbQualis
|
||||||
-- IMPORTANT: both GET and POST Handler must use the same template,
|
-- IMPORTANT: both GET and POST Handler must use the same template,
|
||||||
-- since an Edit is identified via CourseID, which is not embedded in the received form data for security reasons.
|
-- since an Edit is identified via CourseID, which is not embedded in the received form data for security reasons.
|
||||||
courseEditHandler (\p -> Just . SomeRoute $ CourseR tid ssh csh CEditR :#: p) $ $(uncurryN 3) courseToForm <$> courseData
|
courseEditHandler (\p -> Just . SomeRoute $ CourseR tid ssh csh CEditR :#: p) $ $(uncurryN 4) courseToForm <$> courseData
|
||||||
|
|
||||||
|
|
||||||
-- | Course Creation and Editing
|
-- | Course Creation and Editing
|
||||||
@ -357,6 +401,7 @@ courseEditHandler miButtonAction mbCourseForm = do
|
|||||||
let (invites, adds) = partitionEithers $ cfLecturers res
|
let (invites, adds) = partitionEithers $ cfLecturers res
|
||||||
insertMany_ $ map (\(lid, lty) -> Lecturer lid cid lty) adds
|
insertMany_ $ map (\(lid, lty) -> Lecturer lid cid lty) adds
|
||||||
sinkInvitationsF lecturerInvitationConfig $ map (\(lEmail, mLty) -> (lEmail, cid, (InvDBDataLecturer mLty, InvTokenDataLecturer))) invites
|
sinkInvitationsF lecturerInvitationConfig $ map (\(lEmail, mLty) -> (lEmail, cid, (InvDBDataLecturer mLty, InvTokenDataLecturer))) invites
|
||||||
|
void $ upsertCourseQualifications aid cid $ cfQualis res
|
||||||
insert_ $ CourseEdit aid now cid
|
insert_ $ CourseEdit aid now cid
|
||||||
memcachedByInvalidate AuthCacheLecturerList $ Proxy @(Set UserId)
|
memcachedByInvalidate AuthCacheLecturerList $ Proxy @(Set UserId)
|
||||||
return insertOkay
|
return insertOkay
|
||||||
@ -405,11 +450,9 @@ courseEditHandler miButtonAction mbCourseForm = do
|
|||||||
let (invites, adds) = partitionEithers $ cfLecturers res
|
let (invites, adds) = partitionEithers $ cfLecturers res
|
||||||
insertMany_ $ map (\(lid, lty) -> Lecturer lid cid lty) adds
|
insertMany_ $ map (\(lid, lty) -> Lecturer lid cid lty) adds
|
||||||
sinkInvitationsF lecturerInvitationConfig $ map (\(lEmail, mLty) -> (lEmail, cid, (InvDBDataLecturer mLty, InvTokenDataLecturer))) invites
|
sinkInvitationsF lecturerInvitationConfig $ map (\(lEmail, mLty) -> (lEmail, cid, (InvDBDataLecturer mLty, InvTokenDataLecturer))) invites
|
||||||
|
void $ upsertCourseQualifications aid cid $ cfQualis res
|
||||||
insert_ $ CourseEdit aid now cid
|
insert_ $ CourseEdit aid now cid
|
||||||
|
|
||||||
memcachedByInvalidate AuthCacheLecturerList $ Proxy @(Set UserId)
|
memcachedByInvalidate AuthCacheLecturerList $ Proxy @(Set UserId)
|
||||||
|
|
||||||
addMessageI Success $ MsgCourseEditOk tid ssh csh
|
addMessageI Success $ MsgCourseEditOk tid ssh csh
|
||||||
return True
|
return True
|
||||||
when success $ redirect $ CourseR tid ssh csh CShowR
|
when success $ redirect $ CourseR tid ssh csh CShowR
|
||||||
@ -420,3 +463,35 @@ courseEditHandler miButtonAction mbCourseForm = do
|
|||||||
{ formAction = Just $ SomeRoute actionUrl
|
{ formAction = Just $ SomeRoute actionUrl
|
||||||
, formEncoding = formEnctype
|
, formEncoding = formEnctype
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- upsertCourseQualifications :: forall m backend . (MonadIO m, PersistStoreWrite backend, PersistQueryRead backend) => UserId -> CourseId -> [(QualificationId, Int)] -> ReaderT backend m Bool
|
||||||
|
upsertCourseQualifications :: UserId -> CourseId -> [(QualificationId, Int)] -> YesodJobDB UniWorX Bool -- could be generalized
|
||||||
|
upsertCourseQualifications uid cid qualis = do
|
||||||
|
let newQualis = Map.fromList qualis
|
||||||
|
oldQualis <- Map.fromList . fmap (\Entity{entityKey=k, entityVal=CourseQualification{..}} -> (courseQualificationQualification, (k, courseQualificationSortOrder)))
|
||||||
|
<$> selectList [CourseQualificationCourse ==. cid] [Asc CourseQualificationQualification]
|
||||||
|
-- NOTE: CourseQualification allow the immediate assignment of these qualifications to any enrolled user. Hence SchoolAdmins must not be allowed to assign school-foreign qualifications, see #150
|
||||||
|
okSchools <- Set.fromList . fmap (userFunctionSchool . entityVal)
|
||||||
|
<$> selectList [UserFunctionUser ==. uid, UserFunctionFunction <-. [SchoolAdmin, SchoolLecturer]] [Asc UserFunctionSchool]
|
||||||
|
{- Some debugging due to an error caused by using fromDistinctAscList with violated precondition:
|
||||||
|
$logErrorS "CourseQuali" $ "OLD Course Qualifications:" <> tshow oldQualis
|
||||||
|
$logErrorS "CourseQuali" $ "NEW Course Qualifications:" <> tshow newQualis
|
||||||
|
$logErrorS "CourseQuali" $ "DIFF Course Qualifications:" <> tshow (newQualis Map.\\ oldQualis)
|
||||||
|
-}
|
||||||
|
foldWithKeyMapM oldQualis $ \qu (k, so_old) -> case Map.lookup qu newQualis of
|
||||||
|
Just so_new | so_new /= so_old
|
||||||
|
-> update k [CourseQualificationSortOrder =. so_new] -- existing CourseQualifications may be re-ordered, regardless of school association
|
||||||
|
Nothing -> delete k -- existing CourseQualifications may be removed, regardless of school association
|
||||||
|
_ -> return ()
|
||||||
|
res <- foldWithKeyMapM (newQualis Map.\\ oldQualis) $ \qu so -> get qu >>= \case
|
||||||
|
Just Qualification{qualificationSchool=ssh, qualificationShorthand=qsh}
|
||||||
|
| Set.member ssh okSchools ->
|
||||||
|
insert_ CourseQualification{courseQualificationQualification = qu, courseQualificationCourse = cid, courseQualificationSortOrder = so}
|
||||||
|
$> All True
|
||||||
|
| otherwise -> do
|
||||||
|
addMessageI Warning $ MsgCourseEditQualificationFailRights qsh ssh
|
||||||
|
pure $ All False
|
||||||
|
_ -> do
|
||||||
|
addMessageI Warning MsgCourseEditQualificationFail
|
||||||
|
pure $ All False
|
||||||
|
pure $ getAll res
|
||||||
|
|||||||
@ -105,7 +105,7 @@ colSchoolShort = sortable (Just "schoolshort") (i18nCell MsgFilterCourseSchoolSh
|
|||||||
in anchorCell (TermSchoolCourseListR courseTerm courseSchool) [whamlet|_{schoolShorthand}|]
|
in anchorCell (TermSchoolCourseListR courseTerm courseSchool) [whamlet|_{schoolShorthand}|]
|
||||||
|
|
||||||
colRegistered :: IsDBTable m a => Colonnade Sortable CourseTableData (DBCell m a)
|
colRegistered :: IsDBTable m a => Colonnade Sortable CourseTableData (DBCell m a)
|
||||||
colRegistered = sortable (Just "registered") (i18nCell MsgFilterRegistered) $ views resultIsRegistered tickmarkCell
|
colRegistered = sortable (Just "registered") (i18nCell MsgFilterRegistered) $ views resultIsRegistered ((spacerCell <>) . tickmarkCell)
|
||||||
|
|
||||||
|
|
||||||
makeCourseTable :: (ToSortable h, Functor h)
|
makeCourseTable :: (ToSortable h, Functor h)
|
||||||
@ -226,7 +226,16 @@ getCourseListR = do
|
|||||||
]
|
]
|
||||||
validator = def
|
validator = def
|
||||||
& defaultSorting [SortDescBy "term",SortAscBy "course"]
|
& defaultSorting [SortDescBy "term",SortAscBy "course"]
|
||||||
coursesTable <- runDB $ makeCourseTable colonnade validator
|
now <- liftIO getCurrentTime
|
||||||
|
coursesTable <- runDB $ do
|
||||||
|
activeTs <- selectList [TermActiveFrom <=. now
|
||||||
|
, FilterOr [TermActiveTo >. Just now, TermActiveTo ==. Nothing]
|
||||||
|
, FilterOr [TermActiveFor ==. muid, TermActiveFor ==. Nothing] -- TermActiveFor <-. [Nothing, muid] did not work as intended
|
||||||
|
] [Desc TermActiveTerm]
|
||||||
|
let addTermFilter = if null activeTs
|
||||||
|
then id
|
||||||
|
else defaultFilter $ singletonMap "term" [toPathPiece termActiveTerm | Entity _ TermActive{termActiveTerm} <- activeTs]
|
||||||
|
makeCourseTable colonnade (validator & addTermFilter)
|
||||||
defaultLayout $ do
|
defaultLayout $ do
|
||||||
setTitleI MsgCourseListTitle
|
setTitleI MsgCourseListTitle
|
||||||
$(widgetFile "courses")
|
$(widgetFile "courses")
|
||||||
|
|||||||
@ -49,10 +49,18 @@ tutorialTemplateNames Nothing = ["Vorlage", "Template"]
|
|||||||
tutorialTemplateNames (Just name) = [prefixes <> suffixes | prefixes <- tutorialTemplateNames Nothing, suffixes <- [mempty, tutorialTypeSeparator <> name]]
|
tutorialTemplateNames (Just name) = [prefixes <> suffixes | prefixes <- tutorialTemplateNames Nothing, suffixes <- [mempty, tutorialTypeSeparator <> name]]
|
||||||
|
|
||||||
tutorialDefaultName :: Maybe TutorialType -> Day -> TutorialName
|
tutorialDefaultName :: Maybe TutorialType -> Day -> TutorialName
|
||||||
tutorialDefaultName Nothing = CI.mk . tshow -- Don't use user date display setting, so that tutorial default names conform to all users
|
tutorialDefaultName Nothing = formatDayForTutName
|
||||||
tutorialDefaultName (Just ttyp) =
|
tutorialDefaultName (Just ttyp) =
|
||||||
let prefix = CI.mk $ snd $ Text.breakOnEnd (CI.original tutorialTypeSeparator) $ CI.original ttyp
|
let prefix = CI.mk $ snd $ Text.breakOnEnd (CI.original tutorialTypeSeparator) $ CI.original ttyp
|
||||||
in ((prefix <> tutorialTypeSeparator) <>) . tutorialDefaultName Nothing
|
in (<> (tutorialTypeSeparator <> prefix)) . tutorialDefaultName Nothing
|
||||||
|
|
||||||
|
formatDayForTutName :: Day -> CI Text -- "%yy_%mm_%dd" -- Do not use user date display setting, since tutorial default names must be universal regardless of user
|
||||||
|
-- formatDayForTutName = CI.mk . formatTime' "%y_%m_%d" -- we don't want to go monadic for this
|
||||||
|
formatDayForTutName = CI.mk . Text.map d2u . Text.drop 2 . tshow
|
||||||
|
where
|
||||||
|
d2u '-' = '_'
|
||||||
|
d2u c = c
|
||||||
|
|
||||||
|
|
||||||
data ButtonCourseRegisterMode = BtnCourseRegisterConfirm | BtnCourseRegisterAbort
|
data ButtonCourseRegisterMode = BtnCourseRegisterConfirm | BtnCourseRegisterAbort
|
||||||
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic)
|
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic)
|
||||||
@ -184,26 +192,37 @@ handleAddUserR tid ssh csh tdesc ttyp = do
|
|||||||
|
|
||||||
currentRoute <- fromMaybe (error "postCAddUserR called from 404-handler") <$> getCurrentRoute
|
currentRoute <- fromMaybe (error "postCAddUserR called from 404-handler") <$> getCurrentRoute
|
||||||
|
|
||||||
confirmedActs :: Set CourseRegisterActionData <- fmap Set.fromList . throwExceptT . mapMM encodedSecretBoxOpen . lookupPostParams $ toPathPiece PostCourseUserAddConfirmAction
|
(_ , registerConfirmResult) <- runButtonForm FIDCourseRegisterConfirm
|
||||||
-- $logDebugS "CAddUserR confirmedActs" . tshow $ Set.map Aeson.encode confirmedActs
|
-- $logDebugS "***AbortProblem***" $ tshow registerConfirmResult
|
||||||
unless (Set.null confirmedActs) $ do -- TODO: check that all acts are member of availableActs
|
prefillUsers <- case registerConfirmResult of
|
||||||
let
|
Nothing -> return mempty
|
||||||
users = Map.fromList . fmap (\act -> (crActIdent act, Just . view _1 $ crActUser act)) $ Set.toList confirmedActs
|
(Just BtnCourseRegisterAbort) -> do
|
||||||
tutActs = Set.filter (is _CourseRegisterActionAddTutorialMemberData) confirmedActs
|
addMessageI Warning MsgAborted
|
||||||
actTutorial = crActTutorial <$> Set.lookupMin tutActs -- tutorial ident must be the same for every added member!
|
-- prefill confirmed users for convenience. Note that Browser-Back may also return to the filled form, but history.back() does not in Chrome
|
||||||
registeredUsers <- registerUsers cid users
|
confirmedActs :: [CourseRegisterActionData] <- exceptT (const $ return mempty) return . mapMM encodedSecretBoxOpen . lookupPostParams $ toPathPiece PostCourseUserAddConfirmAction -- ignore any exception, since it is only used to prefill a form field for convenience
|
||||||
whenIsJust actTutorial $ \(tutName,tutType,tutDay) -> do
|
return $ Just $ Set.fromList $ fmap crActIdent confirmedActs
|
||||||
whenIsJust (tutName <|> fmap (tutorialDefaultName tutType) tutDay) $ \tName -> do
|
(Just BtnCourseRegisterConfirm) -> do
|
||||||
tutId <- upsertNewTutorial cid tName tutType tutDay
|
confirmedActs :: Set CourseRegisterActionData <- fmap Set.fromList . throwExceptT . mapMM encodedSecretBoxOpen . lookupPostParams $ toPathPiece PostCourseUserAddConfirmAction
|
||||||
registerTutorialMembers tutId registeredUsers
|
-- $logDebugS "CAddUserR confirmedActs" . tshow $ Set.map Aeson.encode confirmedActs
|
||||||
-- when (Set.size tutActs == Set.size confirmedActs) $ -- not sure how this condition might be false at this point
|
unless (Set.null confirmedActs) $ do -- TODO: check that all acts are member of availableActs
|
||||||
redirect $ CTutorialR tid ssh csh tName TUsersR
|
let
|
||||||
redirect $ CourseR tid ssh csh CUsersR
|
users = Map.fromList . fmap (\act -> (crActIdent act, Just . view _1 $ crActUser act)) $ Set.toList confirmedActs
|
||||||
|
tutActs = Set.filter (is _CourseRegisterActionAddTutorialMemberData) confirmedActs
|
||||||
|
actTutorial = crActTutorial <$> Set.lookupMin tutActs -- tutorial ident must be the same for every added member!
|
||||||
|
registeredUsers <- registerUsers cid users
|
||||||
|
whenIsJust actTutorial $ \(tutName,tutType,tutDay) -> do
|
||||||
|
whenIsJust (tutName <|> fmap (tutorialDefaultName tutType) tutDay) $ \tName -> do
|
||||||
|
tutId <- upsertNewTutorial cid tName tutType tutDay
|
||||||
|
registerTutorialMembers tutId registeredUsers
|
||||||
|
-- when (Set.size tutActs == Set.size confirmedActs) $ -- not sure how this condition might be false at this point
|
||||||
|
redirect $ CTutorialR tid ssh csh tName TUsersR
|
||||||
|
redirect $ CourseR tid ssh csh CUsersR
|
||||||
|
return mempty
|
||||||
|
|
||||||
((usersToAdd :: FormResult AddUserRequest, formWgt), formEncoding) <- runFormPost . renderWForm FormStandard $ do
|
((usersToAdd :: FormResult AddUserRequest, formWgt), formEncoding) <- runFormPost . identifyForm FIDCourseRegister . renderWForm FormStandard $ do
|
||||||
let tutTypesMsg = [(SomeMessage tt,tt) | tt <- tutTypes]
|
let tutTypesMsg = [(SomeMessage tt,tt) | tt <- tutTypes]
|
||||||
tutDefType = ttyp >>= (\ty -> if ty `elem` tutTypes then Just ty else Nothing)
|
tutDefType = ttyp >>= (\ty -> if ty `elem` tutTypes then Just ty else Nothing)
|
||||||
auReqUsers <- wreq (textField & cfAnySeparatedSet) (fslI MsgCourseParticipantsRegisterUsersField & setTooltip MsgCourseParticipantsRegisterUsersFieldTip) mempty
|
auReqUsers <- wreq (textField & cfAnySeparatedSet) (fslI MsgCourseParticipantsRegisterUsersField & setTooltip MsgCourseParticipantsRegisterUsersFieldTip) prefillUsers
|
||||||
auReqTutorial <- optionalActionW
|
auReqTutorial <- optionalActionW
|
||||||
( (,,)
|
( (,,)
|
||||||
<$> aopt (textField & cfStrip & cfCI & addDatalist tutNameSuggestions)
|
<$> aopt (textField & cfStrip & cfCI & addDatalist tutNameSuggestions)
|
||||||
|
|||||||
@ -68,7 +68,7 @@ courseRegisterForm (Entity cid Course{..}) = liftHandler $ do
|
|||||||
| otherwise
|
| otherwise
|
||||||
-> return $ FormSuccess ()
|
-> return $ FormSuccess ()
|
||||||
|
|
||||||
mayViewCourseAfterDeregistration <- liftHandler . runDB $ E.selectExists . E.from $ \course -> do
|
mayViewCourseAfterDeregistration <- liftHandler . runDBRead $ E.selectExists . E.from $ \course -> do
|
||||||
E.where_ $ course E.^. CourseId E.==. E.val cid
|
E.where_ $ course E.^. CourseId E.==. E.val cid
|
||||||
E.&&. ( isSchoolAdminLike muid ata (course E.^. CourseSchool)
|
E.&&. ( isSchoolAdminLike muid ata (course E.^. CourseSchool)
|
||||||
E.||. mayEditCourse muid ata course
|
E.||. mayEditCourse muid ata course
|
||||||
@ -92,7 +92,7 @@ courseMayReRegister :: Entity Course -> DB Bool
|
|||||||
courseMayReRegister (Entity cid Course{..}) = do
|
courseMayReRegister (Entity cid Course{..}) = do
|
||||||
registrations <- count [ CourseParticipantState ==. CourseParticipantActive, CourseParticipantCourse ==. cid ]
|
registrations <- count [ CourseParticipantState ==. CourseParticipantActive, CourseParticipantCourse ==. cid ]
|
||||||
let capacity = maybe True (>= registrations) courseCapacity
|
let capacity = maybe True (>= registrations) courseCapacity
|
||||||
|
|
||||||
wouldHaveWriteAccessTo [(AuthCapacity, capacity), (AuthCourseRegistered, False)] $ CourseR courseTerm courseSchool courseShorthand CRegisterR
|
wouldHaveWriteAccessTo [(AuthCapacity, capacity), (AuthCourseRegistered, False)] $ CourseR courseTerm courseSchool courseShorthand CRegisterR
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -9,12 +9,11 @@ module Handler.Course.User
|
|||||||
import Import
|
import Import
|
||||||
|
|
||||||
import Utils.Form
|
import Utils.Form
|
||||||
|
import Utils.Mail (pickValidUserEmail)
|
||||||
import Handler.Utils
|
import Handler.Utils
|
||||||
import Handler.Utils.SheetType
|
import Handler.Utils.SheetType
|
||||||
import Handler.Utils.Profile (pickValidEmail)
|
|
||||||
import Handler.Utils.StudyFeatures
|
import Handler.Utils.StudyFeatures
|
||||||
import Handler.Submission.List
|
import Handler.Submission.List
|
||||||
|
|
||||||
import Handler.Course.Register
|
import Handler.Course.Register
|
||||||
|
|
||||||
import Jobs.Queue
|
import Jobs.Queue
|
||||||
|
|||||||
@ -18,7 +18,8 @@ import Import
|
|||||||
import Utils.Form
|
import Utils.Form
|
||||||
import Handler.Utils
|
import Handler.Utils
|
||||||
import Handler.Utils.Course
|
import Handler.Utils.Course
|
||||||
import qualified Database.Esqueleto.Utils as E
|
import qualified Database.Esqueleto.Utils as E
|
||||||
|
import qualified Database.Esqueleto.PostgreSQL as E
|
||||||
import Database.Esqueleto.Utils.TH
|
import Database.Esqueleto.Utils.TH
|
||||||
|
|
||||||
import Handler.Course.Register (deregisterParticipant)
|
import Handler.Course.Register (deregisterParticipant)
|
||||||
@ -87,7 +88,7 @@ userTableQuery cid ((user `E.InnerJoin` participant) `E.LeftOuterJoin` note `E.L
|
|||||||
E.where_ $ participant E.^. CourseParticipantCourse E.==. E.val cid
|
E.where_ $ participant E.^. CourseParticipantCourse E.==. E.val cid
|
||||||
return (user, participant, note E.?. CourseUserNoteId, subGroup)
|
return (user, participant, note E.?. CourseUserNoteId, subGroup)
|
||||||
|
|
||||||
type UserTableQualifications = [(Entity Qualification, Entity QualificationUser)]
|
type UserTableQualifications = [(Entity Qualification, Entity QualificationUser, Maybe (Entity QualificationUserBlock))]
|
||||||
|
|
||||||
type UserTableData = DBRow ( Entity User
|
type UserTableData = DBRow ( Entity User
|
||||||
, Entity CourseParticipant
|
, Entity CourseParticipant
|
||||||
@ -128,10 +129,12 @@ _userSheets = _dbrOutput . _7
|
|||||||
|
|
||||||
-- _userQualifications :: Traversal' UserTableData [Entity Qualification]
|
-- _userQualifications :: Traversal' UserTableData [Entity Qualification]
|
||||||
-- _userQualifications = _dbrOutput . _8 . (traverse _1)
|
-- _userQualifications = _dbrOutput . _8 . (traverse _1)
|
||||||
-- last part: ([Entity Qualification] -> f [Entity Qualification]) -> UserTableQualfications -> f UserTableQualifications
|
-- last part: ([Entity Qualification] -> f [Entity Qualification]) -> UserTableQualifications -> f UserTableQualifications
|
||||||
|
|
||||||
_userQualifications :: Getter UserTableData [Entity Qualification]
|
_userQualifications :: Getter UserTableData [Entity Qualification]
|
||||||
_userQualifications = _dbrOutput . _8 . to (fmap fst)
|
_userQualifications = _dbrOutput . _8 . to (fmap fst3)
|
||||||
|
-- _userQualifications = _dbrOutput . _8 . each . _1 -- TODO: how to make this work
|
||||||
|
|
||||||
|
|
||||||
_userCourseQualifications :: Lens' UserTableData UserTableQualifications
|
_userCourseQualifications :: Lens' UserTableData UserTableQualifications
|
||||||
_userCourseQualifications = _dbrOutput . _8
|
_userCourseQualifications = _dbrOutput . _8
|
||||||
@ -182,18 +185,17 @@ colUserSheets shns = cap (Sortable Nothing caption) $ foldMap userSheetCol shns
|
|||||||
Just (preview _grading -> Just grading', Just points) -> i18nCell . bool MsgTableNotPassed MsgTablePassed $ Just True == gradingPassed grading' points
|
Just (preview _grading -> Just grading', Just points) -> i18nCell . bool MsgTableNotPassed MsgTablePassed $ Just True == gradingPassed grading' points
|
||||||
_other -> mempty
|
_other -> mempty
|
||||||
|
|
||||||
colUserQualifications :: forall m c. IsDBTable m c => Colonnade Sortable UserTableData (DBCell m c)
|
colUserQualifications :: forall m c. IsDBTable m c => Day -> Colonnade Sortable UserTableData (DBCell m c)
|
||||||
colUserQualifications = sortable (Just "qualifications") (i18nCell MsgTableQualifications) $
|
colUserQualifications cutoff = sortable (Just "qualifications") (i18nCell MsgTableQualifications) $
|
||||||
\(view _userCourseQualifications -> qualis) ->
|
let qualNamedValidCell (q,qu,qb) = textCell ((q ^. hasQualification . _qualificationShorthand . _CI) <> ": ") <> qualificationValidUntilCell cutoff qb qu
|
||||||
(cellAttrs <>~ [("class", "list--inline list--comma-separated list--iconless")]) . listCell qualis $ qualificationValidUntilCell
|
in \(view _userCourseQualifications -> qualis) ->
|
||||||
|
(cellAttrs <>~ [("class", "list--inline list--comma-separated list--iconless")]) . listCell qualis $ qualNamedValidCell
|
||||||
colUserQualificationBlocked :: forall m c. IsDBTable m c => Colonnade Sortable UserTableData (DBCell m c)
|
|
||||||
colUserQualificationBlocked = sortable (Just "qualification-block") (i18nCell MsgTableQualificationBlockedDue) $
|
|
||||||
\(view _userCourseQualifications -> qualis) ->
|
|
||||||
let blocks = qualificationUserBlockedDue . entityVal . snd <$> qualis
|
|
||||||
--blocks = qaulis <$> view (_2 . _entityVal . _qualificationUserBlockedDue)
|
|
||||||
in (cellAttrs <>~ [("class", "list--inline list--comma-separated list--iconless")]) . listCell blocks $ qualificationBlockedCell
|
|
||||||
|
|
||||||
|
colUserQualificationBlocked :: forall m c. IsDBTable m c => Bool -> Day -> Colonnade Sortable UserTableData (DBCell m c)
|
||||||
|
colUserQualificationBlocked isAdmin cutoff = sortable (Just "qualification-block") (i18nCell MsgQualificationValidIndicator & cellTooltip MsgTableQualificationBlockedTooltipSimple) $
|
||||||
|
let qualNamedReasonCell (q,qu,qb) = textCell ((q ^. hasQualification . _qualificationShorthand . _CI) <> ": ") <> qualificationValidReasonCell' Nothing isAdmin cutoff qb qu
|
||||||
|
in \(view _userCourseQualifications -> qualis) ->
|
||||||
|
(cellAttrs <>~ [("class", "list--inline list--comma-separated list--iconless")]) . listCell qualis $ qualNamedReasonCell
|
||||||
|
|
||||||
data UserTableCsv = UserTableCsv
|
data UserTableCsv = UserTableCsv
|
||||||
{ csvUserSurname :: UserSurname
|
{ csvUserSurname :: UserSurname
|
||||||
@ -417,13 +419,14 @@ makeCourseUserTable cid acts restrict colChoices psValidator csvColumns = do
|
|||||||
, submission
|
, submission
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
qualis <- E.select . E.from $ \(qualification `E.InnerJoin` qualificationUser) -> do
|
qualis <- E.select . E.from $ \(qualification `E.InnerJoin` qualificationUser `E.LeftOuterJoin` qualificationBlock) -> do
|
||||||
E.on $ qualification E.^. QualificationId E.==. qualificationUser E.^. QualificationUserQualification
|
E.on $ qualificationUser E.^. QualificationUserId E.=?. qualificationBlock E.?. QualificationUserBlockQualificationUser
|
||||||
|
E.&&. qualificationBlock `isLatestBlockBefore` E.now_
|
||||||
|
E.on $ qualificationUser E.^. QualificationUserQualification E.==. qualification E.^. QualificationId
|
||||||
E.where_ $ qualificationUser E.^. QualificationUserUser E.==. E.val (entityKey user)
|
E.where_ $ qualificationUser E.^. QualificationUserUser E.==. E.val (entityKey user)
|
||||||
E.&&. qualification E.^. QualificationId `E.in_` E.valList cqids
|
E.&&. qualification E.^. QualificationId `E.in_` E.valList cqids
|
||||||
|
E.orderBy [E.asc $ qualification E.^. QualificationShorthand] -- we should sort by CourseQualificationSortOrder instead, but since we have not seen a course with multiple qualifications yet, we take a shortcut here
|
||||||
E.orderBy [E.asc $ qualification E.^. QualificationShorthand] -- we should sort by CourseQualificationSortOrder instead, but since we have not seen a course with multiple qualifications yet, we take a shortcut here
|
return (qualification, qualificationUser, qualificationBlock)
|
||||||
return (qualification, qualificationUser)
|
|
||||||
let
|
let
|
||||||
regGroups = setOf (folded . _entityVal . _tutorialRegGroup . _Just) tutorials
|
regGroups = setOf (folded . _entityVal . _tutorialRegGroup . _Just) tutorials
|
||||||
tuts' = filter (\(Entity tutId _) -> any ((== tutId) . tutorialParticipantTutorial . entityVal) tuts'') tutorials
|
tuts' = filter (\(Entity tutId _) -> any ((== tutId) . tutorialParticipantTutorial . entityVal) tuts'') tutorials
|
||||||
@ -624,6 +627,8 @@ courseUserDeregisterForm _cid = wFormToAForm . pure . pure $ CourseUserDeregiste
|
|||||||
getCUsersR, postCUsersR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
getCUsersR, postCUsersR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
||||||
getCUsersR = postCUsersR
|
getCUsersR = postCUsersR
|
||||||
postCUsersR tid ssh csh = do
|
postCUsersR tid ssh csh = do
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
let nowaday = utctDay now
|
||||||
showSex <- getShowSex
|
showSex <- getShowSex
|
||||||
(course@(Entity cid Course{..}), numParticipants, (participantRes,participantTable)) <- runDB $ do
|
(course@(Entity cid Course{..}), numParticipants, (participantRes,participantTable)) <- runDB $ do
|
||||||
mayRegister <- hasWriteAccessTo $ CourseR tid ssh csh CAddUserR
|
mayRegister <- hasWriteAccessTo $ CourseR tid ssh csh CAddUserR
|
||||||
@ -655,8 +660,8 @@ postCUsersR tid ssh csh = do
|
|||||||
, pure . cap' $ colUserNameLink (CourseR tid ssh csh . CUserR)
|
, pure . cap' $ colUserNameLink (CourseR tid ssh csh . CUserR)
|
||||||
, guardOn showSex . cap' $ colUserSex'
|
, guardOn showSex . cap' $ colUserSex'
|
||||||
, pure . cap' $ colUserEmail
|
, pure . cap' $ colUserEmail
|
||||||
, pure . cap' $ colUserMatriclenr
|
, pure . cap' $ colUserMatriclenr False
|
||||||
, pure . cap' $ colUserQualifications
|
, pure . cap' $ colUserQualifications nowaday
|
||||||
, guardOn hasSubmissionGroups $ cap' colUserSubmissionGroup
|
, guardOn hasSubmissionGroups $ cap' colUserSubmissionGroup
|
||||||
, guardOn hasTutorials . cap' $ colUserTutorials tid ssh csh
|
, guardOn hasTutorials . cap' $ colUserTutorials tid ssh csh
|
||||||
, guardOn hasExams . cap' $ colUserExams tid ssh csh
|
, guardOn hasExams . cap' $ colUserExams tid ssh csh
|
||||||
@ -735,7 +740,6 @@ postCUsersR tid ssh csh = do
|
|||||||
(CourseUserRegisterExamData{..}, selectedUsers) -> do
|
(CourseUserRegisterExamData{..}, selectedUsers) -> do
|
||||||
Sum nrReg <- fmap mconcat . runDB . forM (Set.toList selectedUsers) $ \uid -> maybeT (return mempty) $ do
|
Sum nrReg <- fmap mconcat . runDB . forM (Set.toList selectedUsers) $ \uid -> maybeT (return mempty) $ do
|
||||||
guardM . lift $ exists [ CourseParticipantUser ==. uid, CourseParticipantCourse ==. cid, CourseParticipantState ==. CourseParticipantActive ]
|
guardM . lift $ exists [ CourseParticipantUser ==. uid, CourseParticipantCourse ==. cid, CourseParticipantState ==. CourseParticipantActive ]
|
||||||
now <- liftIO getCurrentTime
|
|
||||||
let (exam, mOccurrence) = registerExam
|
let (exam, mOccurrence) = registerExam
|
||||||
mExamReg <- lift $ insertUnique ExamRegistration
|
mExamReg <- lift $ insertUnique ExamRegistration
|
||||||
{ examRegistrationExam = exam
|
{ examRegistrationExam = exam
|
||||||
@ -760,7 +764,6 @@ postCUsersR tid ssh csh = do
|
|||||||
|
|
||||||
redirect $ CourseR tid ssh csh CUsersR
|
redirect $ CourseR tid ssh csh CUsersR
|
||||||
(CourseUserReRegisterData, selectedUsers) -> do
|
(CourseUserReRegisterData, selectedUsers) -> do
|
||||||
now <- liftIO getCurrentTime
|
|
||||||
Sum nrSet <- runDB . flip foldMapM selectedUsers $ \uid -> maybeT (return mempty) $ do
|
Sum nrSet <- runDB . flip foldMapM selectedUsers $ \uid -> maybeT (return mempty) $ do
|
||||||
didUpdate <- lift $ updateWhereCount
|
didUpdate <- lift $ updateWhereCount
|
||||||
[ CourseParticipantUser ==. uid
|
[ CourseParticipantUser ==. uid
|
||||||
|
|||||||
@ -484,7 +484,7 @@ postEUsersR tid ssh csh examn = do
|
|||||||
dbtColonnade = mconcat $ catMaybes
|
dbtColonnade = mconcat $ catMaybes
|
||||||
[ pure $ dbSelect (_2 . applying _2) _1 $ return . view (resultExamRegistration . _entityKey)
|
[ pure $ dbSelect (_2 . applying _2) _1 $ return . view (resultExamRegistration . _entityKey)
|
||||||
, pure $ colUserNameLink (CourseR tid ssh csh . CUserR)
|
, pure $ colUserNameLink (CourseR tid ssh csh . CUserR)
|
||||||
, pure colUserMatriclenr
|
, pure $ colUserMatriclenr False
|
||||||
, pure $ colStudyFeatures resultStudyFeatures
|
, pure $ colStudyFeatures resultStudyFeatures
|
||||||
, pure $ sortable (Just "occurrence") (i18nCell MsgTableExamOccurrence) $ maybe mempty (anchorCell' (\n -> CExamR tid ssh csh examn EShowR :#: [st|exam-occurrence__#{n}|]) id . examOccurrenceName . entityVal) . view _userTableOccurrence
|
, pure $ sortable (Just "occurrence") (i18nCell MsgTableExamOccurrence) $ maybe mempty (anchorCell' (\n -> CExamR tid ssh csh examn EShowR :#: [st|exam-occurrence__#{n}|]) id . examOccurrenceName . entityVal) . view _userTableOccurrence
|
||||||
, guardOn showPasses $ sortable Nothing (i18nCell MsgAchievedPasses) $ \(view $ resultUser . _entityKey -> uid) ->
|
, guardOn showPasses $ sortable Nothing (i18nCell MsgAchievedPasses) $ \(view $ resultUser . _entityKey -> uid) ->
|
||||||
|
|||||||
1451
src/Handler/Firm.hs
Normal file
1451
src/Handler/Firm.hs
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user